Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
---
changelog:
- date: 2025-06-30
version: v9.7.0
changes:
- type: feature
text: "Launch a backup heartbeat timer per registered PubNub instance in SharedWorker context to protect against browsers throttling of background (hidden) tabs."
- type: bug
text: "Fix issue because of which in new flow `heartbeat` request not cancelled properly when issued in burst."
- type: bug
text: "Fix issue because resource names, which consist only of integers, have been decoded as Unicode characters."
- type: bug
text: "Fix issue because the entity that has been created with `-pnpres` suffix has been removed from subscription loop during unsubscribe from entity with presence listening capability."
- type: improvement
text: "Use string names of classes for locations instead of dynamic access to constructor names because it affect how logs looks like after minification."
- date: 2025-06-30
version: v9.6.2
changes:
Expand Down Expand Up @@ -1263,7 +1276,7 @@ supported-platforms:
- 'Ubuntu 14.04 and up'
- 'Windows 7 and up'
version: 'Pubnub Javascript for Node'
version: '9.6.2'
version: '9.7.0'
sdks:
- full-name: PubNub Javascript SDK
short-name: Javascript
Expand All @@ -1279,7 +1292,7 @@ sdks:
- distribution-type: source
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.6.2.zip
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.7.0.zip
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down Expand Up @@ -1950,7 +1963,7 @@ sdks:
- distribution-type: library
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/releases/download/v9.6.2/pubnub.9.6.2.js
location: https://github.com/pubnub/javascript/releases/download/v9.7.0/pubnub.9.7.0.js
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## v9.7.0
June 30 2025

#### Added
- Launch a backup heartbeat timer per registered PubNub instance in SharedWorker context to protect against browsers throttling of background (hidden) tabs.

#### Fixed
- Fix issue because of which in new flow `heartbeat` request not cancelled properly when issued in burst.
- Fix issue because resource names, which consist only of integers, have been decoded as Unicode characters.
- Fix issue because the entity that has been created with `-pnpres` suffix has been removed from subscription loop during unsubscribe from entity with presence listening capability.

#### Modified
- Use string names of classes for locations instead of dynamic access to constructor names because it affect how logs looks like after minification.

## v9.6.2
June 30 2025

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Watch [Getting Started with PubNub JS SDK](https://app.dashcam.io/replay/64ee0d2
npm install pubnub
```
* or download one of our builds from our CDN:
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.6.2.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.6.2.min.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.7.0.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.7.0.min.js

2. Configure your keys:

Expand Down
6,614 changes: 3,407 additions & 3,207 deletions dist/web/pubnub.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/web/pubnub.min.js

Large diffs are not rendered by default.

34 changes: 31 additions & 3 deletions dist/web/pubnub.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,11 @@
if (hbRequestsBySubscriptionKey[heartbeatRequestKey] &&
hbRequestsBySubscriptionKey[heartbeatRequestKey].clientIdentifier === client.clientIdentifier)
delete hbRequestsBySubscriptionKey[heartbeatRequestKey].clientIdentifier;
if (heartbeat.timer) {
clearInterval(heartbeat.timer);
delete heartbeat.heartbeatEvent;
delete heartbeat.timer;
}
}
}
if (!request) {
Expand Down Expand Up @@ -1242,20 +1247,23 @@
* Use information from request to populate list of channels and other useful information.
*
* @param event - Send request.
* @returns `true` if channels / groups list has been changed. May return `undefined` because `client` is missing.
*/
const updateClientSubscribeStateIfRequired = (event) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
var _m, _o, _p, _q, _r, _s, _t, _u, _v;
const query = event.request.queryParameters;
const { clientIdentifier } = event;
const client = pubNubClients[clientIdentifier];
let changed = false;
// This should never happen.
if (!client)
return;
const channelGroupQuery = ((_a = query['channel-group']) !== null && _a !== void 0 ? _a : '');
const state = ((_b = query.state) !== null && _b !== void 0 ? _b : '');
let subscription = client.subscription;
if (!subscription) {
changed = true;
subscription = {
path: '',
channelGroupQuery: '',
Expand Down Expand Up @@ -1294,11 +1302,17 @@
}
if (subscription.path !== event.request.path) {
subscription.path = event.request.path;
subscription.channels = channelsFromRequest(event.request);
const _channelsFromRequest = channelsFromRequest(event.request);
if (!changed)
changed = includesStrings(subscription.channels, _channelsFromRequest);
subscription.channels = _channelsFromRequest;
}
if (subscription.channelGroupQuery !== channelGroupQuery) {
subscription.channelGroupQuery = channelGroupQuery;
subscription.channelGroups = channelGroupsFromRequest(event.request);
const _channelGroupsFromRequest = channelGroupsFromRequest(event.request);
if (!changed)
changed = includesStrings(subscription.channelGroups, _channelGroupsFromRequest);
subscription.channelGroups = _channelGroupsFromRequest;
}
let { authKey } = client;
const { userId } = client;
Expand Down Expand Up @@ -1326,7 +1340,8 @@
*/
const updateClientHeartbeatState = (event) => {
var _a, _b;
const client = pubNubClients[event.clientIdentifier];
const { clientIdentifier } = event;
const client = pubNubClients[clientIdentifier];
const { request } = event;
// This should never happen.
if (!client)
Expand All @@ -1335,6 +1350,16 @@
channels: [],
channelGroups: [],
}));
_clientHeartbeat.heartbeatEvent = Object.assign({}, event);
if (!_clientHeartbeat.timer) {
_clientHeartbeat.timer = setInterval(() => {
const client = pubNubClients[clientIdentifier];
// This should never happen.
if (!client || !client.heartbeat || !client.heartbeat.heartbeatEvent)
return;
handleHeartbeatRequestEvent(client.heartbeat.heartbeatEvent);
}, client.heartbeatInterval * 1000);
}
// Update presence heartbeat information about client.
_clientHeartbeat.channelGroups = channelGroupsFromRequest(request).filter((group) => !group.endsWith('-pnpres'));
_clientHeartbeat.channels = channelsFromRequest(request).filter((channel) => !channel.endsWith('-pnpres'));
Expand Down Expand Up @@ -1396,6 +1421,9 @@
if (serviceRequestId)
cancelRequest(serviceRequestId);
}
// Make sure to stop heartbeat timer.
if (invalidatedClient.heartbeat && invalidatedClient.heartbeat.timer)
clearInterval(invalidatedClient.heartbeat.timer);
if (serviceHeartbeatRequests[subscriptionKey]) {
const hbRequestsBySubscriptionKey = ((_a = serviceHeartbeatRequests[subscriptionKey]) !== null && _a !== void 0 ? _a : (serviceHeartbeatRequests[subscriptionKey] = {}));
const heartbeatRequestKey = `${invalidatedClient.userId}_${(_b = clientAggregateAuthKey(invalidatedClient)) !== null && _b !== void 0 ? _b : ''}`;
Expand Down
2 changes: 1 addition & 1 deletion dist/web/pubnub.worker.min.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions lib/core/components/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeConfiguration = void 0;
const console_logger_1 = require("../../loggers/console-logger");
const retry_policy_1 = require("./retry-policy");
const uuid_1 = __importDefault(require("./uuid"));
const logger_1 = require("../interfaces/logger");
const logger_manager_1 = require("./logger-manager");
const console_logger_1 = require("../../loggers/console-logger");
const uuid_1 = __importDefault(require("./uuid"));
// --------------------------------------------------------
// ----------------------- Defaults -----------------------
// --------------------------------------------------------
Expand Down Expand Up @@ -164,7 +164,7 @@ const makeConfiguration = (base, setupCryptoModule) => {
return base.PubNubFile;
},
get version() {
return '9.6.2';
return '9.7.0';
},
getVersion() {
return this.version;
Expand Down
12 changes: 6 additions & 6 deletions lib/core/components/cryptography/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class default_1 {
encrypt(data, customCipherKey, options) {
if (this.configuration.customEncrypt) {
if (this.logger)
this.logger.warn(this.constructor.name, "'customEncrypt' is deprecated. Consult docs for better alternative.");
this.logger.warn('Crypto', "'customEncrypt' is deprecated. Consult docs for better alternative.");
return this.configuration.customEncrypt(data);
}
return this.pnEncrypt(data, customCipherKey, options);
Expand All @@ -140,7 +140,7 @@ class default_1 {
decrypt(data, customCipherKey, options) {
if (this.configuration.customDecrypt) {
if (this.logger)
this.logger.warn(this.constructor.name, "'customDecrypt' is deprecated. Consult docs for better alternative.");
this.logger.warn('Crypto', "'customDecrypt' is deprecated. Consult docs for better alternative.");
return this.configuration.customDecrypt(data);
}
return this.pnDecrypt(data, customCipherKey, options);
Expand All @@ -159,7 +159,7 @@ class default_1 {
if (!decidedCipherKey)
return data;
if (this.logger) {
this.logger.debug(this.constructor.name, () => ({
this.logger.debug('Crypto', () => ({
messageType: 'object',
message: Object.assign({ data, cipherKey: decidedCipherKey }, (options !== null && options !== void 0 ? options : {})),
details: 'Encrypt with parameters:',
Expand Down Expand Up @@ -196,7 +196,7 @@ class default_1 {
if (!decidedCipherKey)
return data;
if (this.logger) {
this.logger.debug(this.constructor.name, () => ({
this.logger.debug('Crypto', () => ({
messageType: 'object',
message: Object.assign({ data, cipherKey: decidedCipherKey }, (options !== null && options !== void 0 ? options : {})),
details: 'Decrypt with parameters:',
Expand All @@ -218,7 +218,7 @@ class default_1 {
}
catch (e) {
if (this.logger)
this.logger.error(this.constructor.name, () => ({ messageType: 'error', message: e }));
this.logger.error('Crypto', () => ({ messageType: 'error', message: e }));
return null;
}
}
Expand All @@ -233,7 +233,7 @@ class default_1 {
}
catch (e) {
if (this.logger)
this.logger.error(this.constructor.name, () => ({ messageType: 'error', message: e }));
this.logger.error('Crypto', () => ({ messageType: 'error', message: e }));
return null;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/core/components/deduping_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DedupingManager {
*/
constructor(config) {
this.config = config;
config.logger().debug(this.constructor.name, () => ({
config.logger().debug('DedupingManager', () => ({
messageType: 'object',
message: { maximumCacheSize: config.maximumCacheSize },
details: 'Create with configuration:',
Expand Down
23 changes: 13 additions & 10 deletions lib/core/components/stringify_buffer_keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ exports.stringifyBufferKeys = stringifyBufferKeys;
* Re-map CBOR object keys from potentially C buffer strings to actual strings.
*
* @param obj CBOR which should be remapped to stringified keys.
* @param nestingLevel PAM token structure nesting level.
*
* @returns Dictionary with stringified keys.
*
* @internal
*/
function stringifyBufferKeys(obj) {
function stringifyBufferKeys(obj, nestingLevel = 0) {
const isObject = (value) => typeof value === 'object' && value !== null && value.constructor === Object;
const isString = (value) => typeof value === 'string' || value instanceof String;
const isNumber = (value) => typeof value === 'number' && isFinite(value);
Expand All @@ -26,16 +27,18 @@ function stringifyBufferKeys(obj) {
const keyIsString = isString(key);
let stringifiedKey = key;
const value = obj[key];
if (keyIsString && key.indexOf(',') >= 0) {
const bytes = key.split(',').map(Number);
stringifiedKey = bytes.reduce((string, byte) => {
return string + String.fromCharCode(byte);
}, '');
if (nestingLevel < 2) {
if (keyIsString && key.indexOf(',') >= 0) {
const bytes = key.split(',').map(Number);
stringifiedKey = bytes.reduce((string, byte) => {
return string + String.fromCharCode(byte);
}, '');
}
else if (isNumber(key) || (keyIsString && !isNaN(Number(key)))) {
stringifiedKey = String.fromCharCode(isNumber(key) ? key : parseInt(key, 10));
}
}
else if (isNumber(key) || (keyIsString && !isNaN(Number(key)))) {
stringifiedKey = String.fromCharCode(isNumber(key) ? key : parseInt(key, 10));
}
normalizedObject[stringifiedKey] = isObject(value) ? stringifyBufferKeys(value) : value;
normalizedObject[stringifiedKey] = isObject(value) ? stringifyBufferKeys(value, nestingLevel + 1) : value;
});
return normalizedObject;
}
6 changes: 3 additions & 3 deletions lib/core/components/subscription-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class SubscriptionManager {
this.subscribeCall = subscribeCall;
this.heartbeatCall = heartbeatCall;
this.leaveCall = leaveCall;
configuration.logger().trace(this.constructor.name, 'Create manager.');
configuration.logger().trace('SubscriptionManager', 'Create manager.');
this.reconnectionManager = new reconnection_manager_1.ReconnectionManager(time);
this.dedupingManager = new deduping_manager_1.DedupingManager(this.configuration);
this.heartbeatChannelGroups = {};
Expand Down Expand Up @@ -320,7 +320,7 @@ class SubscriptionManager {
timetoken: this.currentTimetoken,
region: this.region ? this.region : undefined,
};
this.configuration.logger().debug(this.constructor.name, () => {
this.configuration.logger().debug('SubscriptionManager', () => {
const hashedEvents = messages.map((event) => {
const pn_mfp = event.type === subscribe_1.PubNubEventType.Message || event.type === subscribe_1.PubNubEventType.Signal
? (0, utils_1.messageFingerprint)(event.data.message)
Expand All @@ -332,7 +332,7 @@ class SubscriptionManager {
messages.forEach((message) => {
if (dedupeOnSubscribe && 'message' in message.data && 'timetoken' in message.data) {
if (this.dedupingManager.isDuplicate(message.data)) {
this.configuration.logger().warn(this.constructor.name, () => ({
this.configuration.logger().warn('SubscriptionManager', () => ({
messageType: 'object',
message: message.data,
details: 'Duplicate message detected (skipped):',
Expand Down
7 changes: 7 additions & 0 deletions lib/core/endpoints/objects/channel/set.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ class SetChannelMetadataRequest extends request_1.AbstractRequest {
if (!this.parameters.data)
return 'Data cannot be empty';
}
get headers() {
var _a;
let headers = (_a = super.headers) !== null && _a !== void 0 ? _a : {};
if (this.parameters.ifMatchesEtag)
headers = Object.assign(Object.assign({}, headers), { 'If-Match': this.parameters.ifMatchesEtag });
return Object.assign(Object.assign({}, headers), { 'Content-Type': 'application/json' });
}
get path() {
const { keySet: { subscribeKey }, channel, } = this.parameters;
return `/v2/objects/${subscribeKey}/channels/${(0, utils_1.encodeString)(channel)}`;
Expand Down
Loading
Loading