From ad9d90c33f2cecaaab76ad72d7c30889582c5483 Mon Sep 17 00:00:00 2001 From: Eduardo Gurgel Pinho Date: Wed, 26 Nov 2025 14:55:30 +1300 Subject: [PATCH 1/4] feat(realtime): add metadata to realtime user broadcast push We don't have any usage at the moment for this extra metadata but we want to have this ready for the future --- .../core/realtime-js/src/lib/serializer.ts | 28 +++++++++-- .../core/realtime-js/test/serializer.test.ts | 49 +++++++++++++++++-- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/packages/core/realtime-js/src/lib/serializer.ts b/packages/core/realtime-js/src/lib/serializer.ts index b1cbd932a..46cc81043 100644 --- a/packages/core/realtime-js/src/lib/serializer.ts +++ b/packages/core/realtime-js/src/lib/serializer.ts @@ -10,8 +10,7 @@ export type Msg = { export default class Serializer { HEADER_LENGTH = 1 - META_LENGTH = 4 - USER_BROADCAST_PUSH_META_LENGTH = 5 + USER_BROADCAST_PUSH_META_LENGTH = 6 KINDS = { userBroadcastPush: 3, userBroadcast: 4 } BINARY_ENCODING = 0 JSON_ENCODING = 1 @@ -46,13 +45,17 @@ export default class Serializer { const joinRef = message.join_ref ?? '' const userEvent = message.payload.event const userPayload = message.payload?.payload ?? new ArrayBuffer(0) + const rest = this._omit(message.payload, ['type', 'event', 'payload']) + + const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) const metaLength = this.USER_BROADCAST_PUSH_META_LENGTH + joinRef.length + ref.length + topic.length + - userEvent.length + userEvent.length + + metadata.length const header = new ArrayBuffer(this.HEADER_LENGTH + metaLength) let view = new DataView(header) @@ -63,11 +66,13 @@ export default class Serializer { view.setUint8(offset++, ref.length) view.setUint8(offset++, topic.length) view.setUint8(offset++, userEvent.length) + view.setUint8(offset++, metadata.length) view.setUint8(offset++, this.BINARY_ENCODING) Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0))) + Array.from(metadata, (char) => view.setUint8(offset++, char.charCodeAt(0))) var combined = new Uint8Array(header.byteLength + userPayload.byteLength) combined.set(new Uint8Array(header), 0) @@ -81,8 +86,11 @@ export default class Serializer { const ref = message.ref ?? '' const joinRef = message.join_ref ?? '' const userEvent = message.payload.event + const rest = this._omit(message.payload, ['type', 'event', 'payload']) const userPayload = message.payload?.payload ?? {} + const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) + const encoder = new TextEncoder() // Encodes to UTF-8 const encodedUserPayload = encoder.encode(JSON.stringify(userPayload)).buffer @@ -91,7 +99,8 @@ export default class Serializer { joinRef.length + ref.length + topic.length + - userEvent.length + userEvent.length + + metadata.length const header = new ArrayBuffer(this.HEADER_LENGTH + metaLength) let view = new DataView(header) @@ -102,11 +111,13 @@ export default class Serializer { view.setUint8(offset++, ref.length) view.setUint8(offset++, topic.length) view.setUint8(offset++, userEvent.length) + view.setUint8(offset++, metadata.length) view.setUint8(offset++, this.JSON_ENCODING) Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0))) + Array.from(metadata, (char) => view.setUint8(offset++, char.charCodeAt(0))) var combined = new Uint8Array(header.byteLength + encodedUserPayload.byteLength) combined.set(new Uint8Array(header), 0) @@ -185,4 +196,13 @@ export default class Serializer { private _isArrayBuffer(buffer: any): boolean { return buffer instanceof ArrayBuffer || buffer?.constructor?.name === 'ArrayBuffer' } + + private _omit(obj: Record | null | undefined, keys: string[]): Record { + if (!obj || typeof obj !== 'object') { + return {}; + } + return Object.fromEntries( + Object.entries(obj).filter(([key]) => !keys.includes(key)) + ); + } } diff --git a/packages/core/realtime-js/test/serializer.test.ts b/packages/core/realtime-js/test/serializer.test.ts index eb4798540..3bb9ebdd7 100644 --- a/packages/core/realtime-js/test/serializer.test.ts +++ b/packages/core/realtime-js/test/serializer.test.ts @@ -67,19 +67,53 @@ describe('JSON', () => { }) describe('binary', () => { + it('encodes user broadcast push with JSON payload no metadata', async () => { + // 3 -> user_broadcast_push + // 2 join_ref length + // 1 for ref length + // 3 for topic length + // 10 for user event length + // 0 for metadata length + // 1 for JSON encoding + // actual join ref + // actual ref + // actual topic + // actual user event + // no actual metadata + // actual payload + let bin = '\x03\x02\x01\x03\x0a\x00\x01101topuser-event{"a":"b"}' + + const result = await encodeAsync(serializer, { + join_ref: '10', + ref: '1', + topic: 'top', + event: 'broadcast', + payload: { + type: 'broadcast', + event: 'user-event', + payload: { + a: 'b', + }, + }, + }) + expect(decoder.decode(result as ArrayBuffer)).toBe(bin) + }) + it('encodes user broadcast push with JSON payload', async () => { // 3 -> user_broadcast_push // 2 join_ref length // 1 for ref length // 3 for topic length // 10 for user event length + // 15 for metadata length // 1 for JSON encoding // actual join ref // actual ref // actual topic // actual user event + // actual metadata // actual payload - let bin = '\x03\x02\x01\x03\x0a\x01101topuser-event{"a":"b"}' + let bin = '\x03\x02\x01\x03\x0a\x0f\x01101topuser-event{"extra":"bit"}{"a":"b"}' const result = await encodeAsync(serializer, { join_ref: '10', @@ -87,7 +121,9 @@ describe('binary', () => { topic: 'top', event: 'broadcast', payload: { + type: 'broadcast', event: 'user-event', + extra: "bit", payload: { a: 'b', }, @@ -102,13 +138,14 @@ describe('binary', () => { // 0 for ref length // 3 for topic length // 10 for user event length + // 0 for metadata length // 1 for JSON encoding // actual join ref // actual ref // actual topic // actual user event // actual payload - let bin = '\x03\x00\x00\x03\x0a\x01topuser-event{"a":"b"}' + let bin = '\x03\x00\x00\x03\x0a\x00\x01topuser-event{"a":"b"}' const result = await encodeAsync(serializer, { topic: 'top', @@ -129,13 +166,15 @@ describe('binary', () => { // 1 for ref length // 3 for topic length // 10 for user event length + // 0 for metadata length // 0 for Binary encoding // actual join ref // actual ref // actual topic // actual user event + // no actual metadata // actual payload - let bin = '\x03\x02\x01\x03\x0a\x00101topuser-event\x01\x04' + let bin = '\x03\x02\x01\x03\x0a\x00\x00101topuser-event\x01\x04' const result = await encodeAsync(serializer, { join_ref: '10', @@ -156,13 +195,15 @@ describe('binary', () => { // 0 for ref length // 3 for topic length // 10 for user event length + // 0 for metadata length // 0 for Binary encoding // actual join ref // actual ref // actual topic // actual user event + // no actual metadata // actual payload - let bin = '\x03\x00\x00\x03\x0a\x00topuser-event\x01\x04' + let bin = '\x03\x00\x00\x03\x0a\x00\x00topuser-event\x01\x04' const result = await encodeAsync(serializer, { topic: 'top', From 99d870f9a71b010a44598557c24c5f834ded3740 Mon Sep 17 00:00:00 2001 From: Eduardo Gurgel Pinho Date: Wed, 26 Nov 2025 15:33:52 +1300 Subject: [PATCH 2/4] fix(realtime): throw error when uint8 fields exceeds 255 --- .../core/realtime-js/src/lib/serializer.ts | 39 +++- .../core/realtime-js/test/serializer.test.ts | 185 +++++++++++++++++- 2 files changed, 219 insertions(+), 5 deletions(-) diff --git a/packages/core/realtime-js/src/lib/serializer.ts b/packages/core/realtime-js/src/lib/serializer.ts index 46cc81043..adda0cb09 100644 --- a/packages/core/realtime-js/src/lib/serializer.ts +++ b/packages/core/realtime-js/src/lib/serializer.ts @@ -48,6 +48,22 @@ export default class Serializer { const rest = this._omit(message.payload, ['type', 'event', 'payload']) const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) + // Validate lengths don't exceed uint8 max value (255) + if (joinRef.length > 255) { + throw new Error(`joinRef length ${joinRef.length} exceeds maximum of 255`) + } + if (ref.length > 255) { + throw new Error(`ref length ${ref.length} exceeds maximum of 255`) + } + if (topic.length > 255) { + throw new Error(`topic length ${topic.length} exceeds maximum of 255`) + } + if (userEvent.length > 255) { + throw new Error(`userEvent length ${userEvent.length} exceeds maximum of 255`) + } + if (metadata.length > 255) { + throw new Error(`metadata length ${metadata.length} exceeds maximum of 255`) + } const metaLength = this.USER_BROADCAST_PUSH_META_LENGTH + @@ -91,6 +107,23 @@ export default class Serializer { const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) + // Validate lengths don't exceed uint8 max value (255) + if (joinRef.length > 255) { + throw new Error(`joinRef length ${joinRef.length} exceeds maximum of 255`) + } + if (ref.length > 255) { + throw new Error(`ref length ${ref.length} exceeds maximum of 255`) + } + if (topic.length > 255) { + throw new Error(`topic length ${topic.length} exceeds maximum of 255`) + } + if (userEvent.length > 255) { + throw new Error(`userEvent length ${userEvent.length} exceeds maximum of 255`) + } + if (metadata.length > 255) { + throw new Error(`metadata length ${metadata.length} exceeds maximum of 255`) + } + const encoder = new TextEncoder() // Encodes to UTF-8 const encodedUserPayload = encoder.encode(JSON.stringify(userPayload)).buffer @@ -199,10 +232,8 @@ export default class Serializer { private _omit(obj: Record | null | undefined, keys: string[]): Record { if (!obj || typeof obj !== 'object') { - return {}; + return {} } - return Object.fromEntries( - Object.entries(obj).filter(([key]) => !keys.includes(key)) - ); + return Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key))) } } diff --git a/packages/core/realtime-js/test/serializer.test.ts b/packages/core/realtime-js/test/serializer.test.ts index 3bb9ebdd7..0ad5b9084 100644 --- a/packages/core/realtime-js/test/serializer.test.ts +++ b/packages/core/realtime-js/test/serializer.test.ts @@ -123,7 +123,7 @@ describe('binary', () => { payload: { type: 'broadcast', event: 'user-event', - extra: "bit", + extra: 'bit', payload: { a: 'b', }, @@ -160,6 +160,108 @@ describe('binary', () => { expect(decoder.decode(result as ArrayBuffer)).toBe(bin) }) + it('throws error when joinRef exceeds 255 characters with JSON payload', async () => { + const longJoinRef = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + join_ref: longJoinRef, + ref: '1', + topic: 'top', + event: 'broadcast', + payload: { + type: 'broadcast', + event: 'user-event', + payload: { + a: 'b', + }, + }, + }) + ).rejects.toThrow('joinRef length 256 exceeds maximum of 255') + }) + + it('throws error when ref exceeds 255 characters with JSON payload', async () => { + const longRef = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + join_ref: '10', + ref: longRef, + topic: 'top', + event: 'broadcast', + payload: { + type: 'broadcast', + event: 'user-event', + payload: { + a: 'b', + }, + }, + }) + ).rejects.toThrow('ref length 256 exceeds maximum of 255') + }) + + it('throws error when topic exceeds 255 characters with JSON payload', async () => { + const longTopic = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + join_ref: '10', + ref: '1', + topic: longTopic, + event: 'broadcast', + payload: { + type: 'broadcast', + event: 'user-event', + payload: { + a: 'b', + }, + }, + }) + ).rejects.toThrow('topic length 256 exceeds maximum of 255') + }) + + it('throws error when user event exceeds 255 characters with JSON payload', async () => { + const longUserEvent = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + join_ref: '10', + ref: '1', + topic: 'top', + event: 'broadcast', + payload: { + type: 'broadcast', + event: longUserEvent, + payload: { + a: 'b', + }, + }, + }) + ).rejects.toThrow('userEvent length 256 exceeds maximum of 255') + }) + + it('throws error when metadata exceeds 255 characters with JSON payload', async () => { + // Create metadata that will exceed 255 chars when JSON.stringify'd + const longValue = 'a'.repeat(240) + + await expect( + encodeAsync(serializer, { + join_ref: '10', + ref: '1', + topic: 'top', + event: 'broadcast', + payload: { + type: 'broadcast', + event: 'user-event', + payload: { + a: 'b', + }, + extraField: longValue, // This will be in the metadata (rest) + }, + }) + ).rejects.toThrow('metadata length') + }) + it('encodes user broadcast push with Binary payload', async () => { // 3 -> user_broadcast_push // 2 join_ref length @@ -216,6 +318,87 @@ describe('binary', () => { expect(decoder.decode(result as ArrayBuffer)).toBe(bin) }) + it('throws error when joinRef exceeds 255 characters', async () => { + const longJoinRef = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + topic: 'top', + event: 'broadcast', + join_ref: longJoinRef, + payload: { + event: 'user-event', + payload: binPayload(), + }, + }) + ).rejects.toThrow('joinRef length 256 exceeds maximum of 255') + }) + + it('throws error when ref exceeds 255 characters', async () => { + const longRef = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + topic: 'top', + event: 'broadcast', + ref: longRef, + payload: { + event: 'user-event', + payload: binPayload(), + }, + }) + ).rejects.toThrow('ref length 256 exceeds maximum of 255') + }) + + it('throws error when topic exceeds 255 characters', async () => { + const longTopic = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + topic: longTopic, + event: 'broadcast', + payload: { + event: 'user-event', + payload: binPayload(), + }, + }) + ).rejects.toThrow('topic length 256 exceeds maximum of 255') + }) + + it('throws error when user event exceeds 255 characters', async () => { + const longUserEvent = 'a'.repeat(256) + + await expect( + encodeAsync(serializer, { + topic: 'top', + event: 'broadcast', + payload: { + event: longUserEvent, + payload: binPayload(), + }, + }) + ).rejects.toThrow('userEvent length 256 exceeds maximum of 255') + }) + + it('throws error when metadata exceeds 255 characters', async () => { + // Create metadata that will exceed 255 chars when JSON.stringify'd + // JSON.stringify will add quotes and colons, so we need a bit less than 256 + const longValue = 'a'.repeat(240) + + await expect( + encodeAsync(serializer, { + topic: 'top', + event: 'broadcast', + payload: { + event: 'user-event', + payload: binPayload(), + extraField: longValue, // This will be in the metadata + }, + }) + ).rejects.toThrow('metadata length') + // Note: The exact length will depend on JSON.stringify output + }) + it('decodes user broadcast with JSON payload and no metadata', async () => { // 4 -> user_broadcast // 3 for topic length From 33e584b260e383e282d11524997e80a22e931137 Mon Sep 17 00:00:00 2001 From: Eduardo Gurgel Pinho Date: Wed, 26 Nov 2025 15:45:36 +1300 Subject: [PATCH 3/4] chore(realtime): refactor serializer functions --- .../core/realtime-js/src/lib/serializer.ts | 76 ++++--------------- 1 file changed, 15 insertions(+), 61 deletions(-) diff --git a/packages/core/realtime-js/src/lib/serializer.ts b/packages/core/realtime-js/src/lib/serializer.ts index adda0cb09..d13c07dce 100644 --- a/packages/core/realtime-js/src/lib/serializer.ts +++ b/packages/core/realtime-js/src/lib/serializer.ts @@ -40,70 +40,27 @@ export default class Serializer { } private _encodeBinaryUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) { - const topic = message.topic - const ref = message.ref ?? '' - const joinRef = message.join_ref ?? '' - const userEvent = message.payload.event const userPayload = message.payload?.payload ?? new ArrayBuffer(0) - const rest = this._omit(message.payload, ['type', 'event', 'payload']) - - const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) - // Validate lengths don't exceed uint8 max value (255) - if (joinRef.length > 255) { - throw new Error(`joinRef length ${joinRef.length} exceeds maximum of 255`) - } - if (ref.length > 255) { - throw new Error(`ref length ${ref.length} exceeds maximum of 255`) - } - if (topic.length > 255) { - throw new Error(`topic length ${topic.length} exceeds maximum of 255`) - } - if (userEvent.length > 255) { - throw new Error(`userEvent length ${userEvent.length} exceeds maximum of 255`) - } - if (metadata.length > 255) { - throw new Error(`metadata length ${metadata.length} exceeds maximum of 255`) - } - - const metaLength = - this.USER_BROADCAST_PUSH_META_LENGTH + - joinRef.length + - ref.length + - topic.length + - userEvent.length + - metadata.length - - const header = new ArrayBuffer(this.HEADER_LENGTH + metaLength) - let view = new DataView(header) - let offset = 0 - - view.setUint8(offset++, this.KINDS.userBroadcastPush) // kind - view.setUint8(offset++, joinRef.length) - view.setUint8(offset++, ref.length) - view.setUint8(offset++, topic.length) - view.setUint8(offset++, userEvent.length) - view.setUint8(offset++, metadata.length) - view.setUint8(offset++, this.BINARY_ENCODING) - Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0))) - Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0))) - Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0))) - Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0))) - Array.from(metadata, (char) => view.setUint8(offset++, char.charCodeAt(0))) - - var combined = new Uint8Array(header.byteLength + userPayload.byteLength) - combined.set(new Uint8Array(header), 0) - combined.set(new Uint8Array(userPayload), header.byteLength) - - return combined.buffer + return this._encodeUserBroadcastPush(message, this.BINARY_ENCODING, userPayload) } private _encodeJsonUserBroadcastPush(message: Msg<{ event: string } & { [key: string]: any }>) { + const userPayload = message.payload?.payload ?? {} + const encoder = new TextEncoder() + const encodedUserPayload = encoder.encode(JSON.stringify(userPayload)).buffer + return this._encodeUserBroadcastPush(message, this.JSON_ENCODING, encodedUserPayload) + } + + private _encodeUserBroadcastPush( + message: Msg<{ event: string } & { [key: string]: any }>, + encodingType: number, + encodedPayload: ArrayBuffer + ) { const topic = message.topic const ref = message.ref ?? '' const joinRef = message.join_ref ?? '' const userEvent = message.payload.event const rest = this._omit(message.payload, ['type', 'event', 'payload']) - const userPayload = message.payload?.payload ?? {} const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) @@ -124,9 +81,6 @@ export default class Serializer { throw new Error(`metadata length ${metadata.length} exceeds maximum of 255`) } - const encoder = new TextEncoder() // Encodes to UTF-8 - const encodedUserPayload = encoder.encode(JSON.stringify(userPayload)).buffer - const metaLength = this.USER_BROADCAST_PUSH_META_LENGTH + joinRef.length + @@ -145,16 +99,16 @@ export default class Serializer { view.setUint8(offset++, topic.length) view.setUint8(offset++, userEvent.length) view.setUint8(offset++, metadata.length) - view.setUint8(offset++, this.JSON_ENCODING) + view.setUint8(offset++, encodingType) Array.from(joinRef, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(ref, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(topic, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(userEvent, (char) => view.setUint8(offset++, char.charCodeAt(0))) Array.from(metadata, (char) => view.setUint8(offset++, char.charCodeAt(0))) - var combined = new Uint8Array(header.byteLength + encodedUserPayload.byteLength) + var combined = new Uint8Array(header.byteLength + encodedPayload.byteLength) combined.set(new Uint8Array(header), 0) - combined.set(new Uint8Array(encodedUserPayload), header.byteLength) + combined.set(new Uint8Array(encodedPayload), header.byteLength) return combined.buffer } From 1c7d8c505a7f76ff7d30729799b6283acbd7b01e Mon Sep 17 00:00:00 2001 From: Eduardo Gurgel Pinho Date: Wed, 26 Nov 2025 17:50:16 +1300 Subject: [PATCH 4/4] fix(realtime): change serializer to have allowed metadata keys --- .../core/realtime-js/src/lib/serializer.ts | 16 ++++++++-- .../core/realtime-js/test/serializer.test.ts | 29 +++++++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/core/realtime-js/src/lib/serializer.ts b/packages/core/realtime-js/src/lib/serializer.ts index d13c07dce..668ade2be 100644 --- a/packages/core/realtime-js/src/lib/serializer.ts +++ b/packages/core/realtime-js/src/lib/serializer.ts @@ -16,6 +16,12 @@ export default class Serializer { JSON_ENCODING = 1 BROADCAST_EVENT = 'broadcast' + allowedMetadataKeys: string[] = [] + + constructor(allowedMetadataKeys?: string[] | null) { + this.allowedMetadataKeys = allowedMetadataKeys ?? [] + } + encode(msg: Msg<{ [key: string]: any }>, callback: (result: ArrayBuffer | string) => any) { if ( msg.event === this.BROADCAST_EVENT && @@ -60,7 +66,11 @@ export default class Serializer { const ref = message.ref ?? '' const joinRef = message.join_ref ?? '' const userEvent = message.payload.event - const rest = this._omit(message.payload, ['type', 'event', 'payload']) + + // Filter metadata based on allowed keys + const rest = this.allowedMetadataKeys + ? this._pick(message.payload, this.allowedMetadataKeys) + : {} const metadata = Object.keys(rest).length === 0 ? '' : JSON.stringify(rest) @@ -184,10 +194,10 @@ export default class Serializer { return buffer instanceof ArrayBuffer || buffer?.constructor?.name === 'ArrayBuffer' } - private _omit(obj: Record | null | undefined, keys: string[]): Record { + private _pick(obj: Record | null | undefined, keys: string[]): Record { if (!obj || typeof obj !== 'object') { return {} } - return Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key))) + return Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key))) } } diff --git a/packages/core/realtime-js/test/serializer.test.ts b/packages/core/realtime-js/test/serializer.test.ts index 0ad5b9084..9952012d6 100644 --- a/packages/core/realtime-js/test/serializer.test.ts +++ b/packages/core/realtime-js/test/serializer.test.ts @@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest' import Serializer from '../src/lib/serializer' import type { Msg } from '../src/lib/serializer' -let serializer = new Serializer() let decoder = new TextDecoder() const encodeAsync = ( @@ -46,21 +45,25 @@ let binPayload = () => { describe('JSON', () => { it('encodes', async () => { + const serializer = new Serializer() const result = await encodeAsync(serializer, exampleMsg) expect(result).toBe('["0","1","t","e",{"foo":1}]') }) it('encodes missing refs', async () => { + const serializer = new Serializer() const result = await encodeAsync(serializer, missingRefExampleMsg) expect(result).toBe('[null,null,"t","e",{"foo":1}]') }) it('decodes', async () => { + const serializer = new Serializer() const result = await decodeAsync(serializer, '["0","1","t","e",{"foo":1}]') expect(result).toEqual(exampleMsg) }) it('decodes missing refs', async () => { + const serializer = new Serializer() const result = await decodeAsync(serializer, '[null,null,"t","e",{"foo":1}]') expect(result).toEqual(missingRefExampleMsg) }) @@ -68,6 +71,7 @@ describe('JSON', () => { describe('binary', () => { it('encodes user broadcast push with JSON payload no metadata', async () => { + const serializer = new Serializer() // 3 -> user_broadcast_push // 2 join_ref length // 1 for ref length @@ -99,7 +103,9 @@ describe('binary', () => { expect(decoder.decode(result as ArrayBuffer)).toBe(bin) }) - it('encodes user broadcast push with JSON payload', async () => { + it('encodes user broadcast push with JSON payload with allowed metadata', async () => { + const serializer = new Serializer(['extra']) + // 3 -> user_broadcast_push // 2 join_ref length // 1 for ref length @@ -124,6 +130,8 @@ describe('binary', () => { type: 'broadcast', event: 'user-event', extra: 'bit', + // store field is not included into metadata + store: true, payload: { a: 'b', }, @@ -133,6 +141,7 @@ describe('binary', () => { }) it('encodes user broadcast push with JSON payload no refs', async () => { + const serializer = new Serializer() // 3 -> user_broadcast_push // 0 join_ref length // 0 for ref length @@ -161,6 +170,7 @@ describe('binary', () => { }) it('throws error when joinRef exceeds 255 characters with JSON payload', async () => { + const serializer = new Serializer() const longJoinRef = 'a'.repeat(256) await expect( @@ -181,6 +191,7 @@ describe('binary', () => { }) it('throws error when ref exceeds 255 characters with JSON payload', async () => { + const serializer = new Serializer() const longRef = 'a'.repeat(256) await expect( @@ -201,6 +212,7 @@ describe('binary', () => { }) it('throws error when topic exceeds 255 characters with JSON payload', async () => { + const serializer = new Serializer() const longTopic = 'a'.repeat(256) await expect( @@ -221,6 +233,7 @@ describe('binary', () => { }) it('throws error when user event exceeds 255 characters with JSON payload', async () => { + const serializer = new Serializer() const longUserEvent = 'a'.repeat(256) await expect( @@ -241,6 +254,7 @@ describe('binary', () => { }) it('throws error when metadata exceeds 255 characters with JSON payload', async () => { + const serializer = new Serializer(['extraField']) // Create metadata that will exceed 255 chars when JSON.stringify'd const longValue = 'a'.repeat(240) @@ -263,6 +277,7 @@ describe('binary', () => { }) it('encodes user broadcast push with Binary payload', async () => { + const serializer = new Serializer() // 3 -> user_broadcast_push // 2 join_ref length // 1 for ref length @@ -292,6 +307,7 @@ describe('binary', () => { }) it('encodes user broadcast push with Binary payload no refs', async () => { + const serializer = new Serializer() // 3 -> user_broadcast_push // 0 join_ref length // 0 for ref length @@ -319,6 +335,7 @@ describe('binary', () => { }) it('throws error when joinRef exceeds 255 characters', async () => { + const serializer = new Serializer() const longJoinRef = 'a'.repeat(256) await expect( @@ -335,6 +352,7 @@ describe('binary', () => { }) it('throws error when ref exceeds 255 characters', async () => { + const serializer = new Serializer() const longRef = 'a'.repeat(256) await expect( @@ -351,6 +369,7 @@ describe('binary', () => { }) it('throws error when topic exceeds 255 characters', async () => { + const serializer = new Serializer() const longTopic = 'a'.repeat(256) await expect( @@ -366,6 +385,7 @@ describe('binary', () => { }) it('throws error when user event exceeds 255 characters', async () => { + const serializer = new Serializer() const longUserEvent = 'a'.repeat(256) await expect( @@ -381,6 +401,7 @@ describe('binary', () => { }) it('throws error when metadata exceeds 255 characters', async () => { + const serializer = new Serializer(['extraField']) // Create metadata that will exceed 255 chars when JSON.stringify'd // JSON.stringify will add quotes and colons, so we need a bit less than 256 const longValue = 'a'.repeat(240) @@ -400,6 +421,7 @@ describe('binary', () => { }) it('decodes user broadcast with JSON payload and no metadata', async () => { + const serializer = new Serializer() // 4 -> user_broadcast // 3 for topic length // 10 for user event length @@ -427,6 +449,7 @@ describe('binary', () => { }) it('decodes user broadcast with JSON payload and metadata', async () => { + const serializer = new Serializer() // 4 -> user_broadcast // 3 for topic length // 10 for user event length (\x0a) @@ -455,6 +478,7 @@ describe('binary', () => { }) it('decodes user broadcast with binary payload and no metadata', async () => { + const serializer = new Serializer() // 4 -> user_broadcast // 3 for topic length // 10 for user event length (\x0a) @@ -481,6 +505,7 @@ describe('binary', () => { }) it('decodes user broadcast with binary payload and metadata', async () => { + const serializer = new Serializer() // 4 -> user_broadcast // 3 for topic length // 10 for user event length (\x0a)