@cendarsoss/pusher-js 8.4.15 → 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.15",
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,22 +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
- return {
292
- event: event,
293
- channel: channel,
294
- data: parsedMessage.data || parsedMessage,
295
- };
296
- } catch (e) {
297
- // If not JSON, return as-is
298
- return {
299
- event: event,
300
- channel: channel,
301
- data: reconstructedMessage,
302
- };
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;
303
306
  }
307
+
308
+ return {
309
+ event: event,
310
+ channel: channel,
311
+ data: reconstructedData,
312
+ };
304
313
  } catch (error) {
305
314
  this.error('Delta decode failed', {
306
315
  channel,
@@ -327,14 +336,25 @@ export default class DeltaCompressionManager {
327
336
  sequence?: number,
328
337
  conflationKey?: string,
329
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
+
330
350
  if (!sequence && sequence !== 0) {
331
351
  // Attempt to extract __delta_seq from payload when not provided separately
332
352
  try {
333
- const parsed = JSON.parse(rawMessage);
334
353
  const candidate =
335
- typeof parsed.data === 'string'
336
- ? (JSON.parse(parsed.data).__delta_seq ?? parsed.__delta_seq)
337
- : (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);
338
358
  if (candidate === 0 || candidate) {
339
359
  sequence = candidate;
340
360
  } else {
@@ -353,7 +373,24 @@ export default class DeltaCompressionManager {
353
373
  }
354
374
  }
355
375
 
356
- 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;
357
394
 
358
395
  let channelState = this.channelStates.get(channel);
359
396
  if (!channelState) {
@@ -361,10 +398,6 @@ export default class DeltaCompressionManager {
361
398
  this.channelStates.set(channel, channelState);
362
399
  }
363
400
 
364
- // The rawMessage is already stripped of __delta_seq and __conflation_key by pusher.ts
365
- // We use it directly as the base - NO re-parsing/re-stringifying to preserve exact bytes
366
- // (JSON.parse/stringify can change float representations which breaks delta checksums)
367
-
368
401
  // Use the provided conflationKey parameter (already extracted by pusher.ts from original message)
369
402
  const finalConflationKey = conflationKey;
370
403
 
@@ -376,7 +409,7 @@ export default class DeltaCompressionManager {
376
409
  if (channelState.conflationKey && finalConflationKey !== undefined) {
377
410
  channelState.updateConflationCache(
378
411
  finalConflationKey,
379
- rawMessage, // Store raw message directly
412
+ baseMessage,
380
413
  sequence,
381
414
  );
382
415
  this.log('Stored full message (conflation)', {
@@ -386,7 +419,7 @@ export default class DeltaCompressionManager {
386
419
  size: messageSize,
387
420
  });
388
421
  } else {
389
- channelState.setBase(rawMessage, sequence); // Store raw message directly
422
+ channelState.setBase(baseMessage, sequence);
390
423
  this.log('Stored full message', {
391
424
  channel,
392
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
  );