UNPKG

@supabase/realtime-js

Version:
155 lines 7.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class Serializer { constructor(allowedMetadataKeys) { this.HEADER_LENGTH = 1; this.USER_BROADCAST_PUSH_META_LENGTH = 6; this.KINDS = { userBroadcastPush: 3, userBroadcast: 4 }; this.BINARY_ENCODING = 0; this.JSON_ENCODING = 1; this.BROADCAST_EVENT = 'broadcast'; this.allowedMetadataKeys = []; this.allowedMetadataKeys = allowedMetadataKeys !== null && allowedMetadataKeys !== void 0 ? allowedMetadataKeys : []; } encode(msg, callback) { if (msg.event === this.BROADCAST_EVENT && !(msg.payload instanceof ArrayBuffer) && typeof msg.payload.event === 'string') { return callback(this._binaryEncodeUserBroadcastPush(msg)); } let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]; return callback(JSON.stringify(payload)); } _binaryEncodeUserBroadcastPush(message) { var _a; if (this._isArrayBuffer((_a = message.payload) === null || _a === void 0 ? void 0 : _a.payload)) { return this._encodeBinaryUserBroadcastPush(message); } else { return this._encodeJsonUserBroadcastPush(message); } } _encodeBinaryUserBroadcastPush(message) { var _a, _b; const userPayload = (_b = (_a = message.payload) === null || _a === void 0 ? void 0 : _a.payload) !== null && _b !== void 0 ? _b : new ArrayBuffer(0); return this._encodeUserBroadcastPush(message, this.BINARY_ENCODING, userPayload); } _encodeJsonUserBroadcastPush(message) { var _a, _b; const userPayload = (_b = (_a = message.payload) === null || _a === void 0 ? void 0 : _a.payload) !== null && _b !== void 0 ? _b : {}; const encoder = new TextEncoder(); const encodedUserPayload = encoder.encode(JSON.stringify(userPayload)).buffer; return this._encodeUserBroadcastPush(message, this.JSON_ENCODING, encodedUserPayload); } _encodeUserBroadcastPush(message, encodingType, encodedPayload) { var _a, _b; const topic = message.topic; const ref = (_a = message.ref) !== null && _a !== void 0 ? _a : ''; const joinRef = (_b = message.join_ref) !== null && _b !== void 0 ? _b : ''; const userEvent = message.payload.event; // 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); // 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++, 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 + encodedPayload.byteLength); combined.set(new Uint8Array(header), 0); combined.set(new Uint8Array(encodedPayload), header.byteLength); return combined.buffer; } decode(rawPayload, callback) { if (this._isArrayBuffer(rawPayload)) { let result = this._binaryDecode(rawPayload); return callback(result); } if (typeof rawPayload === 'string') { const jsonPayload = JSON.parse(rawPayload); const [join_ref, ref, topic, event, payload] = jsonPayload; return callback({ join_ref, ref, topic, event, payload }); } return callback({}); } _binaryDecode(buffer) { const view = new DataView(buffer); const kind = view.getUint8(0); const decoder = new TextDecoder(); switch (kind) { case this.KINDS.userBroadcast: return this._decodeUserBroadcast(buffer, view, decoder); } } _decodeUserBroadcast(buffer, view, decoder) { const topicSize = view.getUint8(1); const userEventSize = view.getUint8(2); const metadataSize = view.getUint8(3); const payloadEncoding = view.getUint8(4); let offset = this.HEADER_LENGTH + 4; const topic = decoder.decode(buffer.slice(offset, offset + topicSize)); offset = offset + topicSize; const userEvent = decoder.decode(buffer.slice(offset, offset + userEventSize)); offset = offset + userEventSize; const metadata = decoder.decode(buffer.slice(offset, offset + metadataSize)); offset = offset + metadataSize; const payload = buffer.slice(offset, buffer.byteLength); const parsedPayload = payloadEncoding === this.JSON_ENCODING ? JSON.parse(decoder.decode(payload)) : payload; const data = { type: this.BROADCAST_EVENT, event: userEvent, payload: parsedPayload, }; // Metadata is optional and always JSON encoded if (metadataSize > 0) { data['meta'] = JSON.parse(metadata); } return { join_ref: null, ref: null, topic: topic, event: this.BROADCAST_EVENT, payload: data }; } _isArrayBuffer(buffer) { var _a; return buffer instanceof ArrayBuffer || ((_a = buffer === null || buffer === void 0 ? void 0 : buffer.constructor) === null || _a === void 0 ? void 0 : _a.name) === 'ArrayBuffer'; } _pick(obj, keys) { if (!obj || typeof obj !== 'object') { return {}; } return Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key))); } } exports.default = Serializer; //# sourceMappingURL=serializer.js.map