Skip to content

Commit 84caf59

Browse files
authored
Send new presence state to the SharedWorker and fix newer access token override (#473)
fix(shared-worker): fix newer access token override Fix issue because of which requests aggregated from other clients were able to override previously explicitly set newer access token. feat(presence-state): send new presence `state` to the `SharedWorker` Send new presence `state` to the `SharedWorker` as soon as it has been set with `setState` to avoid race conditions between regular heartbeats and `backup` heartbeats. refactor(shared-worker): exclude presence `state` from long-poll subscribe request Remove presence `state` from long-poll subscribe requests as part of the transition to explicit heartbeat.
1 parent 30dbcd3 commit 84caf59

32 files changed

+690
-118
lines changed

.pubnub.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
---
22
changelog:
3+
- date: 2025-09-09
4+
version: v9.10.0
5+
changes:
6+
- type: feature
7+
text: "Send new presence `state` to the `SharedWorker` as soon as it has been set with `setState` to avoid race conditions between regular heartbeats and `backup` heartbeats."
8+
- type: bug
9+
text: "Fix issue because of which requests aggregated from other clients were able to override previously explicitly set newer access token."
10+
- type: improvement
11+
text: "Remove presence `state` from long-poll subscribe requests as part of the transition to explicit heartbeat."
312
- date: 2025-08-25
413
version: v9.9.0
514
changes:
@@ -1326,7 +1335,7 @@ supported-platforms:
13261335
- 'Ubuntu 14.04 and up'
13271336
- 'Windows 7 and up'
13281337
version: 'Pubnub Javascript for Node'
1329-
version: '9.9.0'
1338+
version: '9.10.0'
13301339
sdks:
13311340
- full-name: PubNub Javascript SDK
13321341
short-name: Javascript
@@ -1342,7 +1351,7 @@ sdks:
13421351
- distribution-type: source
13431352
distribution-repository: GitHub release
13441353
package-name: pubnub.js
1345-
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.9.0.zip
1354+
location: https://github.com/pubnub/javascript/archive/refs/tags/v9.10.0.zip
13461355
requires:
13471356
- name: 'agentkeepalive'
13481357
min-version: '3.5.2'
@@ -2013,7 +2022,7 @@ sdks:
20132022
- distribution-type: library
20142023
distribution-repository: GitHub release
20152024
package-name: pubnub.js
2016-
location: https://github.com/pubnub/javascript/releases/download/v9.9.0/pubnub.9.9.0.js
2025+
location: https://github.com/pubnub/javascript/releases/download/v9.10.0/pubnub.9.10.0.js
20172026
requires:
20182027
- name: 'agentkeepalive'
20192028
min-version: '3.5.2'

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## v9.10.0
2+
September 09 2025
3+
4+
#### Added
5+
- Send new presence `state` to the `SharedWorker` as soon as it has been set with `setState` to avoid race conditions between regular heartbeats and `backup` heartbeats.
6+
7+
#### Fixed
8+
- Fix issue because of which requests aggregated from other clients were able to override previously explicitly set newer access token.
9+
10+
#### Modified
11+
- Remove presence `state` from long-poll subscribe requests as part of the transition to explicit heartbeat.
12+
113
## v9.9.0
214
August 25 2025
315

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ Watch [Getting Started with PubNub JS SDK](https://app.dashcam.io/replay/64ee0d2
2727
npm install pubnub
2828
```
2929
* or download one of our builds from our CDN:
30-
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.9.0.js
31-
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.9.0.min.js
30+
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.10.0.js
31+
* https://cdn.pubnub.com/sdk/javascript/pubnub.9.10.0.min.js
3232
3333
2. Configure your keys:
3434

dist/web/pubnub.js

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,21 @@
35953595
workerLogLevel: this.configuration.workerLogLevel,
35963596
});
35973597
}
3598+
/**
3599+
* Update presence state associated with `userId`.
3600+
*
3601+
* @param state - Key-value pair of payloads (states) that should be associated with channels / groups specified as
3602+
* keys.
3603+
*/
3604+
onPresenceStateChange(state) {
3605+
this.scheduleEventPost({
3606+
type: 'client-presence-state-update',
3607+
clientIdentifier: this.configuration.clientIdentifier,
3608+
subscriptionKey: this.configuration.subscriptionKey,
3609+
workerLogLevel: this.configuration.workerLogLevel,
3610+
state,
3611+
});
3612+
}
35983613
/**
35993614
* Update client's heartbeat interval change.
36003615
*
@@ -3897,7 +3912,7 @@
38973912
if (!token || !stringifiedToken)
38983913
return undefined;
38993914
return (this.accessTokensMap = {
3900-
[accessToken]: { token: stringifiedToken, expiration: token.timestamp * token.ttl * 60 },
3915+
[accessToken]: { token: stringifiedToken, expiration: token.timestamp + token.ttl * 60 },
39013916
})[accessToken];
39023917
});
39033918
});
@@ -5421,7 +5436,7 @@
54215436
return base.PubNubFile;
54225437
},
54235438
get version() {
5424-
return '9.9.0';
5439+
return '9.10.0';
54255440
},
54265441
getVersion() {
54275442
return this.version;
@@ -6519,52 +6534,61 @@
65196534
let { e: eventType } = envelope;
65206535
// Resolve missing event type.
65216536
eventType !== null && eventType !== void 0 ? eventType : (eventType = envelope.c.endsWith('-pnpres') ? PubNubEventType.Presence : PubNubEventType.Message);
6537+
const pn_mfp = messageFingerprint(envelope.d);
65226538
// Check whether payload is string (potentially encrypted data).
65236539
if (eventType != PubNubEventType.Signal && typeof envelope.d === 'string') {
65246540
if (eventType == PubNubEventType.Message) {
65256541
return {
65266542
type: PubNubEventType.Message,
65276543
data: this.messageFromEnvelope(envelope),
6544+
pn_mfp,
65286545
};
65296546
}
65306547
return {
65316548
type: PubNubEventType.Files,
65326549
data: this.fileFromEnvelope(envelope),
6550+
pn_mfp,
65336551
};
65346552
}
65356553
else if (eventType == PubNubEventType.Message) {
65366554
return {
65376555
type: PubNubEventType.Message,
65386556
data: this.messageFromEnvelope(envelope),
6557+
pn_mfp,
65396558
};
65406559
}
65416560
else if (eventType === PubNubEventType.Presence) {
65426561
return {
65436562
type: PubNubEventType.Presence,
65446563
data: this.presenceEventFromEnvelope(envelope),
6564+
pn_mfp,
65456565
};
65466566
}
65476567
else if (eventType == PubNubEventType.Signal) {
65486568
return {
65496569
type: PubNubEventType.Signal,
65506570
data: this.signalFromEnvelope(envelope),
6571+
pn_mfp,
65516572
};
65526573
}
65536574
else if (eventType === PubNubEventType.AppContext) {
65546575
return {
65556576
type: PubNubEventType.AppContext,
65566577
data: this.appContextFromEnvelope(envelope),
6578+
pn_mfp,
65576579
};
65586580
}
65596581
else if (eventType === PubNubEventType.MessageAction) {
65606582
return {
65616583
type: PubNubEventType.MessageAction,
65626584
data: this.messageActionFromEnvelope(envelope),
6585+
pn_mfp,
65636586
};
65646587
}
65656588
return {
65666589
type: PubNubEventType.Files,
65676590
data: this.fileFromEnvelope(envelope),
6591+
pn_mfp,
65686592
};
65696593
});
65706594
return {
@@ -7413,12 +7437,10 @@
74137437
region: this.region ? this.region : undefined,
74147438
};
74157439
this.configuration.logger().debug('SubscriptionManager', () => {
7416-
const hashedEvents = messages.map((event) => {
7417-
const pn_mfp = event.type === PubNubEventType.Message || event.type === PubNubEventType.Signal
7418-
? messageFingerprint(event.data.message)
7419-
: undefined;
7420-
return pn_mfp ? { type: event.type, data: Object.assign(Object.assign({}, event.data), { pn_mfp }) } : event;
7421-
});
7440+
const hashedEvents = messages.map((event) => ({
7441+
type: event.type,
7442+
data: Object.assign(Object.assign({}, event.data), { pn_mfp: event.pn_mfp }),
7443+
}));
74227444
return { messageType: 'object', message: hashedEvents, details: 'Received events:' };
74237445
});
74247446
messages.forEach((message) => {
@@ -13009,7 +13031,7 @@
1300913031
const logResponse = (response) => {
1301013032
if (!response)
1301113033
return;
13012-
this.logger.info('PubNub', `List channel group channels success. Received ${response.channels.length} channels.`);
13034+
this.logger.debug('PubNub', `List channel group channels success. Received ${response.channels.length} channels.`);
1301313035
};
1301413036
if (callback)
1301513037
return this.sendRequest(request, (status, response) => {
@@ -13038,7 +13060,7 @@
1303813060
const logResponse = (response) => {
1303913061
if (!response)
1304013062
return;
13041-
this.logger.info('PubNub', `List all channel groups success. Received ${response.groups.length} groups.`);
13063+
this.logger.debug('PubNub', `List all channel groups success. Received ${response.groups.length} groups.`);
1304213064
};
1304313065
if (callback)
1304413066
return this.sendRequest(request, (status, response) => {
@@ -13069,7 +13091,7 @@
1306913091
}));
1307013092
const request = new AddChannelGroupChannelsRequest(Object.assign(Object.assign({}, parameters), { keySet: this.keySet }));
1307113093
const logResponse = () => {
13072-
this.logger.info('PubNub', `Add channels to the channel group success.`);
13094+
this.logger.debug('PubNub', `Add channels to the channel group success.`);
1307313095
};
1307413096
if (callback)
1307513097
return this.sendRequest(request, (status) => {
@@ -13101,7 +13123,7 @@
1310113123
}));
1310213124
const request = new RemoveChannelGroupChannelsRequest(Object.assign(Object.assign({}, parameters), { keySet: this.keySet }));
1310313125
const logResponse = () => {
13104-
this.logger.info('PubNub', `Remove channels from the channel group success.`);
13126+
this.logger.debug('PubNub', `Remove channels from the channel group success.`);
1310513127
};
1310613128
if (callback)
1310713129
return this.sendRequest(request, (status) => {
@@ -13132,7 +13154,7 @@
1313213154
}));
1313313155
const request = new DeleteChannelGroupRequest(Object.assign(Object.assign({}, parameters), { keySet: this.keySet }));
1313413156
const logResponse = () => {
13135-
this.logger.info('PubNub', `Remove a channel group success. Removed '${parameters.channelGroup}' channel group.'`);
13157+
this.logger.debug('PubNub', `Remove a channel group success. Removed '${parameters.channelGroup}' channel group.'`);
1313613158
};
1313713159
if (callback)
1313813160
return this.sendRequest(request, (status) => {
@@ -14516,7 +14538,7 @@
1451614538
const logResponse = (response) => {
1451714539
if (!response)
1451814540
return;
14519-
this.logger.debug('PubNub', `Set UUID metadata object success. Updated '${parameters.uuid}' UUID metadata object.'`);
14541+
this.logger.debug('PubNub', `Set UUID metadata object success. Updated '${parameters.uuid}' UUID metadata object.`);
1452014542
};
1452114543
if (callback)
1452214544
return this.sendRequest(request, (status, response) => {
@@ -14673,7 +14695,7 @@
1467314695
const logResponse = (response) => {
1467414696
if (!response)
1467514697
return;
14676-
this.logger.debug('PubNub', `Get Channel metadata object success. Received '${parameters.channel}' Channel metadata object.'`);
14698+
this.logger.debug('PubNub', `Get Channel metadata object success. Received '${parameters.channel}' Channel metadata object.`);
1467714699
};
1467814700
if (callback)
1467914701
return this.sendRequest(request, (status, response) => {
@@ -16858,6 +16880,8 @@
1685816880
if ('channelGroups' in parameters) {
1685916881
(_b = parameters.channelGroups) === null || _b === void 0 ? void 0 : _b.forEach((group) => (presenceState[group] = parameters.state));
1686016882
}
16883+
if (this.onPresenceStateChange)
16884+
this.onPresenceStateChange(this.presenceState);
1686116885
}
1686216886
// Check whether the state should be set with heartbeat or not.
1686316887
if ('withHeartbeat' in parameters && parameters.withHeartbeat) {
@@ -16872,8 +16896,11 @@
1687216896
this.logger.debug('PubNub', `Set presence state success.${request instanceof HeartbeatRequest ? ' Presence state has been set using heartbeat endpoint.' : ''}`);
1687316897
};
1687416898
// Update state used by subscription manager.
16875-
if (this.subscriptionManager)
16899+
if (this.subscriptionManager) {
1687616900
this.subscriptionManager.setState(parameters);
16901+
if (this.onPresenceStateChange)
16902+
this.onPresenceStateChange(this.subscriptionManager.presenceState);
16903+
}
1687716904
if (callback)
1687816905
return this.sendRequest(request, (status, response) => {
1687916906
logResponse(response);
@@ -16943,8 +16970,7 @@
1694316970
return callback(responseStatus, {});
1694416971
return Promise.resolve(responseStatus);
1694516972
}
16946-
const request = new HeartbeatRequest(Object.assign(Object.assign({}, parameters), { channels,
16947-
channelGroups, keySet: this._configuration.keySet }));
16973+
const request = new HeartbeatRequest(Object.assign(Object.assign({}, parameters), { channels: [...new Set(channels)], channelGroups: [...new Set(channelGroups)], keySet: this._configuration.keySet }));
1694816974
const logResponse = (response) => {
1694916975
if (!response)
1695016976
return;
@@ -18246,6 +18272,7 @@
1824618272
}
1824718273
// Settings change handlers
1824818274
let heartbeatIntervalChangeHandler = () => { };
18275+
let presenceStateChangeHandler = () => { };
1824918276
let authenticationChangeHandler = () => { };
1825018277
let userIdChangeHandler = () => { };
1825118278
let cryptography;
@@ -18272,6 +18299,7 @@
1827218299
transport,
1827318300
logger: clientConfiguration.logger(),
1827418301
});
18302+
presenceStateChangeHandler = (state) => middleware.onPresenceStateChange(state);
1827518303
heartbeatIntervalChangeHandler = (interval) => middleware.onHeartbeatIntervalChange(interval);
1827618304
authenticationChangeHandler = (auth) => middleware.onTokenChange(auth);
1827718305
userIdChangeHandler = (userId) => middleware.onUserIdChange(userId);
@@ -18310,6 +18338,7 @@
1831018338
});
1831118339
this.onHeartbeatIntervalChange = heartbeatIntervalChangeHandler;
1831218340
this.onAuthenticationChange = authenticationChangeHandler;
18341+
this.onPresenceStateChange = presenceStateChangeHandler;
1831318342
this.onUserIdChange = userIdChangeHandler;
1831418343
{
1831518344
if (transport instanceof SubscriptionWorkerMiddleware) {

dist/web/pubnub.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)