@strapi/data-transfer
Version:
Data transfer capabilities for Strapi
68 lines (65 loc) • 3.3 kB
JavaScript
;
/**
* Shared `JSON.stringify` replacer for data-transfer WebSocket frames (push and pull).
*
* Default `JSON.stringify` uses `Buffer.toJSON()` → `{ type: 'Buffer', data: [n,n,...] }`, which
* allocates a large array on the peer during `JSON.parse`. Encode binary values as compact base64 strings instead.
*
* Note: Node runs `Buffer.prototype.toJSON` before the replacer sees a `Buffer` property, so the
* replacer receives `{ type: 'Buffer', data: [...] }` unless the value is already a string (see
* `createTransferAssetStreamChunk` in `transfer-asset-chunk.ts`).
*/ const replacerForTransferWebSocket = (_key, value)=>{
/** `JSON.stringify` throws on bigint; upload metadata or ORM fields may surface as BigInt. */ if (typeof value === 'bigint') {
return value.toString();
}
if (Buffer.isBuffer(value)) {
return value.toString('base64');
}
if (value instanceof Uint8Array) {
const { buffer, byteOffset, byteLength } = value;
if (buffer == null) {
throw new TypeError('Invalid Uint8Array in transfer payload (missing underlying ArrayBuffer); cannot encode for WebSocket');
}
return Buffer.from(buffer, byteOffset, byteLength).toString('base64');
}
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
const v = value;
const { buffer, byteOffset, byteLength } = v;
if (buffer == null) {
throw new TypeError('Invalid typed array in transfer payload (missing underlying ArrayBuffer); cannot encode for WebSocket');
}
return Buffer.from(buffer, byteOffset, byteLength).toString('base64');
}
return value;
};
/**
* `JSON.stringify` invokes an own enumerable `toJSON` on the root value before replacers run. If that
* method returns `undefined`, the whole `JSON.stringify` result is `undefined`, and `ws.send(undefined)`
* throws ("The first argument must be of type string or an instance of Buffer... Received undefined").
* Spreading transfer messages (`{ ...message, uuid }`) can copy an enumerable `toJSON` from user / ORM
* objects onto the wire payload — strip it on the root object we control.
*/ function stripRootToJSONMethod(payload) {
if (typeof payload.toJSON === 'function') {
delete payload.toJSON;
}
}
/**
* Serialize a transfer WebSocket envelope. Never returns `undefined` (unlike raw `JSON.stringify`).
*/ function stringifyTransferWebSocketPayload(payload) {
stripRootToJSONMethod(payload);
let s;
try {
s = JSON.stringify(payload, replacerForTransferWebSocket);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new TypeError(`Transfer WebSocket payload could not be serialized to JSON: ${message}`);
}
if (typeof s !== 'string') {
throw new TypeError('Transfer WebSocket payload could not be serialized to JSON (result was undefined). Check for Symbol or other non-JSON values on the root payload.');
}
return s;
}
exports.replacerForTransferWebSocket = replacerForTransferWebSocket;
exports.stringifyTransferWebSocketPayload = stringifyTransferWebSocketPayload;
exports.stripRootToJSONMethod = stripRootToJSONMethod;
//# sourceMappingURL=transfer-websocket-json.js.map