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
13 changes: 10 additions & 3 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
---
changelog:
- date: 2025-07-15
version: v9.8.1
changes:
- type: bug
text: "Fix incorrect subscription reference timetoken (used by listeners to filter old messages) caused by the server returning timetoken older than the previous one because of MX."
- type: bug
text: "Fix the issue because of which all subscriptions of the subscription set have been requested to handle the received event."
- date: 2025-07-11
version: v9.8.0
changes:
Expand Down Expand Up @@ -1291,7 +1298,7 @@ supported-platforms:
- 'Ubuntu 14.04 and up'
- 'Windows 7 and up'
version: 'Pubnub Javascript for Node'
version: '9.8.0'
version: '9.8.1'
sdks:
- full-name: PubNub Javascript SDK
short-name: Javascript
Expand All @@ -1307,7 +1314,7 @@ sdks:
- distribution-type: source
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.8.0.zip
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.8.1.zip
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down Expand Up @@ -1978,7 +1985,7 @@ sdks:
- distribution-type: library
distribution-repository: GitHub release
package-name: pubnub.js
location: https://github.com/pubnub/javascript/releases/download/v9.8.0/pubnub.9.8.0.js
location: https://github.com/pubnub/javascript/releases/download/v9.8.1/pubnub.9.8.1.js
requires:
- name: 'agentkeepalive'
min-version: '3.5.2'
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v9.8.1
July 15 2025

#### Fixed
- Fix incorrect subscription reference timetoken (used by listeners to filter old messages) caused by the server returning timetoken older than the previous one because of MX.
- Fix the issue because of which all subscriptions of the subscription set have been requested to handle the received event.

## v9.8.0
July 11 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.8.0.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.8.0.min.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.8.1.js
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.8.1.min.js

2. Configure your keys:

Expand Down
36 changes: 23 additions & 13 deletions dist/web/pubnub.js
Original file line number Diff line number Diff line change
Expand Up @@ -4394,6 +4394,10 @@
*/
const adjustedTimetokenBy = (timetoken, value, increment) => {
// Normalize value to the PubNub's high-precision time format.
if (value.startsWith('-')) {
value = value.replace('-', '');
increment = false;
}
value = value.padStart(17, '0');
const secA = timetoken.slice(0, 10);
const tickA = timetoken.slice(10, 17);
Expand All @@ -4415,6 +4419,10 @@
else if (seconds < 0)
ticks *= -1;
}
else if (seconds < 0 && ticks > 0) {
seconds += 1;
ticks = 10000000 - ticks;
}
return seconds !== 0 ? `${seconds}${`${ticks}`.padStart(7, '0')}` : `${ticks}`;
};
/**
Expand Down Expand Up @@ -5325,7 +5333,7 @@
return base.PubNubFile;
},
get version() {
return '9.8.0';
return '9.8.1';
},
getVersion() {
return this.version;
Expand Down Expand Up @@ -10795,25 +10803,24 @@
* @internal
*/
handleEvent(cursor, event) {
var _a;
if (!this.state.isSubscribed)
var _a, _b;
if (!this.state.isSubscribed ||
!this.state.subscriptionInput.contains((_a = event.data.subscription) !== null && _a !== void 0 ? _a : event.data.channel))
return;
if (this.parentSetsCount > 0) {
// Creating from whole payload (not only for published messages).
const fingerprint = messageFingerprint(event.data);
if (this.handledUpdates.includes(fingerprint)) {
this.state.client.logger.trace(this.subscriptionType, `Message (${fingerprint}) already handled by ${this.id}. Ignoring.`);
this.state.client.logger.trace(this.subscriptionType, `Event (${fingerprint}) already handled by ${this.id}. Ignoring.`);
return;
}
else
console.log(`${this.id} handled (${fingerprint})`);
// Update a list of tracked messages and shrink it if too big.
this.handledUpdates.push(fingerprint);
if (this.handledUpdates.length > 10)
this.handledUpdates.shift();
}
// Check whether an event is not designated for this subscription set.
if (!this.state.subscriptionInput.contains((_a = event.data.subscription) !== null && _a !== void 0 ? _a : event.data.channel))
if (!this.state.subscriptionInput.contains((_b = event.data.subscription) !== null && _b !== void 0 ? _b : event.data.channel))
return;
super.handleEvent(cursor, event);
}
Expand Down Expand Up @@ -16233,7 +16240,7 @@
*/
disconnect(isOffline = false) {
{
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'}`);
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'})`);
if (this.subscriptionManager)
this.subscriptionManager.disconnect();
else if (this.eventEngine)
Expand Down Expand Up @@ -17694,7 +17701,8 @@
this._globalSubscriptionSet.handleEvent(cursor, event);
(_a = this.eventDispatcher) === null || _a === void 0 ? void 0 : _a.handleEvent(event);
Object.values(this.eventHandleCapable).forEach((eventHandleCapable) => {
eventHandleCapable.handleEvent(cursor, event);
if (eventHandleCapable !== this._globalSubscriptionSet)
eventHandleCapable.handleEvent(cursor, event);
});
}
}
Expand Down Expand Up @@ -18103,10 +18111,12 @@
authenticationChangeHandler = (auth) => middleware.onTokenChange(auth);
userIdChangeHandler = (userId) => middleware.onUserIdChange(userId);
transport = middleware;
window.onpagehide = (event) => {
if (!event.persisted)
middleware.terminate();
};
if (configurationCopy.subscriptionWorkerUnsubscribeOfflineClients) {
window.addEventListener('pagehide', (event) => {
Copy link
Contributor

@mohitpubnub mohitpubnub Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay so this will not overwrite others

and event.persisted will be false for both - soft and hard refresh 👍🏻

if (!event.persisted)
middleware.terminate();
}, { once: true });
}
}
catch (e) {
clientConfiguration.logger().error('PubNub', () => ({
Expand Down
4 changes: 2 additions & 2 deletions dist/web/pubnub.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/core/components/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const makeConfiguration = (base, setupCryptoModule) => {
return base.PubNubFile;
},
get version() {
return '9.8.0';
return '9.8.1';
},
getVersion() {
return this.version;
Expand Down
5 changes: 3 additions & 2 deletions lib/core/pubnub-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -1319,7 +1319,7 @@ class PubNubCore {
*/
disconnect(isOffline = false) {
if (process.env.SUBSCRIBE_MODULE !== 'disabled') {
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'}`);
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'})`);
if (this.subscriptionManager)
this.subscriptionManager.disconnect();
else if (this.eventEngine)
Expand Down Expand Up @@ -2960,7 +2960,8 @@ class PubNubCore {
this._globalSubscriptionSet.handleEvent(cursor, event);
(_a = this.eventDispatcher) === null || _a === void 0 ? void 0 : _a.handleEvent(event);
Object.values(this.eventHandleCapable).forEach((eventHandleCapable) => {
eventHandleCapable.handleEvent(cursor, event);
if (eventHandleCapable !== this._globalSubscriptionSet)
eventHandleCapable.handleEvent(cursor, event);
});
}
}
Expand Down
8 changes: 8 additions & 0 deletions lib/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ exports.referenceSubscribeTimetoken = referenceSubscribeTimetoken;
*/
const adjustedTimetokenBy = (timetoken, value, increment) => {
// Normalize value to the PubNub's high-precision time format.
if (value.startsWith('-')) {
value = value.replace('-', '');
increment = false;
}
value = value.padStart(17, '0');
const secA = timetoken.slice(0, 10);
const tickA = timetoken.slice(10, 17);
Expand All @@ -152,6 +156,10 @@ const adjustedTimetokenBy = (timetoken, value, increment) => {
else if (seconds < 0)
ticks *= -1;
}
else if (seconds < 0 && ticks > 0) {
seconds += 1;
ticks = 10000000 - ticks;
}
return seconds !== 0 ? `${seconds}${`${ticks}`.padStart(7, '0')}` : `${ticks}`;
};
exports.adjustedTimetokenBy = adjustedTimetokenBy;
Expand Down
11 changes: 5 additions & 6 deletions lib/entities/subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,25 +112,24 @@ class Subscription extends subscription_base_1.SubscriptionBase {
* @internal
*/
handleEvent(cursor, event) {
var _a;
if (!this.state.isSubscribed)
var _a, _b;
if (!this.state.isSubscribed ||
!this.state.subscriptionInput.contains((_a = event.data.subscription) !== null && _a !== void 0 ? _a : event.data.channel))
return;
if (this.parentSetsCount > 0) {
// Creating from whole payload (not only for published messages).
const fingerprint = (0, utils_1.messageFingerprint)(event.data);
if (this.handledUpdates.includes(fingerprint)) {
this.state.client.logger.trace(this.subscriptionType, `Message (${fingerprint}) already handled by ${this.id}. Ignoring.`);
this.state.client.logger.trace(this.subscriptionType, `Event (${fingerprint}) already handled by ${this.id}. Ignoring.`);
return;
}
else
console.log(`${this.id} handled (${fingerprint})`);
// Update a list of tracked messages and shrink it if too big.
this.handledUpdates.push(fingerprint);
if (this.handledUpdates.length > 10)
this.handledUpdates.shift();
}
// Check whether an event is not designated for this subscription set.
if (!this.state.subscriptionInput.contains((_a = event.data.subscription) !== null && _a !== void 0 ? _a : event.data.channel))
if (!this.state.subscriptionInput.contains((_b = event.data.subscription) !== null && _b !== void 0 ? _b : event.data.channel))
return;
super.handleEvent(cursor, event);
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pubnub",
"version": "9.8.0",
"version": "9.8.1",
"author": "PubNub <support@pubnub.com>",
"description": "Publish & Subscribe Real-time Messaging with PubNub",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/core/components/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export const makeConfiguration = (
return base.PubNubFile;
},
get version(): string {
return '9.8.0';
return '9.8.1';
},
getVersion(): string {
return this.version;
Expand Down
1 change: 0 additions & 1 deletion src/core/components/subscription-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,6 @@ export class SubscriptionManager {

return;
}

this.referenceTimetoken = referenceSubscribeTimetoken(result!.cursor.timetoken, this.storedTimetoken);
if (this.storedTimetoken) {
this.currentTimetoken = this.storedTimetoken;
Expand Down
4 changes: 2 additions & 2 deletions src/core/pubnub-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1786,7 +1786,7 @@ export class PubNubCore<
*/
public disconnect(isOffline: boolean = false): void {
if (process.env.SUBSCRIBE_MODULE !== 'disabled') {
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'}`);
this.logger.debug('PubNub', `Disconnect (while offline? ${!!isOffline ? 'yes' : 'no'})`);

if (this.subscriptionManager) this.subscriptionManager.disconnect();
else if (this.eventEngine) this.eventEngine.disconnect(isOffline);
Expand Down Expand Up @@ -4612,7 +4612,7 @@ export class PubNubCore<
this.eventDispatcher?.handleEvent(event);

Object.values(this.eventHandleCapable).forEach((eventHandleCapable) => {
eventHandleCapable.handleEvent(cursor, event);
if (eventHandleCapable !== this._globalSubscriptionSet) eventHandleCapable.handleEvent(cursor, event);
});
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export const referenceSubscribeTimetoken = (
*/
export const adjustedTimetokenBy = (timetoken: string, value: string, increment: boolean): string => {
// Normalize value to the PubNub's high-precision time format.
if (value.startsWith('-')) {
value = value.replace('-', '');
increment = false;
}
value = value.padStart(17, '0');

const secA = timetoken.slice(0, 10);
Expand All @@ -164,6 +168,9 @@ export const adjustedTimetokenBy = (timetoken: string, value: string, increment:
seconds -= 1;
ticks += 10_000_000;
} else if (seconds < 0) ticks *= -1;
} else if (seconds < 0 && ticks > 0) {
seconds += 1;
ticks = 10_000_000 - ticks;
}

return seconds !== 0 ? `${seconds}${`${ticks}`.padStart(7, '0')}` : `${ticks}`;
Expand Down
10 changes: 7 additions & 3 deletions src/entities/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,22 @@ export class Subscription extends SubscriptionBase {
* @internal
*/
override handleEvent(cursor: SubscriptionCursor, event: SubscriptionResponse['messages'][0]): void {
if (!this.state.isSubscribed) return;
if (
!this.state.isSubscribed ||
!this.state.subscriptionInput.contains(event.data.subscription ?? event.data.channel)
)
return;

if (this.parentSetsCount > 0) {
// Creating from whole payload (not only for published messages).
const fingerprint = messageFingerprint(event.data);
if (this.handledUpdates.includes(fingerprint)) {
this.state.client.logger.trace(
this.subscriptionType,
`Message (${fingerprint}) already handled by ${this.id}. Ignoring.`,
`Event (${fingerprint}) already handled by ${this.id}. Ignoring.`,
);
return;
} else console.log(`${this.id} handled (${fingerprint})`);
}

// Update a list of tracked messages and shrink it if too big.
this.handledUpdates.push(fingerprint);
Expand Down
12 changes: 9 additions & 3 deletions src/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,15 @@ export default class PubNub extends PubNubCore<ArrayBuffer | string, PubNubFileP
userIdChangeHandler = (userId: string) => middleware.onUserIdChange(userId);
transport = middleware;

window.onpagehide = (event: PageTransitionEvent) => {
if (!event.persisted) middleware.terminate();
};
if (configurationCopy.subscriptionWorkerUnsubscribeOfflineClients) {
window.addEventListener(
'pagehide',
(event) => {
if (!event.persisted) middleware.terminate();
},
{ once: true },
);
}
} catch (e) {
clientConfiguration.logger().error('PubNub', () => ({
messageType: 'error',
Expand Down
Loading
Loading