@cendarsoss/pusher-js 8.4.16 → 8.4.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cendarsoss/pusher-js",
3
- "version": "8.4.16",
3
+ "version": "8.4.17",
4
4
  "description": "Pusher Channels JavaScript library for browsers, React Native, NodeJS and web workers",
5
5
  "types": "index.d.ts",
6
6
  "main": "dist/node/pusher.js",
@@ -40,15 +40,16 @@ const Protocol = {
40
40
  }
41
41
  // Copy sequence and conflation_key for delta compression
42
42
  // Server sends these as __delta_seq and __conflation_key
43
- const sequence = messageData.__delta_seq ?? messageData.sequence;
44
- const conflationKey =
45
- messageData.__conflation_key ?? messageData.conflation_key;
43
+ const sequence = messageData.seq;
44
+ const conflationKey = messageData.conflation_key;
45
+
46
46
  if (typeof sequence === 'number') {
47
47
  (pusherEvent as any).sequence = sequence;
48
48
  }
49
49
  if (conflationKey !== undefined) {
50
50
  (pusherEvent as any).conflation_key = conflationKey;
51
51
  }
52
+
52
53
  return pusherEvent;
53
54
  } catch (e) {
54
55
  throw { type: 'MessageParseError', error: e, data: messageEvent.data };
@@ -285,29 +285,31 @@ export default class DeltaCompressionManager {
285
285
  '%',
286
286
  });
287
287
 
288
- // Parse and return the reconstructed event
288
+ // Parse and return the reconstructed data payload
289
+ // reconstructedMessage is always a JSON string (from JSON.stringify),
290
+ // so we should always parse it before sending to event handlers
291
+ let reconstructedData: any;
289
292
  try {
290
- const parsedMessage = JSON.parse(reconstructedMessage);
291
- let data = parsedMessage.data || parsedMessage;
292
- // Parse data if it's a JSON string (consistent with protocol.ts decodeMessage)
293
- if (typeof data === 'string') {
294
- try {
295
- data = JSON.parse(data);
296
- } catch (e) {}
297
- }
298
- return {
299
- event: event,
300
- channel: channel,
301
- data: data,
302
- };
303
- } catch (e) {
304
- // If not JSON, return as-is
305
- return {
306
- event: event,
307
- channel: channel,
308
- data: reconstructedMessage,
309
- };
293
+ reconstructedData = JSON.parse(JSON.parse(reconstructedMessage));
294
+ } catch (parseError) {
295
+ // This should not happen if the base message was valid JSON
296
+ // Log error but still return the data to avoid breaking the system
297
+ this.error('Failed to parse reconstructed message as JSON', {
298
+ channel,
299
+ event,
300
+ sequence,
301
+ parseError: (parseError as Error).message,
302
+ reconstructedMessageLength: reconstructedMessage.length,
303
+ });
304
+ // Return as string if parsing fails (fallback behavior)
305
+ reconstructedData = reconstructedMessage;
310
306
  }
307
+
308
+ return {
309
+ event: event,
310
+ channel: channel,
311
+ data: reconstructedData,
312
+ };
311
313
  } catch (error) {
312
314
  this.error('Delta decode failed', {
313
315
  channel,
@@ -334,14 +336,25 @@ export default class DeltaCompressionManager {
334
336
  sequence?: number,
335
337
  conflationKey?: string,
336
338
  ): void {
339
+ let parsedMessage: any;
340
+ try {
341
+ parsedMessage = JSON.parse(rawMessage);
342
+ } catch (e) {
343
+ this.log('handleFullMessage parse failed', {
344
+ channel,
345
+ hasSequence: sequence === 0 || !!sequence,
346
+ });
347
+ return;
348
+ }
349
+
337
350
  if (!sequence && sequence !== 0) {
338
351
  // Attempt to extract __delta_seq from payload when not provided separately
339
352
  try {
340
- const parsed = JSON.parse(rawMessage);
341
353
  const candidate =
342
- typeof parsed.data === 'string'
343
- ? (JSON.parse(parsed.data).__delta_seq ?? parsed.__delta_seq)
344
- : (parsed.data?.__delta_seq ?? parsed.__delta_seq);
354
+ typeof parsedMessage.data === 'string'
355
+ ? (JSON.parse(parsedMessage.data).__delta_seq ??
356
+ parsedMessage.__delta_seq)
357
+ : (parsedMessage.data?.__delta_seq ?? parsedMessage.__delta_seq);
345
358
  if (candidate === 0 || candidate) {
346
359
  sequence = candidate;
347
360
  } else {
@@ -360,7 +373,24 @@ export default class DeltaCompressionManager {
360
373
  }
361
374
  }
362
375
 
363
- const messageSize = rawMessage.length;
376
+ if (parsedMessage?.data === undefined) {
377
+ this.log('handleFullMessage missing data field, skipping', {
378
+ channel,
379
+ hasSequence: sequence === 0 || !!sequence,
380
+ });
381
+ return;
382
+ }
383
+
384
+ const baseMessage = JSON.stringify(parsedMessage.data);
385
+ if (typeof baseMessage !== 'string') {
386
+ this.log('handleFullMessage base serialization failed, skipping', {
387
+ channel,
388
+ hasSequence: sequence === 0 || !!sequence,
389
+ });
390
+ return;
391
+ }
392
+
393
+ const messageSize = baseMessage.length;
364
394
 
365
395
  let channelState = this.channelStates.get(channel);
366
396
  if (!channelState) {
@@ -368,10 +398,6 @@ export default class DeltaCompressionManager {
368
398
  this.channelStates.set(channel, channelState);
369
399
  }
370
400
 
371
- // The rawMessage is already stripped of __delta_seq and __conflation_key by pusher.ts
372
- // We use it directly as the base - NO re-parsing/re-stringifying to preserve exact bytes
373
- // (JSON.parse/stringify can change float representations which breaks delta checksums)
374
-
375
401
  // Use the provided conflationKey parameter (already extracted by pusher.ts from original message)
376
402
  const finalConflationKey = conflationKey;
377
403
 
@@ -383,7 +409,7 @@ export default class DeltaCompressionManager {
383
409
  if (channelState.conflationKey && finalConflationKey !== undefined) {
384
410
  channelState.updateConflationCache(
385
411
  finalConflationKey,
386
- rawMessage, // Store raw message directly
412
+ baseMessage,
387
413
  sequence,
388
414
  );
389
415
  this.log('Stored full message (conflation)', {
@@ -393,7 +419,7 @@ export default class DeltaCompressionManager {
393
419
  size: messageSize,
394
420
  });
395
421
  } else {
396
- channelState.setBase(rawMessage, sequence); // Store raw message directly
422
+ channelState.setBase(baseMessage, sequence);
397
423
  this.log('Stored full message', {
398
424
  channel,
399
425
  sequence,
@@ -214,30 +214,12 @@ export default class Pusher {
214
214
  conflationKey = (event as any).conflation_key;
215
215
  }
216
216
 
217
- // Use RAW message and strip metadata fields using REGEX
218
- // This preserves exact byte representation (including float precision)
219
- // We MUST NOT use JSON.parse/stringify as it can change float representations
220
- let fullMessage = event.rawMessage || '';
221
-
222
- if (fullMessage && sequence !== undefined) {
223
- // Strip metadata fields using regex to preserve exact bytes
224
- // Pattern matches: ,"__delta_seq":NUMBER or "__delta_seq":NUMBER, at start
225
- fullMessage = fullMessage.replace(/,"__delta_seq":\d+/g, '');
226
- fullMessage = fullMessage.replace(/"__delta_seq":\d+,/g, '');
227
- // Pattern matches: ,"__conflation_key":"..." or "__conflation_key":"...", at start
228
- fullMessage = fullMessage.replace(
229
- /,"__conflation_key":"[^"]*"/g,
230
- '',
231
- );
232
- fullMessage = fullMessage.replace(
233
- /"__conflation_key":"[^"]*",/g,
234
- '',
235
- );
236
- }
217
+ // Use RAW message as the canonical source for data-only base
218
+ const rawMessage = event.rawMessage || '';
237
219
 
238
220
  this.deltaCompression.handleFullMessage(
239
221
  event.channel,
240
- fullMessage,
222
+ rawMessage,
241
223
  sequence,
242
224
  conflationKey,
243
225
  );