Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
28 changes: 24 additions & 4 deletions packages/core/realtime-js/src/lib/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export type Msg<T> = {

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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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<string, any> | null | undefined, keys: string[]): Record<string, any> {
if (!obj || typeof obj !== 'object') {
return {};
}
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
);
}
}
49 changes: 45 additions & 4 deletions packages/core/realtime-js/test/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,63 @@ 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',
ref: '1',
topic: 'top',
event: 'broadcast',
payload: {
type: 'broadcast',
event: 'user-event',
extra: "bit",
payload: {
a: 'b',
},
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down
Loading