2425 lines
81 KiB
JavaScript
2425 lines
81 KiB
JavaScript
const require_shared = require("./shared-u-KYXAax.cjs");
|
|
const require_typed_array = require("./typed-array-BkSwWNFI.cjs");
|
|
//#region src/core/node.ts
|
|
function createSerovalNode(t, i, s, c, m, p, e, a, f, b, o, l) {
|
|
if (c === void 0 && m === void 0 && p === void 0 && e === void 0 && a === void 0 && f === void 0 && b === void 0 && o === void 0 && l === void 0) return {
|
|
t,
|
|
i,
|
|
s
|
|
};
|
|
return {
|
|
t,
|
|
i,
|
|
s,
|
|
c,
|
|
m,
|
|
p,
|
|
e,
|
|
a,
|
|
f,
|
|
b,
|
|
o,
|
|
l
|
|
};
|
|
}
|
|
//#endregion
|
|
//#region src/core/literals.ts
|
|
function createConstantNode(value) {
|
|
return createSerovalNode(2, void 0, value);
|
|
}
|
|
const TRUE_NODE = /* @__PURE__ */ createConstantNode(2);
|
|
const FALSE_NODE = /* @__PURE__ */ createConstantNode(3);
|
|
const UNDEFINED_NODE = /* @__PURE__ */ createConstantNode(1);
|
|
const NULL_NODE = /* @__PURE__ */ createConstantNode(0);
|
|
const NEG_ZERO_NODE = /* @__PURE__ */ createConstantNode(4);
|
|
const INFINITY_NODE = /* @__PURE__ */ createConstantNode(5);
|
|
const NEG_INFINITY_NODE = /* @__PURE__ */ createConstantNode(6);
|
|
const NAN_NODE = /* @__PURE__ */ createConstantNode(7);
|
|
//#endregion
|
|
//#region src/core/utils/error.ts
|
|
function getErrorConstructor(error) {
|
|
for (let i = 1; i <= 6; i++) if (error instanceof require_shared.ERROR_CONSTRUCTOR[i]) return i;
|
|
return 0;
|
|
}
|
|
function getErrorOptions(error, features) {
|
|
const options = Object.create(null);
|
|
const construct = require_shared.ERROR_CONSTRUCTOR_STRING[getErrorConstructor(error)];
|
|
if (error.name !== construct) options.name = error.name;
|
|
else if (error.constructor.name !== construct) options.name = error.constructor.name;
|
|
for (const name of Object.getOwnPropertyNames(error)) if (name !== "name" && name !== "message" && (name !== "stack" || features & 4)) options[name] = error[name];
|
|
return options;
|
|
}
|
|
//#endregion
|
|
//#region src/core/utils/get-object-flag.ts
|
|
function getObjectFlag(obj) {
|
|
if (Object.isExtensible(obj)) return 0;
|
|
if (Object.isFrozen(obj)) return 3;
|
|
if (Object.isSealed(obj)) return 2;
|
|
return 1;
|
|
}
|
|
//#endregion
|
|
//#region src/core/base-primitives.ts
|
|
function createNumberNode(value) {
|
|
switch (value) {
|
|
case Number.POSITIVE_INFINITY: return INFINITY_NODE;
|
|
case Number.NEGATIVE_INFINITY: return NEG_INFINITY_NODE;
|
|
}
|
|
if (value !== value) return NAN_NODE;
|
|
if (Object.is(value, -0)) return NEG_ZERO_NODE;
|
|
return createSerovalNode(0, void 0, value);
|
|
}
|
|
function createStringNode(value) {
|
|
return createSerovalNode(1, void 0, require_shared.serializeString(value));
|
|
}
|
|
function createBigIntNode(current) {
|
|
return createSerovalNode(3, void 0, "" + current);
|
|
}
|
|
function createIndexedValueNode(id) {
|
|
return createSerovalNode(4, id);
|
|
}
|
|
const getDateTime = Date.prototype.getTime;
|
|
const getDateISOString = Date.prototype.toISOString;
|
|
function createDateNode(id, current) {
|
|
const timestamp = getDateTime.call(current);
|
|
return createSerovalNode(5, id, timestamp === timestamp ? getDateISOString.call(current) : "");
|
|
}
|
|
function createTemporalNode(id, type, temporalClass, current) {
|
|
return createSerovalNode(36, id, temporalClass.prototype.toString.call(current), type);
|
|
}
|
|
const getRegExpSource = Object.getOwnPropertyDescriptor(RegExp.prototype, "source").get;
|
|
const getRegExpFlags = Object.getOwnPropertyDescriptor(RegExp.prototype, "flags").get;
|
|
function createRegExpNode(id, current) {
|
|
return createSerovalNode(6, id, void 0, require_shared.serializeString(getRegExpSource.call(current)), getRegExpFlags.call(current));
|
|
}
|
|
function createWKSymbolNode(id, current) {
|
|
return createSerovalNode(17, id, require_shared.INV_SYMBOL_REF[current]);
|
|
}
|
|
function createReferenceNode(id, referenceId) {
|
|
return createSerovalNode(18, id, require_shared.serializeString(referenceId));
|
|
}
|
|
function createPluginNode(id, tag, value) {
|
|
return createSerovalNode(25, id, value, require_shared.serializeString(tag));
|
|
}
|
|
function createArrayNode(id, current, parsedItems) {
|
|
return createSerovalNode(9, id, void 0, void 0, void 0, void 0, void 0, parsedItems, void 0, void 0, getObjectFlag(current));
|
|
}
|
|
function createBoxedNode(id, boxed) {
|
|
return createSerovalNode(21, id, void 0, void 0, void 0, void 0, void 0, void 0, boxed);
|
|
}
|
|
const MAX_TYPED_ARRAY_LENGTH = 1e6;
|
|
/**
|
|
* Rejects a sparse array with more holes than the typed array limit. Each hole
|
|
* still costs a node and output, however few elements the array holds.
|
|
*/
|
|
function assertArrayHoles(current) {
|
|
const len = current.length;
|
|
if (len > MAX_TYPED_ARRAY_LENGTH && len - Object.keys(current).length > MAX_TYPED_ARRAY_LENGTH) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
function createViewNode(type, id, current, view, buffer) {
|
|
const isDataView = type === 20;
|
|
const size = view.length;
|
|
if (size > MAX_TYPED_ARRAY_LENGTH) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
return createSerovalNode(type, id, void 0, isDataView ? void 0 : require_typed_array.getTypedArrayName(current), void 0, void 0, void 0, void 0, buffer, view.byteOffset, void 0, size);
|
|
}
|
|
function createErrorNode(type, id, current, options) {
|
|
return createSerovalNode(type, id, getErrorConstructor(current), void 0, require_shared.serializeString(current.message), options);
|
|
}
|
|
function createSetNode(id, items) {
|
|
return createSerovalNode(7, id, void 0, void 0, void 0, void 0, void 0, items);
|
|
}
|
|
function createIteratorFactoryInstanceNode(type, factory, items) {
|
|
return createSerovalNode(type, void 0, void 0, void 0, void 0, void 0, void 0, [factory, items]);
|
|
}
|
|
function createStreamConstructorNode(id, factory, sequence, live) {
|
|
return createSerovalNode(31, id, void 0, void 0, void 0, void 0, void 0, sequence, factory, void 0, void 0, live);
|
|
}
|
|
function createStreamEventNode(type, id, parsed) {
|
|
return createSerovalNode(type, id, void 0, void 0, void 0, void 0, void 0, void 0, parsed);
|
|
}
|
|
function createSequenceNode(id, sequence, throwAt, doneAt) {
|
|
return createSerovalNode(35, id, throwAt, void 0, void 0, void 0, void 0, sequence, void 0, void 0, void 0, doneAt);
|
|
}
|
|
//#endregion
|
|
//#region src/core/special-reference.ts
|
|
const ITERATOR = {};
|
|
const ASYNC_ITERATOR = {};
|
|
/**
|
|
* Placeholder references
|
|
*/
|
|
const SPECIAL_REFS = {
|
|
[0]: {},
|
|
[1]: {},
|
|
[2]: {},
|
|
[3]: {},
|
|
[4]: {},
|
|
[5]: {}
|
|
};
|
|
const SPECIAL_REF_STRING = {
|
|
[0]: "[]",
|
|
[1]: require_shared.SERIALIZED_PROMISE_CONSTRUCTOR,
|
|
[2]: require_shared.SERIALIZED_PROMISE_SUCCESS,
|
|
[3]: require_shared.SERIALIZED_PROMISE_FAILURE,
|
|
[4]: require_shared.SERIALIZED_STREAM_CONSTRUCTOR,
|
|
[5]: require_shared.SERIALIZED_ARRAY_BUFFER_CONSTRUCTOR
|
|
};
|
|
//#endregion
|
|
//#region src/core/utils/promise-to-result.ts
|
|
async function promiseToResult(current) {
|
|
try {
|
|
return [1, await current];
|
|
} catch (e) {
|
|
return [0, e];
|
|
}
|
|
}
|
|
//#endregion
|
|
//#region src/core/context/parser.ts
|
|
function createBaseParserContext(options) {
|
|
return {
|
|
plugins: options.plugins,
|
|
plainObjectPlugins: require_shared.getPlainObjectPlugins(options.plugins),
|
|
marked: /* @__PURE__ */ new Set(),
|
|
features: 127 ^ (options.disabledFeatures ?? 0),
|
|
refs: options.refs ?? /* @__PURE__ */ new Map(),
|
|
depthLimit: options.depthLimit || 1e3,
|
|
compactArrayBufferViews: options.compactArrayBufferViews ?? false
|
|
};
|
|
}
|
|
/**
|
|
* Returns the plugins to test for an object. Plain objects, whose prototype is
|
|
* `Object.prototype`, skip the plugins that set `skipPlainObjects`. Checking
|
|
* the prototype as well as `constructor` keeps instances whose prototype has
|
|
* no own `constructor` property from skipping those plugins.
|
|
*/
|
|
function getObjectPlugins(ctx, current, currentClass) {
|
|
return currentClass === Object && Object.getPrototypeOf(current) === Object.prototype ? ctx.plainObjectPlugins : ctx.plugins;
|
|
}
|
|
/**
|
|
* Ensures that the value (based on an identifier) has been visited by the parser.
|
|
* @param ctx
|
|
* @param id
|
|
*/
|
|
function markParserRef(ctx, id) {
|
|
ctx.marked.add(id);
|
|
}
|
|
/**
|
|
* Creates an identifier for a value
|
|
* @param ctx
|
|
* @param current
|
|
*/
|
|
function createIndexForValue(ctx, current) {
|
|
const id = ctx.refs.size;
|
|
ctx.refs.set(current, id);
|
|
return id;
|
|
}
|
|
/**
|
|
* Removes the ids registered from `size` on. A parse that fails emits none of
|
|
* them, so a caller-owned map must not keep them for later calls.
|
|
*/
|
|
function rollbackRefs(refs, size) {
|
|
if (refs.size > size) {
|
|
for (const [value, id] of refs) if (id >= size) refs.delete(value);
|
|
}
|
|
}
|
|
function getNodeForIndexedValue(ctx, current) {
|
|
const registeredId = ctx.refs.get(current);
|
|
if (registeredId != null) {
|
|
markParserRef(ctx, registeredId);
|
|
return createIndexedValueNode(registeredId);
|
|
}
|
|
return createIndexForValue(ctx, current);
|
|
}
|
|
function getReferenceNode(ctx, current) {
|
|
const indexed = getNodeForIndexedValue(ctx, current);
|
|
if (typeof indexed !== "number") return indexed;
|
|
const referenceId = require_shared.getReferenceID(current);
|
|
return referenceId === void 0 ? indexed : createReferenceNode(indexed, referenceId);
|
|
}
|
|
/**
|
|
* Parsing methods
|
|
*/
|
|
function parseWellKnownSymbol(ctx, current) {
|
|
const ref = getReferenceNode(ctx, current);
|
|
if (typeof ref !== "number") return ref;
|
|
if (current in require_shared.INV_SYMBOL_REF) return createWKSymbolNode(ref, current);
|
|
throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
function parseSpecialReference(ctx, ref) {
|
|
const result = getNodeForIndexedValue(ctx, SPECIAL_REFS[ref]);
|
|
if (typeof result !== "number") return result;
|
|
return createSerovalNode(26, result, ref);
|
|
}
|
|
function parseIteratorFactory(ctx) {
|
|
const result = getNodeForIndexedValue(ctx, ITERATOR);
|
|
if (typeof result !== "number") return result;
|
|
return createSerovalNode(27, result, void 0, void 0, void 0, void 0, void 0, void 0, parseWellKnownSymbol(ctx, require_shared.SYM_ITERATOR));
|
|
}
|
|
function parseAsyncIteratorFactory(ctx) {
|
|
const result = getNodeForIndexedValue(ctx, ASYNC_ITERATOR);
|
|
if (typeof result !== "number") return result;
|
|
return createSerovalNode(29, result, void 0, void 0, void 0, void 0, void 0, [parseSpecialReference(ctx, 1), parseWellKnownSymbol(ctx, require_shared.SYM_ASYNC_ITERATOR)]);
|
|
}
|
|
function getTemporalType(currentClass) {
|
|
switch (currentClass) {
|
|
case Temporal.Instant: return 0;
|
|
case Temporal.Duration: return 1;
|
|
case Temporal.PlainDate: return 2;
|
|
case Temporal.PlainDateTime: return 3;
|
|
case Temporal.PlainMonthDay: return 4;
|
|
case Temporal.PlainTime: return 5;
|
|
case Temporal.PlainYearMonth: return 6;
|
|
case Temporal.ZonedDateTime: return 7;
|
|
default: return;
|
|
}
|
|
}
|
|
function getObjectClass(current) {
|
|
const proto = Object.getPrototypeOf(current);
|
|
if (proto === null) return void 0;
|
|
if (proto === Object.prototype) return Object;
|
|
const currentClass = proto.constructor;
|
|
if (typeof currentClass !== "function" || currentClass === Object || currentClass.prototype === proto) return currentClass;
|
|
return proto;
|
|
}
|
|
/**
|
|
* Decides how an object is parsed. Shared by the sync, stream and async
|
|
* parsers so the class checks and feature gates live in one place.
|
|
*/
|
|
function getObjectKind(current, currentClass, features) {
|
|
switch (currentClass) {
|
|
case Object: return 1;
|
|
case void 0: return 2;
|
|
case Date: return 3;
|
|
case Error:
|
|
case EvalError:
|
|
case RangeError:
|
|
case ReferenceError:
|
|
case SyntaxError:
|
|
case TypeError:
|
|
case URIError: return 4;
|
|
case Number:
|
|
case Boolean:
|
|
case String:
|
|
case BigInt: return 6;
|
|
case ArrayBuffer: return 7;
|
|
case Int8Array:
|
|
case Int16Array:
|
|
case Int32Array:
|
|
case Uint8Array:
|
|
case Uint16Array:
|
|
case Uint32Array:
|
|
case Uint8ClampedArray:
|
|
case Float32Array:
|
|
case Float64Array: return 8;
|
|
case DataView: return 10;
|
|
case Map: return 11;
|
|
case Set: return 12;
|
|
}
|
|
if (currentClass === Promise || current instanceof Promise) return 13;
|
|
if (features & 32 && currentClass === RegExp) return 14;
|
|
if (features & 16 && (currentClass === BigInt64Array || currentClass === BigUint64Array)) return 9;
|
|
if (features & 1 && typeof AggregateError !== "undefined" && (currentClass === AggregateError || current instanceof AggregateError)) return 5;
|
|
if (features & 64 && typeof Temporal !== "undefined" && getTemporalType(currentClass) != null) return 15;
|
|
if (ArrayBuffer.isView(current)) {
|
|
const name = require_typed_array.getTypedArrayName(current);
|
|
if (name === void 0) return 10;
|
|
if (name !== "BigInt64Array" && name !== "BigUint64Array") return 8;
|
|
if (features & 16) return 9;
|
|
}
|
|
if (current instanceof Error) return 4;
|
|
if (require_shared.SYM_ITERATOR in current || require_shared.SYM_ASYNC_ITERATOR in current) return 16;
|
|
return 0;
|
|
}
|
|
function createObjectNode(id, current, empty, record) {
|
|
return createSerovalNode(empty ? 11 : 10, id, void 0, void 0, void 0, record, void 0, void 0, void 0, void 0, getObjectFlag(current));
|
|
}
|
|
/**
|
|
* Gets the sentinel node for a Map. Call it before parsing the entries, so
|
|
* that an outer Map declares the sentinel before any Map inside it uses it.
|
|
*/
|
|
function parseMapSentinel(ctx) {
|
|
return parseSpecialReference(ctx, 0);
|
|
}
|
|
function createMapNode(id, sentinel, k, v) {
|
|
return createSerovalNode(8, id, void 0, void 0, void 0, void 0, {
|
|
k,
|
|
v
|
|
}, void 0, sentinel);
|
|
}
|
|
function createPromiseConstructorNode(ctx, id, resolver) {
|
|
return createSerovalNode(22, id, resolver, void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 1));
|
|
}
|
|
function getArrayBufferView(ctx, view) {
|
|
if (!ctx.compactArrayBufferViews) return view;
|
|
return {
|
|
buffer: new Uint8Array(view.buffer, view.byteOffset, view.byteLength).slice().buffer,
|
|
byteOffset: 0,
|
|
byteLength: view.byteLength,
|
|
length: view.length
|
|
};
|
|
}
|
|
function createArrayBufferNode(ctx, id, current) {
|
|
return createSerovalNode(19, id, require_typed_array.encodeArrayBuffer(current), void 0, void 0, void 0, void 0, void 0, parseSpecialReference(ctx, 5));
|
|
}
|
|
//#endregion
|
|
//#region src/core/context/async-parser.ts
|
|
function createAsyncParserContext(options) {
|
|
return { base: createBaseParserContext(options) };
|
|
}
|
|
function ignoreRejection() {}
|
|
/**
|
|
* A promise gets its rejection handler only when the walk reaches it. While an
|
|
* earlier sibling is still pending, a later one that rejects would be an
|
|
* unhandled rejection, which ends a Node.js process by default. Mark the
|
|
* promises among the children as handled before walking them.
|
|
*/
|
|
function handleChildPromises(values) {
|
|
for (const value of values) if (value instanceof Promise) Promise.prototype.then.call(value, void 0, ignoreRejection);
|
|
}
|
|
async function parseItems$1(ctx, depth, current) {
|
|
assertArrayHoles(current);
|
|
const len = current.length;
|
|
const nodes = new Array(len);
|
|
const values = new Array(len);
|
|
const present = new Array(len);
|
|
for (let i = 0; i < len; i++) {
|
|
present[i] = i in current;
|
|
if (present[i]) values[i] = current[i];
|
|
}
|
|
handleChildPromises(values);
|
|
for (let i = 0, node; i < len; i++) if (present[i]) {
|
|
node = parseValueAsync(ctx, depth, values[i]);
|
|
nodes[i] = node instanceof Promise ? await node : node;
|
|
} else nodes[i] = 0;
|
|
return nodes;
|
|
}
|
|
async function parseArray$1(ctx, depth, id, current) {
|
|
return createArrayNode(id, current, await parseItems$1(ctx, depth, current));
|
|
}
|
|
async function parseProperties$1(ctx, depth, properties) {
|
|
const keys = Object.keys(properties);
|
|
const len = keys.length;
|
|
const keyNodes = new Array(len);
|
|
const valueNodes = new Array(len);
|
|
const values = new Array(len);
|
|
for (let i = 0; i < len; i++) values[i] = properties[keys[i]];
|
|
handleChildPromises(values);
|
|
for (let i = 0, node; i < len; i++) {
|
|
keyNodes[i] = require_shared.serializeString(keys[i]);
|
|
node = parseValueAsync(ctx, depth, values[i]);
|
|
valueNodes[i] = node instanceof Promise ? await node : node;
|
|
}
|
|
if (require_shared.SYM_ITERATOR in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_ITERATOR));
|
|
valueNodes.push(createIteratorFactoryInstanceNode(28, parseIteratorFactory(ctx.base), await parseAsync(ctx, depth, require_shared.createSequenceFromIterable(properties))));
|
|
}
|
|
if (require_shared.SYM_ASYNC_ITERATOR in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_ASYNC_ITERATOR));
|
|
valueNodes.push(createIteratorFactoryInstanceNode(30, parseAsyncIteratorFactory(ctx.base), await parseAsyncIterable$1(ctx, depth, properties)));
|
|
}
|
|
if (require_shared.SYM_TO_STRING_TAG in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_TO_STRING_TAG));
|
|
valueNodes.push(createStringNode(properties[require_shared.SYM_TO_STRING_TAG]));
|
|
}
|
|
if (require_shared.SYM_IS_CONCAT_SPREADABLE in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_IS_CONCAT_SPREADABLE));
|
|
valueNodes.push(properties[require_shared.SYM_IS_CONCAT_SPREADABLE] ? TRUE_NODE : FALSE_NODE);
|
|
}
|
|
return {
|
|
k: keyNodes,
|
|
v: valueNodes
|
|
};
|
|
}
|
|
async function parsePlainObject$1(ctx, depth, id, current, empty) {
|
|
return createObjectNode(id, current, empty, await parseProperties$1(ctx, depth, current));
|
|
}
|
|
async function parseBoxed$1(ctx, depth, id, current) {
|
|
const node = parseValueAsync(ctx, depth, current.valueOf());
|
|
return createBoxedNode(id, node instanceof Promise ? await node : node);
|
|
}
|
|
async function parseView$1(ctx, depth, type, id, current) {
|
|
const view = getArrayBufferView(ctx.base, require_typed_array.readArrayBufferView(current, type === 20));
|
|
return createViewNode(type, id, current, view, await parseAsync(ctx, depth, view.buffer));
|
|
}
|
|
async function parseError$1(ctx, depth, type, id, current) {
|
|
const options = getErrorOptions(current, ctx.base.features);
|
|
return createErrorNode(type, id, current, options ? await parseProperties$1(ctx, depth, options) : void 0);
|
|
}
|
|
async function parseMap$1(ctx, depth, id, current) {
|
|
const sentinel = parseMapSentinel(ctx.base);
|
|
const entries = [...current.entries()];
|
|
const keyNodes = new Array(entries.length);
|
|
const valueNodes = new Array(entries.length);
|
|
const keys = new Array(entries.length);
|
|
const values = new Array(entries.length);
|
|
for (let i = 0; i < entries.length; i++) {
|
|
keys[i] = entries[i][0];
|
|
values[i] = entries[i][1];
|
|
}
|
|
handleChildPromises(keys);
|
|
handleChildPromises(values);
|
|
for (let i = 0, node; i < entries.length; i++) {
|
|
node = parseValueAsync(ctx, depth, keys[i]);
|
|
keyNodes[i] = node instanceof Promise ? await node : node;
|
|
node = parseValueAsync(ctx, depth, values[i]);
|
|
valueNodes[i] = node instanceof Promise ? await node : node;
|
|
}
|
|
return createMapNode(id, sentinel, keyNodes, valueNodes);
|
|
}
|
|
async function parseSet$1(ctx, depth, id, current) {
|
|
const values = [...current.keys()];
|
|
const items = new Array(values.length);
|
|
handleChildPromises(values);
|
|
for (let i = 0, node; i < values.length; i++) {
|
|
node = parseValueAsync(ctx, depth, values[i]);
|
|
items[i] = node instanceof Promise ? await node : node;
|
|
}
|
|
return createSetNode(id, items);
|
|
}
|
|
async function parsePlugin$1(ctx, depth, id, current, currentPlugins) {
|
|
if (currentPlugins) for (let i = 0, len = currentPlugins.length; i < len; i++) {
|
|
const plugin = currentPlugins[i];
|
|
if (plugin.parse.async && plugin.test(current)) return createPluginNode(id, plugin.tag, await plugin.parse.async(current, {
|
|
parse: (value) => parseAsync(ctx, depth, value),
|
|
parseStreamSource(source) {
|
|
if (depth >= ctx.base.depthLimit) throw new require_shared.SerovalDepthLimitError(ctx.base.depthLimit);
|
|
return parseAsyncIterable$1(ctx, depth + 1, source);
|
|
}
|
|
}, { id }));
|
|
}
|
|
}
|
|
async function parsePromise$1(ctx, depth, id, current) {
|
|
const [status, result] = await promiseToResult(current);
|
|
return createSerovalNode(12, id, status, void 0, void 0, void 0, void 0, void 0, await parseAsync(ctx, depth, result));
|
|
}
|
|
function parseStreamHandle(depth, id, current, resolve, reject) {
|
|
const sequence = [];
|
|
let unparsed = 0;
|
|
let ended = false;
|
|
let settled = false;
|
|
const fail = (error) => {
|
|
if (!settled) {
|
|
settled = true;
|
|
try {
|
|
cleanup(error);
|
|
} catch {}
|
|
reject(error);
|
|
}
|
|
};
|
|
const handle = (type) => (value, accept) => {
|
|
if (settled) return;
|
|
markParserRef(this.base, id);
|
|
const slot = sequence.push(void 0) - 1;
|
|
unparsed++;
|
|
if (type !== 32) ended = true;
|
|
parseAsync(this, depth, value).then((data) => {
|
|
if (!settled) {
|
|
sequence[slot] = createStreamEventNode(type, id, data);
|
|
unparsed--;
|
|
accept?.();
|
|
if (ended && unparsed === 0) {
|
|
settled = true;
|
|
try {
|
|
cleanup();
|
|
} catch (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
resolve(sequence);
|
|
}
|
|
}
|
|
}, fail);
|
|
};
|
|
const listener = {
|
|
next: handle(32),
|
|
throw: handle(33),
|
|
return: handle(34)
|
|
};
|
|
const cleanup = require_shared.isLiveStream(current) ? current.pump(listener) : current.on(listener);
|
|
}
|
|
async function parseStream$1(ctx, depth, id, current) {
|
|
return createStreamConstructorNode(id, parseSpecialReference(ctx.base, 4), await new Promise(parseStreamHandle.bind(ctx, depth, id, current)));
|
|
}
|
|
/**
|
|
* Collects an async iterable into a replay node, one value at a time.
|
|
*/
|
|
async function parseAsyncIterable$1(ctx, depth, current) {
|
|
const id = createIndexForValue(ctx.base, {});
|
|
const reference = parseSpecialReference(ctx.base, 4);
|
|
const iterator = current[require_shared.SYM_ASYNC_ITERATOR]();
|
|
const sequence = [];
|
|
for (;;) {
|
|
let result;
|
|
markParserRef(ctx.base, id);
|
|
try {
|
|
result = await iterator.next();
|
|
} catch (error) {
|
|
sequence.push(createStreamEventNode(33, id, await parseAsync(ctx, depth, error)));
|
|
break;
|
|
}
|
|
let parsed;
|
|
try {
|
|
parsed = await parseAsync(ctx, depth, result.value);
|
|
} catch (error) {
|
|
Promise.resolve().then(() => iterator.return?.()).catch(() => {});
|
|
throw error;
|
|
}
|
|
sequence.push(createStreamEventNode(result.done ? 34 : 32, id, parsed));
|
|
if (result.done) break;
|
|
}
|
|
return createStreamConstructorNode(id, reference, sequence);
|
|
}
|
|
async function parseSequence$1(ctx, depth, id, current) {
|
|
const len = current.v.length;
|
|
const nodes = new Array(len);
|
|
for (let i = 0, node; i < len; i++) {
|
|
node = parseValueAsync(ctx, depth, current.v[i]);
|
|
nodes[i] = node instanceof Promise ? await node : node;
|
|
}
|
|
return createSequenceNode(id, nodes, current.t, current.d);
|
|
}
|
|
async function parseObjectAsync(ctx, depth, id, current) {
|
|
if (Array.isArray(current)) return parseArray$1(ctx, depth, id, current);
|
|
if (require_shared.isStream(current)) return parseStream$1(ctx, depth, id, current);
|
|
if (require_shared.isSequence(current)) return parseSequence$1(ctx, depth, id, current);
|
|
const currentClass = getObjectClass(current);
|
|
if (currentClass === require_shared.OpaqueReference) return parseAsync(ctx, depth, current.replacement);
|
|
const parsed = await parsePlugin$1(ctx, depth, id, current, getObjectPlugins(ctx.base, current, currentClass));
|
|
if (parsed) return parsed;
|
|
if (currentClass === Object) return parsePlainObject$1(ctx, depth, id, current, false);
|
|
switch (getObjectKind(current, currentClass, ctx.base.features)) {
|
|
case 2: return parsePlainObject$1(ctx, depth, id, current, true);
|
|
case 3: return createDateNode(id, current);
|
|
case 4: return parseError$1(ctx, depth, 13, id, current);
|
|
case 5: return parseError$1(ctx, depth, 14, id, current);
|
|
case 6: return parseBoxed$1(ctx, depth, id, current);
|
|
case 7: return createArrayBufferNode(ctx.base, id, current);
|
|
case 8: return parseView$1(ctx, depth, 15, id, current);
|
|
case 9: return parseView$1(ctx, depth, 16, id, current);
|
|
case 10: return parseView$1(ctx, depth, 20, id, current);
|
|
case 11: return parseMap$1(ctx, depth, id, current);
|
|
case 12: return parseSet$1(ctx, depth, id, current);
|
|
case 13: return parsePromise$1(ctx, depth, id, current);
|
|
case 14: return createRegExpNode(id, current);
|
|
case 15: return createTemporalNode(id, getTemporalType(currentClass), currentClass, current);
|
|
case 16: return parsePlainObject$1(ctx, depth, id, current, !!currentClass);
|
|
default: throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
}
|
|
async function parseFunctionAsync(ctx, depth, current) {
|
|
const ref = getReferenceNode(ctx.base, current);
|
|
if (typeof ref !== "number") return ref;
|
|
const plugin = await parsePlugin$1(ctx, depth, ref, current, ctx.base.plugins);
|
|
if (plugin) return plugin;
|
|
throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
/**
|
|
* Parses primitives and already-seen values synchronously; only objects and
|
|
* functions come back as a Promise. Errors are thrown synchronously, which
|
|
* an async caller turns into a rejection as before.
|
|
*/
|
|
function parseValueAsync(ctx, depth, current) {
|
|
if (depth >= ctx.base.depthLimit) throw new require_shared.SerovalDepthLimitError(ctx.base.depthLimit);
|
|
switch (typeof current) {
|
|
case "boolean": return current ? TRUE_NODE : FALSE_NODE;
|
|
case "undefined": return UNDEFINED_NODE;
|
|
case "string": return createStringNode(current);
|
|
case "number": return createNumberNode(current);
|
|
case "bigint": return createBigIntNode(current);
|
|
case "object":
|
|
if (current) {
|
|
const ref = getReferenceNode(ctx.base, current);
|
|
return typeof ref === "number" ? parseObjectAsync(ctx, depth + 1, ref, current) : ref;
|
|
}
|
|
return NULL_NODE;
|
|
case "symbol": return parseWellKnownSymbol(ctx.base, current);
|
|
case "function": return parseFunctionAsync(ctx, depth, current);
|
|
default: throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
}
|
|
async function parseAsync(ctx, depth, current) {
|
|
return await parseValueAsync(ctx, depth, current);
|
|
}
|
|
async function parseTopAsync(ctx, current) {
|
|
const refs = ctx.base.refs;
|
|
const size = refs.size;
|
|
try {
|
|
return await parseAsync(ctx, 0, current);
|
|
} catch (error) {
|
|
rollbackRefs(refs, size);
|
|
throw error instanceof require_shared.SerovalParserError ? error : new require_shared.SerovalParserError(error);
|
|
}
|
|
}
|
|
//#endregion
|
|
//#region src/core/function-string.ts
|
|
const RETURN = () => T;
|
|
const IS_MODERN = /* @__PURE__ */ (/* @__PURE__ */ RETURN.toString()).includes("=>");
|
|
function createFunction(parameters, body) {
|
|
if (IS_MODERN) return (parameters.length === 1 ? parameters[0] : "(" + parameters.join(",") + ")") + "=>" + (body.startsWith("{") ? "(" + body + ")" : body);
|
|
return "function(" + parameters.join(",") + "){return " + body + "}";
|
|
}
|
|
function createEffectfulFunction(parameters, body) {
|
|
if (IS_MODERN) return (parameters.length === 1 ? parameters[0] : "(" + parameters.join(",") + ")") + "=>{" + body + "}";
|
|
return "function(" + parameters.join(",") + "){" + body + "}";
|
|
}
|
|
//#endregion
|
|
//#region src/core/utils/get-identifier.ts
|
|
const REF_START_CHARS = "hjkmoquxzABCDEFGHIJKLNPQRTUVWXYZ$_";
|
|
const REF_START_CHARS_LEN = 34;
|
|
const REF_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_";
|
|
const REF_CHARS_LEN = 64;
|
|
function getIdentifier(index) {
|
|
let mod = index % REF_START_CHARS_LEN;
|
|
let ref = REF_START_CHARS[mod];
|
|
index = (index - mod) / REF_START_CHARS_LEN;
|
|
while (index > 0) {
|
|
mod = index % REF_CHARS_LEN;
|
|
ref += REF_CHARS[mod];
|
|
index = (index - mod) / REF_CHARS_LEN;
|
|
}
|
|
return ref;
|
|
}
|
|
//#endregion
|
|
//#region src/core/utils/is-valid-identifier.ts
|
|
function isValidIdentifier(name) {
|
|
const len = name.length;
|
|
for (let i = 0; i < len; i++) {
|
|
const code = name.charCodeAt(i);
|
|
if (!(code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 && i > 0 || code === 95 || code === 36)) return false;
|
|
}
|
|
return len > 0;
|
|
}
|
|
//#endregion
|
|
//#region src/core/context/serializer.ts
|
|
function getAssignmentExpression(assignment) {
|
|
switch (assignment.t) {
|
|
case 0: return assignment.s + "=" + assignment.v;
|
|
case 2: return assignment.s + ".set(" + assignment.k + "," + assignment.v + ")";
|
|
case 1: return assignment.s + ".add(" + assignment.v + ")";
|
|
case 3: return assignment.s + ".delete(" + assignment.k + ")";
|
|
case 4: return "Object.defineProperty(" + assignment.s + ",\"__proto__\",{value:" + assignment.k + ",configurable:!0,enumerable:!0,writable:!0})";
|
|
}
|
|
}
|
|
const MAX_ASSIGNMENT_CHAIN_LENGTH = 100;
|
|
function resolveAssignments(assignments, chainValues = true) {
|
|
if (!assignments.length) return void 0;
|
|
let previous = assignments[0];
|
|
let expression = getAssignmentExpression(previous);
|
|
let result = "";
|
|
let chainLength = 1;
|
|
for (let index = 1, length = assignments.length; index < length; index++) {
|
|
const assignment = assignments[index];
|
|
if (chainLength === MAX_ASSIGNMENT_CHAIN_LENGTH) {
|
|
result += expression + ",";
|
|
expression = getAssignmentExpression(assignment);
|
|
chainLength = 0;
|
|
} else if (chainValues && assignment.t === 0 && previous.t === 0 && assignment.v === previous.v) expression = assignment.s + "=" + expression;
|
|
else if (assignment.t === 2 && previous.t === 2 && assignment.s === previous.s) expression += ".set(" + assignment.k + "," + assignment.v + ")";
|
|
else if (assignment.t === 1 && previous.t === 1 && assignment.s === previous.s) expression += ".add(" + assignment.v + ")";
|
|
else if (assignment.t === 3 && previous.t === 2 && assignment.s === previous.s) expression += ".delete(" + assignment.k + ")";
|
|
else {
|
|
result += expression + ",";
|
|
expression = getAssignmentExpression(assignment);
|
|
chainLength = 0;
|
|
}
|
|
chainLength++;
|
|
previous = assignment;
|
|
}
|
|
return result + expression + ",";
|
|
}
|
|
const NULL_CONSTRUCTOR = "Object.create(null)";
|
|
const SET_CONSTRUCTOR = "new Set";
|
|
const MAP_CONSTRUCTOR = "new Map";
|
|
const PROMISE_RESOLVE = "Promise.resolve";
|
|
const PROMISE_REJECT = "Promise.reject";
|
|
const OBJECT_FLAG_CONSTRUCTOR = {
|
|
[3]: "Object.freeze",
|
|
[2]: "Object.seal",
|
|
[1]: "Object.preventExtensions",
|
|
[0]: void 0
|
|
};
|
|
function createBaseSerializerContext(mode, options) {
|
|
return {
|
|
mode,
|
|
plugins: options.plugins,
|
|
features: options.features,
|
|
marked: Array.isArray(options.markedRefs) ? new Set(options.markedRefs) : options.markedRefs,
|
|
stack: [],
|
|
flags: [],
|
|
assignments: [],
|
|
assignAll: mode === 2 && !options.assignSharedRefsOnly
|
|
};
|
|
}
|
|
function createVanillaSerializerState() {
|
|
return {
|
|
valid: /* @__PURE__ */ new Map(),
|
|
vars: [],
|
|
sentinels: /* @__PURE__ */ new Set()
|
|
};
|
|
}
|
|
function createVanillaSerializerContext(options) {
|
|
return {
|
|
mode: 1,
|
|
base: createBaseSerializerContext(1, options),
|
|
state: createVanillaSerializerState()
|
|
};
|
|
}
|
|
function createCrossSerializerContext(options) {
|
|
return {
|
|
mode: 2,
|
|
base: createBaseSerializerContext(2, options),
|
|
state: options
|
|
};
|
|
}
|
|
/**
|
|
* Creates the reference param (identifier) from the given reference ID
|
|
* Calling this function means the value has been referenced somewhere
|
|
*/
|
|
function getVanillaRefParam(state, index) {
|
|
/**
|
|
* Creates a new reference ID from a given reference ID
|
|
* This new reference ID means that the reference itself
|
|
* has been referenced at least once, and is used to generate
|
|
* the variables
|
|
*/
|
|
let actualIndex = state.valid.get(index);
|
|
if (actualIndex == null) {
|
|
actualIndex = state.valid.size;
|
|
state.valid.set(index, actualIndex);
|
|
}
|
|
let identifier = state.vars[actualIndex];
|
|
if (identifier == null) {
|
|
identifier = getIdentifier(actualIndex);
|
|
state.vars[actualIndex] = identifier;
|
|
}
|
|
return identifier;
|
|
}
|
|
function getCrossRefParam(id) {
|
|
return "$R[" + id + "]";
|
|
}
|
|
/**
|
|
* Converts the ID of a reference into a identifier string
|
|
* that is used to refer to the object instance in the
|
|
* generated script.
|
|
*/
|
|
function getRefParam(ctx, id) {
|
|
return ctx.mode === 1 ? getVanillaRefParam(ctx.state, id) : getCrossRefParam(id);
|
|
}
|
|
function markSerializerRef(ctx, id) {
|
|
ctx.marked.add(id);
|
|
}
|
|
function isSerializerRefMarked(ctx, id) {
|
|
return ctx.marked.has(id);
|
|
}
|
|
function pushObjectFlag(ctx, flag, id) {
|
|
if (flag !== 0) {
|
|
markSerializerRef(ctx.base, id);
|
|
ctx.base.flags.push({
|
|
type: flag,
|
|
value: getRefParam(ctx, id)
|
|
});
|
|
}
|
|
}
|
|
function resolveFlags(ctx) {
|
|
let result = "";
|
|
for (let i = 0, current = ctx.flags, len = current.length; i < len; i++) {
|
|
const flag = current[i];
|
|
result += OBJECT_FLAG_CONSTRUCTOR[flag.type] + "(" + flag.value + "),";
|
|
}
|
|
return result;
|
|
}
|
|
function resolvePatches(ctx) {
|
|
const assignments = resolveAssignments(ctx.assignments);
|
|
const flags = resolveFlags(ctx);
|
|
if (assignments) {
|
|
if (flags) return assignments + flags;
|
|
return assignments;
|
|
}
|
|
return flags;
|
|
}
|
|
/**
|
|
* Generates the inlined assignment for the reference
|
|
* This is different from the assignments array as this one
|
|
* signifies creation rather than mutation
|
|
*/
|
|
function createAssignment(ctx, source, value) {
|
|
ctx.assignments.push({
|
|
t: 0,
|
|
s: source,
|
|
k: void 0,
|
|
v: value
|
|
});
|
|
}
|
|
function createAddAssignment(ctx, ref, value) {
|
|
ctx.base.assignments.push({
|
|
t: 1,
|
|
s: getRefParam(ctx, ref),
|
|
k: void 0,
|
|
v: value
|
|
});
|
|
}
|
|
function createSetAssignment(ctx, ref, key, value) {
|
|
ctx.base.assignments.push({
|
|
t: 2,
|
|
s: getRefParam(ctx, ref),
|
|
k: key,
|
|
v: value
|
|
});
|
|
}
|
|
function createDeleteAssignment(ctx, ref, key) {
|
|
ctx.base.assignments.push({
|
|
t: 3,
|
|
s: getRefParam(ctx, ref),
|
|
k: key,
|
|
v: void 0
|
|
});
|
|
}
|
|
function createArrayAssign(ctx, ref, index, value) {
|
|
createAssignment(ctx.base, getRefParam(ctx, ref) + "[" + index + "]", value);
|
|
}
|
|
function createObjectAssign(ctx, ref, key, value) {
|
|
if (key === "__proto__") {
|
|
ctx.base.assignments.push({
|
|
t: 4,
|
|
s: getRefParam(ctx, ref),
|
|
k: value,
|
|
v: void 0
|
|
});
|
|
return;
|
|
}
|
|
createAssignment(ctx.base, getRefParam(ctx, ref) + "." + key, value);
|
|
}
|
|
function createSequenceAssign(ctx, ref, index, value) {
|
|
createAssignment(ctx.base, getRefParam(ctx, ref) + ".v[" + index + "]", value);
|
|
}
|
|
function getKeyKind(key) {
|
|
const first = key.charCodeAt(0);
|
|
if (first >= 48 && first <= 57) {
|
|
const check = Number(key);
|
|
return check >= 0 && check.toString() === key ? 2 : 0;
|
|
}
|
|
if (key === "Infinity") return 2;
|
|
return isValidIdentifier(key) ? 1 : 0;
|
|
}
|
|
/**
|
|
* Checks if the value is in the stack. Stack here is a reference
|
|
* structure to know if a object is to be accessed in a TDZ.
|
|
*/
|
|
function isIndexedValueInStack(ctx, node) {
|
|
return node.t === 4 && ctx.stack.includes(node.i);
|
|
}
|
|
/**
|
|
* Checks if the node, or any node inside it, is read from more than one
|
|
* place. Such a node must not be created inside a deferred assignment,
|
|
* because the places that read it may run before the assignment does.
|
|
*/
|
|
function hasMarkedNode(ctx, node) {
|
|
if (Array.isArray(node)) {
|
|
for (let i = 0, len = node.length; i < len; i++) if (hasMarkedNode(ctx, node[i])) return true;
|
|
return false;
|
|
}
|
|
if (node == null || typeof node !== "object") return false;
|
|
const current = node;
|
|
if (current.t !== 4 && typeof current.i === "number" && isSerializerRefMarked(ctx, current.i)) return true;
|
|
for (const key in current) if (hasMarkedNode(ctx, current[key])) return true;
|
|
return false;
|
|
}
|
|
/**
|
|
* Produces an assignment expression. `id` generates a reference
|
|
* parameter (through `getRefParam`) and has the option to
|
|
* return the reference parameter directly or assign a value to
|
|
* it.
|
|
*/
|
|
function assignIndexedValue(ctx, index, value) {
|
|
if (!ctx.base.assignAll && !isSerializerRefMarked(ctx.base, index)) return value;
|
|
/**
|
|
* In cross-reference, we have to assume that
|
|
* every reference are going to be referenced
|
|
* in the future, and so we need to store
|
|
* all of it into the reference array.
|
|
*
|
|
* otherwise in vanilla, we only do this if it
|
|
* is actually referenced. `assignSharedRefsOnly` gives cross-reference
|
|
* output the vanilla behavior.
|
|
*/
|
|
return getRefParam(ctx, index) + "=" + value;
|
|
}
|
|
function serializeReference(node) {
|
|
return require_shared.REFERENCES_KEY + ".get(\"" + node.s + "\")";
|
|
}
|
|
function serializeArrayItem(ctx, id, item, index) {
|
|
if (item) {
|
|
if (isIndexedValueInStack(ctx.base, item)) {
|
|
markSerializerRef(ctx.base, id);
|
|
createArrayAssign(ctx, id, index, getRefParam(ctx, item.i));
|
|
return "";
|
|
}
|
|
return serialize$1(ctx, item);
|
|
}
|
|
return "";
|
|
}
|
|
function serializeArray(ctx, node) {
|
|
const id = node.i;
|
|
const list = node.a;
|
|
const len = list.length;
|
|
if (len > 0) {
|
|
ctx.base.stack.push(id);
|
|
let values = serializeArrayItem(ctx, id, list[0], 0);
|
|
let isHoley = values === "";
|
|
for (let i = 1, item; i < len; i++) {
|
|
item = serializeArrayItem(ctx, id, list[i], i);
|
|
values += "," + item;
|
|
isHoley = item === "";
|
|
}
|
|
ctx.base.stack.pop();
|
|
pushObjectFlag(ctx, node.o, node.i);
|
|
return "[" + values + (isHoley ? ",]" : "]");
|
|
}
|
|
return "[]";
|
|
}
|
|
function serializeProperty(ctx, source, key, val) {
|
|
if (typeof key === "string") {
|
|
const kind = getKeyKind(key);
|
|
if (isIndexedValueInStack(ctx.base, val)) {
|
|
const refParam = getRefParam(ctx, val.i);
|
|
markSerializerRef(ctx.base, source.i);
|
|
createKeyAssign(ctx, source.i, kind, key, refParam);
|
|
return "";
|
|
}
|
|
if (key !== "__proto__") return (kind === 0 ? "\"" + key + "\"" : key) + ":" + serialize$1(ctx, val);
|
|
return "[\"" + key + "\"]:" + serialize$1(ctx, val);
|
|
}
|
|
return "[" + serialize$1(ctx, key) + "]:" + serialize$1(ctx, val);
|
|
}
|
|
function serializeProperties(ctx, source, record) {
|
|
const keys = record.k;
|
|
const len = keys.length;
|
|
if (len > 0) {
|
|
const values = record.v;
|
|
ctx.base.stack.push(source.i);
|
|
let result = serializeProperty(ctx, source, keys[0], values[0]);
|
|
for (let i = 1, item; i < len; i++) {
|
|
item = serializeProperty(ctx, source, keys[i], values[i]);
|
|
result += (item && result && ",") + item;
|
|
}
|
|
ctx.base.stack.pop();
|
|
return "{" + result + "}";
|
|
}
|
|
return "{}";
|
|
}
|
|
function serializeObject(ctx, node) {
|
|
pushObjectFlag(ctx, node.o, node.i);
|
|
return serializeProperties(ctx, node, node.p);
|
|
}
|
|
function serializeWithObjectAssign(ctx, source, value, serialized) {
|
|
const fields = serializeProperties(ctx, source, value);
|
|
if (fields !== "{}") return "Object.assign(" + serialized + "," + fields + ")";
|
|
return serialized;
|
|
}
|
|
function serializeStringKeyAssignment(ctx, source, mainAssignments, key, value) {
|
|
const base = ctx.base;
|
|
const serialized = serialize$1(ctx, value);
|
|
const parentAssignment = base.assignments;
|
|
if (!isIndexedValueInStack(base, value)) base.assignments = mainAssignments;
|
|
createKeyAssign(ctx, source.i, getKeyKind(key), key, serialized);
|
|
base.assignments = parentAssignment;
|
|
}
|
|
function createKeyAssign(ctx, ref, kind, key, value) {
|
|
if (kind === 1) createObjectAssign(ctx, ref, key, value);
|
|
else createArrayAssign(ctx, ref, kind === 2 ? key : "\"" + key + "\"", value);
|
|
}
|
|
function serializeAssignment(ctx, source, mainAssignments, key, value) {
|
|
if (typeof key === "string") serializeStringKeyAssignment(ctx, source, mainAssignments, key, value);
|
|
else {
|
|
const base = ctx.base;
|
|
const parent = base.stack;
|
|
base.stack = [];
|
|
const serialized = serialize$1(ctx, value);
|
|
base.stack = parent;
|
|
const parentAssignment = base.assignments;
|
|
base.assignments = mainAssignments;
|
|
createArrayAssign(ctx, source.i, serialize$1(ctx, key), serialized);
|
|
base.assignments = parentAssignment;
|
|
}
|
|
}
|
|
function serializeAssignments(ctx, source, node) {
|
|
const keys = node.k;
|
|
const len = keys.length;
|
|
if (len > 0) {
|
|
const mainAssignments = [];
|
|
const values = node.v;
|
|
ctx.base.stack.push(source.i);
|
|
for (let i = 0; i < len; i++) serializeAssignment(ctx, source, mainAssignments, keys[i], values[i]);
|
|
ctx.base.stack.pop();
|
|
return resolveAssignments(mainAssignments, false);
|
|
}
|
|
}
|
|
function serializeDictionary(ctx, node, init) {
|
|
if (node.p) {
|
|
const base = ctx.base;
|
|
if (base.features & 8 && !node.p.k.includes("__proto__")) init = serializeWithObjectAssign(ctx, node, node.p, init);
|
|
else {
|
|
markSerializerRef(base, node.i);
|
|
const assignments = serializeAssignments(ctx, node, node.p);
|
|
if (assignments) return "(" + assignIndexedValue(ctx, node.i, init) + "," + assignments + getRefParam(ctx, node.i) + ")";
|
|
}
|
|
}
|
|
return init;
|
|
}
|
|
function serializeNullConstructor(ctx, node) {
|
|
pushObjectFlag(ctx, node.o, node.i);
|
|
return serializeDictionary(ctx, node, NULL_CONSTRUCTOR);
|
|
}
|
|
function serializeDate(node) {
|
|
return "new Date(\"" + require_shared.serializeString(node.s) + "\")";
|
|
}
|
|
function serializeTemporal(ctx, node) {
|
|
if (ctx.base.features & 64) return "Temporal." + require_shared.TEMPORAL_TYPE_NAME[node.c] + ".from(\"" + require_shared.serializeString(node.s) + "\")";
|
|
throw new require_shared.SerovalUnsupportedNodeError(node);
|
|
}
|
|
function serializeRegExp(ctx, node) {
|
|
if (ctx.base.features & 32) {
|
|
if (node.c.includes("\\x3C")) return "new RegExp(\"" + node.c + "\",\"" + node.m + "\")";
|
|
return "/" + require_shared.deserializeString(node.c) + "/" + node.m;
|
|
}
|
|
throw new require_shared.SerovalUnsupportedNodeError(node);
|
|
}
|
|
function serializeSetItem(ctx, id, item) {
|
|
const base = ctx.base;
|
|
if (isIndexedValueInStack(base, item)) {
|
|
markSerializerRef(base, id);
|
|
createAddAssignment(ctx, id, getRefParam(ctx, item.i));
|
|
return "";
|
|
}
|
|
return serialize$1(ctx, item);
|
|
}
|
|
function serializeSet(ctx, node) {
|
|
let serialized = SET_CONSTRUCTOR;
|
|
const items = node.a;
|
|
const size = items.length;
|
|
const id = node.i;
|
|
if (size > 0) {
|
|
ctx.base.stack.push(id);
|
|
let result = serializeSetItem(ctx, id, items[0]);
|
|
for (let i = 1, item; i < size; i++) {
|
|
item = serializeSetItem(ctx, id, items[i]);
|
|
result += (item && result && ",") + item;
|
|
}
|
|
ctx.base.stack.pop();
|
|
if (result) serialized += "([" + result + "])";
|
|
}
|
|
return serialized;
|
|
}
|
|
function serializeMapEntry(ctx, id, key, val, getSentinel) {
|
|
const base = ctx.base;
|
|
if (isIndexedValueInStack(base, key)) {
|
|
const keyRef = getRefParam(ctx, key.i);
|
|
markSerializerRef(base, id);
|
|
if (isIndexedValueInStack(base, val)) {
|
|
createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
|
|
return "";
|
|
}
|
|
if (val.t !== 4 && val.i != null && hasMarkedNode(base, val)) {
|
|
markSerializerRef(base, val.i);
|
|
const sentinel = getSentinel();
|
|
const serialized = "(" + serialize$1(ctx, val) + ",[" + sentinel + "," + sentinel + "])";
|
|
createSetAssignment(ctx, id, keyRef, getRefParam(ctx, val.i));
|
|
createDeleteAssignment(ctx, id, sentinel);
|
|
return serialized;
|
|
}
|
|
const parent = base.stack;
|
|
base.stack = [];
|
|
createSetAssignment(ctx, id, keyRef, serialize$1(ctx, val));
|
|
base.stack = parent;
|
|
return "";
|
|
}
|
|
if (isIndexedValueInStack(base, val)) {
|
|
const valueRef = getRefParam(ctx, val.i);
|
|
markSerializerRef(base, id);
|
|
if (key.t !== 4 && key.i != null && hasMarkedNode(base, key)) {
|
|
markSerializerRef(base, key.i);
|
|
const sentinel = getSentinel();
|
|
const serialized = "(" + serialize$1(ctx, key) + ",[" + sentinel + "," + sentinel + "])";
|
|
createSetAssignment(ctx, id, getRefParam(ctx, key.i), valueRef);
|
|
createDeleteAssignment(ctx, id, sentinel);
|
|
return serialized;
|
|
}
|
|
const parent = base.stack;
|
|
base.stack = [];
|
|
createSetAssignment(ctx, id, serialize$1(ctx, key), valueRef);
|
|
base.stack = parent;
|
|
return "";
|
|
}
|
|
return "[" + serialize$1(ctx, key) + "," + serialize$1(ctx, val) + "]";
|
|
}
|
|
function serializeMap(ctx, node) {
|
|
let serialized = MAP_CONSTRUCTOR;
|
|
const keys = node.e.k;
|
|
const size = keys.length;
|
|
const id = node.i;
|
|
const sentinel = node.f;
|
|
let sentinelRef;
|
|
const getSentinel = () => {
|
|
if (sentinelRef == null) {
|
|
sentinelRef = getRefParam(ctx, sentinel.i);
|
|
if (ctx.mode === 1) ctx.state.sentinels.add(ctx.state.valid.get(sentinel.i));
|
|
}
|
|
return sentinelRef;
|
|
};
|
|
const base = ctx.base;
|
|
if (size > 0) {
|
|
const vals = node.e.v;
|
|
base.stack.push(id);
|
|
let result = serializeMapEntry(ctx, id, keys[0], vals[0], getSentinel);
|
|
for (let i = 1, item; i < size; i++) {
|
|
item = serializeMapEntry(ctx, id, keys[i], vals[i], getSentinel);
|
|
result += (item && result && ",") + item;
|
|
}
|
|
base.stack.pop();
|
|
if (result) serialized += "([" + result + "])";
|
|
}
|
|
if (ctx.mode === 2 && sentinel.t === 26) {
|
|
markSerializerRef(base, sentinel.i);
|
|
serialized = "(" + serialize$1(ctx, sentinel) + "," + serialized + ")";
|
|
}
|
|
return serialized;
|
|
}
|
|
function serializeArrayBuffer(ctx, node) {
|
|
return getConstructor(ctx, node.f) + "(\"" + node.s + "\")";
|
|
}
|
|
function serializeView(ctx, node) {
|
|
return "new " + (node.t === 20 ? "DataView" : node.c) + "(" + serialize$1(ctx, node.f) + "," + +node.b + "," + +node.l + ")";
|
|
}
|
|
function serializeAggregateError(ctx, node) {
|
|
const id = node.i;
|
|
ctx.base.stack.push(id);
|
|
const serialized = serializeDictionary(ctx, node, "new AggregateError([],\"" + node.m + "\")");
|
|
ctx.base.stack.pop();
|
|
return serialized;
|
|
}
|
|
function serializeError(ctx, node) {
|
|
return serializeDictionary(ctx, node, "new " + require_shared.ERROR_CONSTRUCTOR_STRING[node.s] + "(\"" + node.m + "\")");
|
|
}
|
|
function serializePromise(ctx, node) {
|
|
let serialized;
|
|
const fulfilled = node.f;
|
|
const id = node.i;
|
|
const promiseConstructor = node.s ? PROMISE_RESOLVE : PROMISE_REJECT;
|
|
const base = ctx.base;
|
|
if (isIndexedValueInStack(base, fulfilled)) {
|
|
const ref = getRefParam(ctx, fulfilled.i);
|
|
serialized = promiseConstructor + (node.s ? "().then(" + createFunction([], ref) + ")" : "().catch(" + createEffectfulFunction([], "throw " + ref) + ")");
|
|
} else {
|
|
base.stack.push(id);
|
|
const result = serialize$1(ctx, fulfilled);
|
|
base.stack.pop();
|
|
serialized = promiseConstructor + "(" + result + ")";
|
|
}
|
|
return serialized;
|
|
}
|
|
function serializeBoxed(ctx, node) {
|
|
return "Object(" + serialize$1(ctx, node.f) + ")";
|
|
}
|
|
function getConstructor(ctx, node) {
|
|
const current = serialize$1(ctx, node);
|
|
return node.t === 4 ? current : "(" + current + ")";
|
|
}
|
|
function serializePromiseConstructor(ctx, node) {
|
|
if (ctx.mode === 1) throw new require_shared.SerovalUnsupportedNodeError(node);
|
|
return "(" + assignIndexedValue(ctx, node.s, getConstructor(ctx, node.f) + "()") + ").p";
|
|
}
|
|
function serializePromiseResult(ctx, node) {
|
|
if (ctx.mode === 1) throw new require_shared.SerovalUnsupportedNodeError(node);
|
|
return getConstructor(ctx, node.a[0]) + "(" + getRefParam(ctx, node.i) + "," + serialize$1(ctx, node.a[1]) + ")";
|
|
}
|
|
function serializePlugin(ctx, node) {
|
|
const currentPlugins = ctx.base.plugins;
|
|
if (currentPlugins) for (let i = 0, len = currentPlugins.length; i < len; i++) {
|
|
const plugin = currentPlugins[i];
|
|
if (plugin.tag === node.c) return plugin.serialize(node.s, { serialize: (current) => serialize$1(ctx, current) }, { id: node.i });
|
|
}
|
|
throw new require_shared.SerovalMissingPluginError(node.c);
|
|
}
|
|
function serializeIteratorFactory(ctx, node) {
|
|
let result = "";
|
|
let initialized = false;
|
|
if (node.f.t !== 4) {
|
|
markSerializerRef(ctx.base, node.f.i);
|
|
result = "(" + serialize$1(ctx, node.f) + ",";
|
|
initialized = true;
|
|
}
|
|
result += assignIndexedValue(ctx, node.i, "(" + require_shared.SERIALIZED_ITERATOR_CONSTRUCTOR + ")(" + getRefParam(ctx, node.f.i) + ")");
|
|
if (initialized) result += ")";
|
|
return result;
|
|
}
|
|
function serializeIteratorFactoryInstance(ctx, node) {
|
|
return getConstructor(ctx, node.a[0]) + "(" + serialize$1(ctx, node.a[1]) + ")";
|
|
}
|
|
function serializeAsyncIteratorFactory(ctx, node) {
|
|
const promise = node.a[0];
|
|
const symbol = node.a[1];
|
|
const base = ctx.base;
|
|
let result = "";
|
|
if (promise.t !== 4) {
|
|
markSerializerRef(base, promise.i);
|
|
result += "(" + serialize$1(ctx, promise);
|
|
}
|
|
if (symbol.t !== 4) {
|
|
markSerializerRef(base, symbol.i);
|
|
result += (result ? "," : "(") + serialize$1(ctx, symbol);
|
|
}
|
|
if (result) result += ",";
|
|
const iterator = assignIndexedValue(ctx, node.i, "(" + require_shared.SERIALIZED_ASYNC_ITERATOR_CONSTRUCTOR + ")(" + getRefParam(ctx, symbol.i) + "," + getRefParam(ctx, promise.i) + ")");
|
|
if (result) return result + iterator + ")";
|
|
return iterator;
|
|
}
|
|
function serializeStreamConstructor(ctx, node) {
|
|
const result = assignIndexedValue(ctx, node.i, getConstructor(ctx, node.f) + (node.l === 1 ? "(1)" : "()"));
|
|
const len = node.a.length;
|
|
if (len) {
|
|
let values = serialize$1(ctx, node.a[0]);
|
|
for (let i = 1; i < len; i++) values += "," + serialize$1(ctx, node.a[i]);
|
|
return "(" + result + "," + values + "," + getRefParam(ctx, node.i) + ")";
|
|
}
|
|
return result;
|
|
}
|
|
function serializeStreamCall(ctx, node, method) {
|
|
return getRefParam(ctx, node.i) + "." + method + "(" + serialize$1(ctx, node.f) + ")";
|
|
}
|
|
function serializeSequenceItem(ctx, id, index, item) {
|
|
const base = ctx.base;
|
|
if (isIndexedValueInStack(base, item)) {
|
|
markSerializerRef(base, id);
|
|
createSequenceAssign(ctx, id, index, getRefParam(ctx, item.i));
|
|
return "";
|
|
}
|
|
return serialize$1(ctx, item);
|
|
}
|
|
function serializeSequence(ctx, node) {
|
|
const items = node.a;
|
|
const size = items.length;
|
|
const id = node.i;
|
|
if (size > 0) {
|
|
ctx.base.stack.push(id);
|
|
let result = serializeSequenceItem(ctx, id, 0, items[0]);
|
|
for (let i = 1, item; i < size; i++) {
|
|
item = serializeSequenceItem(ctx, id, i, items[i]);
|
|
result += (item && result && ",") + item;
|
|
}
|
|
ctx.base.stack.pop();
|
|
if (result) return "{__SEROVAL_SEQUENCE__:!0,v:[" + result + "],t:" + +node.s + ",d:" + +node.l + "}";
|
|
}
|
|
return "{__SEROVAL_SEQUENCE__:!0,v:[],t:-1,d:0}";
|
|
}
|
|
function serializeAssignable(ctx, node) {
|
|
switch (node.t) {
|
|
case 17: return require_shared.SYMBOL_STRING[node.s];
|
|
case 18: return serializeReference(node);
|
|
case 9: return serializeArray(ctx, node);
|
|
case 10: return serializeObject(ctx, node);
|
|
case 11: return serializeNullConstructor(ctx, node);
|
|
case 5: return serializeDate(node);
|
|
case 6: return serializeRegExp(ctx, node);
|
|
case 7: return serializeSet(ctx, node);
|
|
case 8: return serializeMap(ctx, node);
|
|
case 19: return serializeArrayBuffer(ctx, node);
|
|
case 16:
|
|
case 15:
|
|
case 20: return serializeView(ctx, node);
|
|
case 14: return serializeAggregateError(ctx, node);
|
|
case 13: return serializeError(ctx, node);
|
|
case 12: return serializePromise(ctx, node);
|
|
case 21: return serializeBoxed(ctx, node);
|
|
case 22: return serializePromiseConstructor(ctx, node);
|
|
case 25: return serializePlugin(ctx, node);
|
|
case 26: return SPECIAL_REF_STRING[node.s];
|
|
case 35: return serializeSequence(ctx, node);
|
|
case 36: return serializeTemporal(ctx, node);
|
|
default: throw new require_shared.SerovalUnsupportedNodeError(node);
|
|
}
|
|
}
|
|
function serialize$1(ctx, node) {
|
|
switch (node.t) {
|
|
case 2: return require_shared.CONSTANT_STRING[node.s];
|
|
case 0: return "" + node.s;
|
|
case 1: return "\"" + node.s + "\"";
|
|
case 3: return node.s + "n";
|
|
case 4: return getRefParam(ctx, node.i);
|
|
case 23:
|
|
case 24: return serializePromiseResult(ctx, node);
|
|
case 27: return serializeIteratorFactory(ctx, node);
|
|
case 28:
|
|
case 30: return serializeIteratorFactoryInstance(ctx, node);
|
|
case 29: return serializeAsyncIteratorFactory(ctx, node);
|
|
case 31: return serializeStreamConstructor(ctx, node);
|
|
case 32: return serializeStreamCall(ctx, node, "next");
|
|
case 33: return serializeStreamCall(ctx, node, "throw");
|
|
case 34: return serializeStreamCall(ctx, node, "return");
|
|
default: return assignIndexedValue(ctx, node.i, serializeAssignable(ctx, node));
|
|
}
|
|
}
|
|
function serializeVanilla(ctx, tree) {
|
|
const result = serialize$1(ctx, tree);
|
|
if (tree.i != null && ctx.state.vars.length) {
|
|
const patches = resolvePatches(ctx.base);
|
|
let body = result;
|
|
if (patches) {
|
|
const index = getRefParam(ctx, tree.i);
|
|
body = result + "," + patches + index;
|
|
if (!result.startsWith(index + "=")) body = index + "=" + body;
|
|
body = "(" + body + ")";
|
|
}
|
|
const sentinels = ctx.state.sentinels;
|
|
if (sentinels.size) {
|
|
let declarations = "";
|
|
for (const index of sentinels) declarations += ctx.state.vars[index] + "=[],";
|
|
body = "(" + declarations + body + ")";
|
|
}
|
|
return "(" + createFunction(ctx.state.vars, body) + ")()";
|
|
}
|
|
if (tree.t === 10) return "(" + result + ")";
|
|
return result;
|
|
}
|
|
function serializeCross(ctx, tree) {
|
|
const result = serialize$1(ctx, tree);
|
|
const id = tree.i;
|
|
if (id == null) return result;
|
|
const patches = resolvePatches(ctx.base);
|
|
const ref = getRefParam(ctx, id);
|
|
let body = result;
|
|
if (patches) {
|
|
if (!ctx.base.assignAll && !result.startsWith(ref + "=")) body = ref + "=" + result;
|
|
body = "(" + body + "," + patches + ref + ")";
|
|
}
|
|
const scopeId = ctx.state.scopeId;
|
|
if (scopeId == null) {
|
|
if (tree.t === 10 && !patches) return "(" + body + ")";
|
|
return body;
|
|
}
|
|
return "(" + createFunction(["$R"], body) + ")($R[\"" + require_shared.serializeString(scopeId) + "\"])";
|
|
}
|
|
function wrapSerializationError(error) {
|
|
return error instanceof require_shared.SerovalSerializationError ? error : new require_shared.SerovalSerializationError(error);
|
|
}
|
|
function serializeTopVanilla(ctx, tree) {
|
|
try {
|
|
return serializeVanilla(ctx, tree);
|
|
} catch (error) {
|
|
throw wrapSerializationError(error);
|
|
}
|
|
}
|
|
function serializeTopCross(ctx, tree) {
|
|
try {
|
|
return serializeCross(ctx, tree);
|
|
} catch (error) {
|
|
throw wrapSerializationError(error);
|
|
}
|
|
}
|
|
//#endregion
|
|
//#region src/core/context/sync-parser.ts
|
|
function createSyncParserContext(options) {
|
|
return {
|
|
type: 1,
|
|
base: createBaseParserContext(options)
|
|
};
|
|
}
|
|
function parseItems(ctx, depth, current) {
|
|
assertArrayHoles(current);
|
|
const len = current.length;
|
|
const nodes = new Array(len);
|
|
for (let i = 0; i < len; i++) if (i in current) nodes[i] = parseSOS(ctx, depth, current[i]);
|
|
else nodes[i] = 0;
|
|
return nodes;
|
|
}
|
|
function parseArray(ctx, depth, id, current) {
|
|
return createArrayNode(id, current, parseItems(ctx, depth, current));
|
|
}
|
|
function parseProperties(ctx, depth, properties) {
|
|
const keys = Object.keys(properties);
|
|
const len = keys.length;
|
|
const keyNodes = new Array(len);
|
|
const valueNodes = new Array(len);
|
|
for (let i = 0, key; i < len; i++) {
|
|
key = keys[i];
|
|
keyNodes[i] = require_shared.serializeString(key);
|
|
valueNodes[i] = parseSOS(ctx, depth, properties[key]);
|
|
}
|
|
if (require_shared.SYM_ITERATOR in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_ITERATOR));
|
|
valueNodes.push(createIteratorFactoryInstanceNode(28, parseIteratorFactory(ctx.base), parseSOS(ctx, depth, require_shared.createSequenceFromIterable(properties))));
|
|
}
|
|
if (require_shared.SYM_ASYNC_ITERATOR in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_ASYNC_ITERATOR));
|
|
valueNodes.push(createIteratorFactoryInstanceNode(30, parseAsyncIteratorFactory(ctx.base), parseAsyncIterable(ctx, depth, properties)));
|
|
}
|
|
if (require_shared.SYM_TO_STRING_TAG in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_TO_STRING_TAG));
|
|
valueNodes.push(createStringNode(properties[require_shared.SYM_TO_STRING_TAG]));
|
|
}
|
|
if (require_shared.SYM_IS_CONCAT_SPREADABLE in properties) {
|
|
keyNodes.push(parseWellKnownSymbol(ctx.base, require_shared.SYM_IS_CONCAT_SPREADABLE));
|
|
valueNodes.push(properties[require_shared.SYM_IS_CONCAT_SPREADABLE] ? TRUE_NODE : FALSE_NODE);
|
|
}
|
|
return {
|
|
k: keyNodes,
|
|
v: valueNodes
|
|
};
|
|
}
|
|
function parseAsyncIterable(ctx, depth, current) {
|
|
if (ctx.type !== 2) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
const id = createIndexForValue(ctx.base, {});
|
|
const result = createStreamConstructorNode(id, parseSpecialReference(ctx.base, 4), []);
|
|
ctx.state.iterable(ctx, depth, id, current);
|
|
return result;
|
|
}
|
|
function parsePlainObject(ctx, depth, id, current, empty) {
|
|
return createObjectNode(id, current, empty, parseProperties(ctx, depth, current));
|
|
}
|
|
function parseBoxed(ctx, depth, id, current) {
|
|
return createBoxedNode(id, parseSOS(ctx, depth, current.valueOf()));
|
|
}
|
|
function parseView(ctx, depth, type, id, current) {
|
|
const view = getArrayBufferView(ctx.base, require_typed_array.readArrayBufferView(current, type === 20));
|
|
return createViewNode(type, id, current, view, parseSOS(ctx, depth, view.buffer));
|
|
}
|
|
function parseError(ctx, depth, type, id, current) {
|
|
const options = getErrorOptions(current, ctx.base.features);
|
|
return createErrorNode(type, id, current, options ? parseProperties(ctx, depth, options) : void 0);
|
|
}
|
|
function parseMap(ctx, depth, id, current) {
|
|
const sentinel = parseMapSentinel(ctx.base);
|
|
const keyNodes = new Array(current.size);
|
|
const valueNodes = new Array(current.size);
|
|
let i = 0;
|
|
for (const [key, value] of current.entries()) {
|
|
keyNodes[i] = parseSOS(ctx, depth, key);
|
|
valueNodes[i] = parseSOS(ctx, depth, value);
|
|
i++;
|
|
}
|
|
return createMapNode(id, sentinel, keyNodes, valueNodes);
|
|
}
|
|
function parseSet(ctx, depth, id, current) {
|
|
const items = new Array(current.size);
|
|
let i = 0;
|
|
for (const item of current.keys()) items[i++] = parseSOS(ctx, depth, item);
|
|
return createSetNode(id, items);
|
|
}
|
|
/**
|
|
* Sends the events of a stream that has ended, so the copy gets all of them.
|
|
* A stream that is still open throws, because the other sync modes cannot
|
|
* send its later events.
|
|
*/
|
|
function parseEndedStream(ctx, depth, id, current) {
|
|
const events = current.endedEvents();
|
|
if (!events) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
const values = events.values;
|
|
const last = values.length - 1;
|
|
const sequence = new Array(values.length);
|
|
if (last >= 0) markParserRef(ctx.base, id);
|
|
for (let i = 0; i <= last; i++) sequence[i] = createStreamEventNode(i < last ? 32 : events.success ? 34 : 33, id, parseSOS(ctx, depth, values[i]));
|
|
return sequence;
|
|
}
|
|
function parseStream(ctx, depth, id, current) {
|
|
const reference = parseSpecialReference(ctx.base, 4);
|
|
if (ctx.type === 2) {
|
|
const result = createStreamConstructorNode(id, reference, []);
|
|
ctx.state.stream(ctx, depth, id, current);
|
|
return result;
|
|
}
|
|
return createStreamConstructorNode(id, reference, parseEndedStream(ctx, depth, id, current));
|
|
}
|
|
function parseLiveStream(ctx, depth, id, current) {
|
|
if (ctx.type !== 2) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
const result = createStreamConstructorNode(id, parseSpecialReference(ctx.base, 4), [], 1);
|
|
ctx.state.live(ctx, depth, id, current);
|
|
return result;
|
|
}
|
|
function parsePromise(ctx, depth, id, current) {
|
|
if (ctx.type !== 2) throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
const resolver = createIndexForValue(ctx.base, {});
|
|
ctx.state.promise(ctx, resolver, depth, current);
|
|
return createPromiseConstructorNode(ctx.base, id, resolver);
|
|
}
|
|
function parsePluginSync(ctx, depth, id, current, currentPlugins) {
|
|
for (let i = 0, len = currentPlugins.length; i < len; i++) {
|
|
const plugin = currentPlugins[i];
|
|
if (plugin.parse.sync && plugin.test(current)) return createPluginNode(id, plugin.tag, plugin.parse.sync(current, { parse: (value) => parseSOS(ctx, depth, value) }, { id }));
|
|
}
|
|
}
|
|
function parsePlugin(ctx, depth, id, current, currentPlugins) {
|
|
if (currentPlugins) return ctx.type === 1 ? parsePluginSync(ctx, depth, id, current, currentPlugins) : ctx.state.plugin(ctx, depth, id, current, currentPlugins);
|
|
}
|
|
function parseSequence(ctx, depth, id, current) {
|
|
const len = current.v.length;
|
|
const nodes = new Array(len);
|
|
for (let i = 0; i < len; i++) nodes[i] = parseSOS(ctx, depth, current.v[i]);
|
|
return createSequenceNode(id, nodes, current.t, current.d);
|
|
}
|
|
function parseObjectPhase2(ctx, depth, id, current, currentClass) {
|
|
if (currentClass === Object) return parsePlainObject(ctx, depth, id, current, false);
|
|
switch (getObjectKind(current, currentClass, ctx.base.features)) {
|
|
case 2: return parsePlainObject(ctx, depth, id, current, true);
|
|
case 3: return createDateNode(id, current);
|
|
case 4: return parseError(ctx, depth, 13, id, current);
|
|
case 5: return parseError(ctx, depth, 14, id, current);
|
|
case 6: return parseBoxed(ctx, depth, id, current);
|
|
case 7: return createArrayBufferNode(ctx.base, id, current);
|
|
case 8: return parseView(ctx, depth, 15, id, current);
|
|
case 9: return parseView(ctx, depth, 16, id, current);
|
|
case 10: return parseView(ctx, depth, 20, id, current);
|
|
case 11: return parseMap(ctx, depth, id, current);
|
|
case 12: return parseSet(ctx, depth, id, current);
|
|
case 13: return parsePromise(ctx, depth, id, current);
|
|
case 14: return createRegExpNode(id, current);
|
|
case 15: return createTemporalNode(id, getTemporalType(currentClass), currentClass, current);
|
|
case 16: return parsePlainObject(ctx, depth, id, current, !!currentClass);
|
|
default: throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
}
|
|
function parseObject(ctx, depth, id, current) {
|
|
if (Array.isArray(current)) return parseArray(ctx, depth, id, current);
|
|
if (require_shared.isLiveStream(current)) return parseLiveStream(ctx, depth, id, current);
|
|
if (current instanceof require_shared.Stream) return parseStream(ctx, depth, id, current);
|
|
if (require_shared.isSequence(current)) return parseSequence(ctx, depth, id, current);
|
|
const currentClass = getObjectClass(current);
|
|
if (currentClass === require_shared.OpaqueReference) return parseSOS(ctx, depth, current.replacement);
|
|
const parsed = parsePlugin(ctx, depth, id, current, getObjectPlugins(ctx.base, current, currentClass));
|
|
if (parsed) return parsed;
|
|
return parseObjectPhase2(ctx, depth, id, current, currentClass);
|
|
}
|
|
function parseFunction(ctx, depth, current) {
|
|
const ref = getReferenceNode(ctx.base, current);
|
|
if (typeof ref !== "number") return ref;
|
|
const plugin = parsePlugin(ctx, depth, ref, current, ctx.base.plugins);
|
|
if (plugin) return plugin;
|
|
throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
function parseSOS(ctx, depth, current) {
|
|
if (depth >= ctx.base.depthLimit) throw new require_shared.SerovalDepthLimitError(ctx.base.depthLimit);
|
|
switch (typeof current) {
|
|
case "boolean": return current ? TRUE_NODE : FALSE_NODE;
|
|
case "undefined": return UNDEFINED_NODE;
|
|
case "string": return createStringNode(current);
|
|
case "number": return createNumberNode(current);
|
|
case "bigint": return createBigIntNode(current);
|
|
case "object":
|
|
if (current) {
|
|
const ref = getReferenceNode(ctx.base, current);
|
|
return typeof ref === "number" ? parseObject(ctx, depth + 1, ref, current) : ref;
|
|
}
|
|
return NULL_NODE;
|
|
case "symbol": return parseWellKnownSymbol(ctx.base, current);
|
|
case "function": return parseFunction(ctx, depth, current);
|
|
default: throw new require_shared.SerovalUnsupportedTypeError(current);
|
|
}
|
|
}
|
|
function parseTop(ctx, current) {
|
|
const refs = ctx.base.refs;
|
|
const size = refs.size;
|
|
try {
|
|
return parseSOS(ctx, 0, current);
|
|
} catch (error) {
|
|
rollbackRefs(refs, size);
|
|
throw error instanceof require_shared.SerovalParserError ? error : new require_shared.SerovalParserError(error);
|
|
}
|
|
}
|
|
//#endregion
|
|
//#region src/core/context/stream-parser.ts
|
|
/**
|
|
* Streaming parse mode. Everything here is reached through the function
|
|
* fields of the stream parser state, which only `createStreamParserContext`
|
|
* sets, so synchronous consumers (`serialize`, `toJSON`) never bundle it.
|
|
*/
|
|
function createStreamParsePluginContext(ctx, depth) {
|
|
const state = ctx.state;
|
|
function parse(current) {
|
|
state.parsing++;
|
|
try {
|
|
return parseSOS(ctx, depth, current);
|
|
} finally {
|
|
state.parsing--;
|
|
if (!state.alive) releaseParserValues(ctx);
|
|
}
|
|
}
|
|
return {
|
|
parse,
|
|
parseStreamSource(current) {
|
|
state.parsing++;
|
|
try {
|
|
if (depth >= ctx.base.depthLimit) throw new require_shared.SerovalDepthLimitError(ctx.base.depthLimit);
|
|
const id = createIndexForValue(ctx.base, {});
|
|
const node = createStreamConstructorNode(id, parseSpecialReference(ctx.base, 4), [], 1);
|
|
iterateAsync(ctx, depth + 1, id, current, true);
|
|
return node;
|
|
} finally {
|
|
state.parsing--;
|
|
if (!state.alive) releaseParserValues(ctx);
|
|
}
|
|
},
|
|
parseWithError(current) {
|
|
try {
|
|
return parse(current);
|
|
} catch (error) {
|
|
stopStreamParse(ctx, 1, error);
|
|
return;
|
|
}
|
|
},
|
|
isAlive: () => state.alive,
|
|
pushPendingState: () => {
|
|
pushPendingState(ctx);
|
|
},
|
|
popPendingState: () => {
|
|
popPendingState(ctx);
|
|
},
|
|
onParse(node) {
|
|
if (state.alive) {
|
|
state.queue.push({
|
|
node,
|
|
initial: false,
|
|
accept: void 0,
|
|
refs: ctx.base.refs.size
|
|
});
|
|
drainOutput(ctx);
|
|
}
|
|
},
|
|
onError(error) {
|
|
stopStreamParse(ctx, 1, error);
|
|
},
|
|
addCleanup(callback) {
|
|
addCleanup(ctx, callback);
|
|
}
|
|
};
|
|
}
|
|
function getStreamParsePluginContext(ctx, depth) {
|
|
const contexts = ctx.state.pluginContexts;
|
|
return contexts[depth] ?? (contexts[depth] = createStreamParsePluginContext(ctx, depth));
|
|
}
|
|
function addCleanup(ctx, callback) {
|
|
if (ctx.state.alive) ctx.state.cleanups.push(callback);
|
|
else callback();
|
|
}
|
|
function parsePluginStream(ctx, depth, id, current, currentPlugins) {
|
|
for (let i = 0, len = currentPlugins.length; i < len; i++) {
|
|
const plugin = currentPlugins[i];
|
|
if (plugin.parse.stream && plugin.test(current)) return createPluginNode(id, plugin.tag, plugin.parse.stream(current, getStreamParsePluginContext(ctx, depth), { id }));
|
|
}
|
|
}
|
|
/**
|
|
* One listener shape serves both `Stream.on` (no accept) and
|
|
* `LiveStream.pump` (accept tied to record acceptance). Handlers return the
|
|
* parse error so the pump can stop the source.
|
|
*/
|
|
function streamListener(ctx, depth, id, subscription) {
|
|
const handle = (type, terminal) => (value, accept) => {
|
|
let failure = void 0;
|
|
if (ctx.state.alive) try {
|
|
failure = parseEvent(ctx, depth, value, createStreamEventNode.bind(null, type, id), accept);
|
|
} catch (error) {
|
|
if (!subscription?.active) throw error;
|
|
subscription.failure ?? (subscription.failure = { value: error });
|
|
failure = error;
|
|
}
|
|
if (terminal && !accept) popPendingState(ctx);
|
|
return failure;
|
|
};
|
|
return {
|
|
next: handle(32, false),
|
|
throw: handle(33, true),
|
|
return: handle(34, true),
|
|
done() {
|
|
popPendingState(ctx);
|
|
},
|
|
error(reason) {
|
|
if (ctx.state.alive) stopStreamParse(ctx, 1, reason);
|
|
}
|
|
};
|
|
}
|
|
function subscribeStream(ctx, depth, id, current) {
|
|
pushPendingState(ctx);
|
|
const subscription = {
|
|
active: true,
|
|
failure: void 0
|
|
};
|
|
let cleanup;
|
|
try {
|
|
cleanup = current.on(streamListener(ctx, depth, id, subscription));
|
|
} finally {
|
|
subscription.active = false;
|
|
}
|
|
try {
|
|
addCleanup(ctx, cleanup);
|
|
} catch (error) {
|
|
subscription.failure ?? (subscription.failure = { value: error });
|
|
}
|
|
const failure = subscription.failure;
|
|
subscription.failure = void 0;
|
|
if (failure) throw failure.value;
|
|
}
|
|
function consumeLiveStream(ctx, depth, id, current) {
|
|
const cancel = current.pump(streamListener(ctx, depth, id));
|
|
pushPendingState(ctx);
|
|
addCleanup(ctx, () => {
|
|
cancel(ctx.state.reason);
|
|
});
|
|
}
|
|
function wrapPromiseResult(ctx, type, id, node) {
|
|
return createSerovalNode(type, id, void 0, void 0, void 0, void 0, void 0, [parseSpecialReference(ctx.base, type === 23 ? 2 : 3), node]);
|
|
}
|
|
function handlePromise(type, id, depth, data) {
|
|
if (this.state.alive) parseEvent(this, depth, data, wrapPromiseResult.bind(null, this, type, id), void 0);
|
|
popPendingState(this);
|
|
}
|
|
function watchPromise(ctx, resolver, depth, current) {
|
|
pushPendingState(ctx);
|
|
let settled = false;
|
|
current.then((data) => {
|
|
if (!settled) {
|
|
settled = true;
|
|
handlePromise.call(ctx, 23, resolver, depth, data);
|
|
}
|
|
}, (data) => {
|
|
if (!settled) {
|
|
settled = true;
|
|
handlePromise.call(ctx, 24, resolver, depth, data);
|
|
}
|
|
});
|
|
}
|
|
function returnIterator(iterator) {
|
|
Promise.resolve().then(() => iterator.return?.()).catch(() => {});
|
|
}
|
|
/**
|
|
* Drives an async iterator directly. The next value is pulled only after the
|
|
* record for the previous one has been accepted, and cancellation returns
|
|
* the iterator once.
|
|
*/
|
|
function iterateAsync(ctx, depth, id, current, forwardReason) {
|
|
const iterator = current[require_shared.SYM_ASYNC_ITERATOR]();
|
|
const listener = streamListener(ctx, depth, id);
|
|
let active = true;
|
|
function stop() {
|
|
if (active) {
|
|
active = false;
|
|
if (forwardReason) try {
|
|
Promise.resolve(iterator.return?.(ctx.state.reason)).catch(() => {});
|
|
} catch {}
|
|
else returnIterator(iterator);
|
|
}
|
|
}
|
|
function pull() {
|
|
if (active) iterator.next().then((result) => {
|
|
if (active) {
|
|
if (result.done) {
|
|
active = false;
|
|
listener.return(result.value, void 0);
|
|
} else if (listener.next(result.value, pull) !== void 0) {
|
|
stop();
|
|
listener.done();
|
|
}
|
|
}
|
|
}, (error) => {
|
|
if (active) {
|
|
active = false;
|
|
listener.throw(error, void 0);
|
|
}
|
|
});
|
|
}
|
|
pushPendingState(ctx);
|
|
addCleanup(ctx, stop);
|
|
pull();
|
|
}
|
|
function createStreamParserState(options) {
|
|
return {
|
|
alive: true,
|
|
pending: 0,
|
|
parsing: 0,
|
|
queue: [],
|
|
head: 0,
|
|
writing: false,
|
|
inFlight: void 0,
|
|
reason: void 0,
|
|
starting: false,
|
|
onParse: options.onParse,
|
|
onError: options.onError,
|
|
onDone: options.onDone,
|
|
cleanups: [],
|
|
pluginContexts: [],
|
|
stream: subscribeStream,
|
|
live: consumeLiveStream,
|
|
promise: watchPromise,
|
|
iterable: iterateAsync,
|
|
plugin: parsePluginStream
|
|
};
|
|
}
|
|
function createStreamParserContext(options) {
|
|
return {
|
|
type: 2,
|
|
base: createBaseParserContext(options),
|
|
state: createStreamParserState(options)
|
|
};
|
|
}
|
|
function pushPendingState(ctx) {
|
|
if (ctx.state.alive) ctx.state.pending++;
|
|
}
|
|
function popPendingState(ctx) {
|
|
if (ctx.state.alive) {
|
|
ctx.state.pending--;
|
|
checkStreamParse(ctx);
|
|
}
|
|
}
|
|
/**
|
|
* Parses a value that arrived after the root. Its record is queued before
|
|
* parsing starts so that records discovered inside it follow it, and it is
|
|
* dropped again if parsing fails. Returns the parse error after reporting
|
|
* it, or `undefined`.
|
|
*/
|
|
function parseEvent(ctx, depth, value, wrap, accept) {
|
|
const state = ctx.state;
|
|
const record = {
|
|
node: void 0,
|
|
initial: false,
|
|
accept,
|
|
refs: ctx.base.refs.size
|
|
};
|
|
let failure;
|
|
state.queue.push(record);
|
|
state.parsing++;
|
|
try {
|
|
record.node = wrap(parseSOS(ctx, depth, value));
|
|
} catch (error) {
|
|
failure = { value: error };
|
|
} finally {
|
|
state.parsing--;
|
|
if (!state.alive) releaseParserValues(ctx);
|
|
}
|
|
if (failure) stopStreamParse(ctx, 1, failure.value);
|
|
else drainOutput(ctx);
|
|
return failure?.value;
|
|
}
|
|
function takeRecord(state) {
|
|
const record = state.queue[state.head];
|
|
state.queue[state.head++] = void 0;
|
|
if (state.head === state.queue.length) {
|
|
state.queue.length = 0;
|
|
state.head = 0;
|
|
} else if (state.head > 1024 && state.head * 2 > state.queue.length) {
|
|
state.queue.splice(0, state.head);
|
|
state.head = 0;
|
|
}
|
|
return record;
|
|
}
|
|
/**
|
|
* Emits queued records one at a time. A callback that returns a promise
|
|
* pauses emission until it settles; the source event behind the record is
|
|
* accepted only after that.
|
|
*/
|
|
function drainOutput(ctx) {
|
|
const state = ctx.state;
|
|
if (state.writing || state.parsing > 0) return;
|
|
while (state.alive && state.head < state.queue.length) {
|
|
const record = takeRecord(state);
|
|
if (!record.node) continue;
|
|
let pending;
|
|
state.writing = true;
|
|
state.inFlight = record;
|
|
state.parsing++;
|
|
try {
|
|
if (!state.onParse) throw new Error("Stream output callback is unavailable");
|
|
const result = state.onParse(record.node, record.initial);
|
|
if (result !== null && (typeof result === "object" || typeof result === "function")) {
|
|
const then = Reflect.get(result, "then");
|
|
if (typeof then === "function") pending = new Promise((resolve, reject) => {
|
|
Reflect.apply(then, result, [resolve, reject]);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
releaseRecord(record);
|
|
stopStreamParse(ctx, 1, error);
|
|
return;
|
|
} finally {
|
|
state.parsing--;
|
|
if (!state.alive) releaseParserValues(ctx);
|
|
}
|
|
record.node = void 0;
|
|
if (pending) {
|
|
if (!state.alive) releaseRecord(record);
|
|
pending.then(() => {
|
|
state.writing = false;
|
|
state.inFlight = void 0;
|
|
if (state.alive) {
|
|
const accept = record.accept;
|
|
releaseRecord(record);
|
|
accept?.();
|
|
drainOutput(ctx);
|
|
}
|
|
}, (error) => {
|
|
state.writing = false;
|
|
state.inFlight = void 0;
|
|
releaseRecord(record);
|
|
stopStreamParse(ctx, 1, error);
|
|
});
|
|
return;
|
|
}
|
|
if (!state.alive) {
|
|
releaseRecord(record);
|
|
return;
|
|
}
|
|
state.writing = false;
|
|
state.inFlight = void 0;
|
|
const accept = record.accept;
|
|
releaseRecord(record);
|
|
accept?.();
|
|
}
|
|
checkStreamParse(ctx);
|
|
}
|
|
function checkStreamParse(ctx) {
|
|
const state = ctx.state;
|
|
if (state.alive && state.parsing === 0 && state.pending <= 0 && state.head === state.queue.length && !state.writing) stopStreamParse(ctx, 0);
|
|
}
|
|
function startStreamParse(ctx, current) {
|
|
const state = ctx.state;
|
|
let parsed;
|
|
let failure;
|
|
const refs = ctx.base.refs;
|
|
const size = refs.size;
|
|
state.starting = true;
|
|
try {
|
|
state.parsing++;
|
|
try {
|
|
parsed = parseSOS(ctx, 0, current);
|
|
} catch (error) {
|
|
failure = { value: error };
|
|
} finally {
|
|
state.parsing--;
|
|
if (!state.alive) releaseParserValues(ctx);
|
|
}
|
|
if (failure) {
|
|
rollbackRefs(refs, size);
|
|
if (state.alive) stopStreamParse(ctx, 1, failure.value);
|
|
else throw failure.value;
|
|
} else if (parsed && state.alive) {
|
|
state.queue.unshift({
|
|
node: parsed,
|
|
initial: true,
|
|
accept: void 0,
|
|
refs: size
|
|
});
|
|
drainOutput(ctx);
|
|
}
|
|
} finally {
|
|
state.starting = false;
|
|
}
|
|
}
|
|
function runCleanups(state) {
|
|
const cleanups = state.cleanups;
|
|
let failure;
|
|
state.cleanups = [];
|
|
for (const record of state.queue) if (record) releaseRecord(record);
|
|
state.queue.length = 0;
|
|
state.head = 0;
|
|
if (state.inFlight) releaseRecord(state.inFlight);
|
|
state.inFlight = void 0;
|
|
for (let i = 0, len = cleanups.length; i < len; i++) try {
|
|
cleanups[i]();
|
|
} catch (error) {
|
|
failure ?? (failure = { value: error });
|
|
}
|
|
if (failure) throw failure.value;
|
|
}
|
|
function releaseRecord(record) {
|
|
record.node = void 0;
|
|
record.accept = void 0;
|
|
}
|
|
function releaseParserValues(ctx) {
|
|
ctx.base.refs = /* @__PURE__ */ new Map();
|
|
ctx.base.marked.clear();
|
|
ctx.base.plugins = void 0;
|
|
ctx.base.plainObjectPlugins = void 0;
|
|
ctx.state.pluginContexts = [];
|
|
if (ctx.state.parsing === 0) ctx.state.reason = void 0;
|
|
}
|
|
/**
|
|
* Ends the parse once: `onDone` on success, `onError` on failure, and every
|
|
* cleanup either way.
|
|
*/
|
|
function stopStreamParse(ctx, mode, reason) {
|
|
const state = ctx.state;
|
|
if (state.alive) {
|
|
state.alive = false;
|
|
const unsent = state.queue[state.head];
|
|
if (mode !== 0 && unsent) rollbackRefs(ctx.base.refs, unsent.refs);
|
|
const onError = state.onError;
|
|
const onDone = state.onDone;
|
|
if (mode === 1 && !onError && !(reason instanceof require_shared.SerovalParserError)) reason = new require_shared.SerovalParserError(reason);
|
|
state.reason = reason;
|
|
state.onParse = void 0;
|
|
state.onError = void 0;
|
|
state.onDone = void 0;
|
|
state.writing = false;
|
|
state.parsing++;
|
|
let failure;
|
|
try {
|
|
if (mode === 1) {
|
|
if (onError) onError(reason);
|
|
else if (state.starting) throw reason;
|
|
} else if (mode === 0) onDone?.();
|
|
} catch (error) {
|
|
failure = { value: error };
|
|
}
|
|
try {
|
|
runCleanups(state);
|
|
} catch (error) {
|
|
failure ?? (failure = { value: error });
|
|
} finally {
|
|
state.parsing--;
|
|
releaseParserValues(ctx);
|
|
state.pending = 0;
|
|
}
|
|
if (failure) throw failure.value;
|
|
}
|
|
}
|
|
function destroyStreamParse(ctx, reason) {
|
|
stopStreamParse(ctx, 2, reason);
|
|
}
|
|
//#endregion
|
|
//#region src/core/cross/write.ts
|
|
function crossSerialize(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createSyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures,
|
|
refs: options.refs
|
|
});
|
|
const tree = parseTop(ctx, source);
|
|
return serializeTopCross(createCrossSerializerContext({
|
|
plugins,
|
|
features: ctx.base.features,
|
|
scopeId: options.scopeId,
|
|
markedRefs: ctx.base.marked,
|
|
assignSharedRefsOnly: options.assignSharedRefsOnly
|
|
}), tree);
|
|
}
|
|
async function crossSerializeAsync(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createAsyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures,
|
|
refs: options.refs
|
|
});
|
|
const tree = await parseTopAsync(ctx, source);
|
|
return serializeTopCross(createCrossSerializerContext({
|
|
plugins,
|
|
features: ctx.base.features,
|
|
scopeId: options.scopeId,
|
|
markedRefs: ctx.base.marked,
|
|
assignSharedRefsOnly: options.assignSharedRefsOnly
|
|
}), tree);
|
|
}
|
|
function toCrossJSON(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
return parseTop(createSyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures,
|
|
refs: options.refs
|
|
}), source);
|
|
}
|
|
async function toCrossJSONAsync(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
return await parseTopAsync(createAsyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures,
|
|
refs: options.refs
|
|
}), source);
|
|
}
|
|
function crossSerializeStream(source, options) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const onSerialize = options.onSerialize;
|
|
const scopeId = options.scopeId;
|
|
const ctx = createStreamParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
refs: options.refs,
|
|
disabledFeatures: options.disabledFeatures,
|
|
onParse(node, initial) {
|
|
const serial = createCrossSerializerContext({
|
|
plugins,
|
|
features: ctx.base.features,
|
|
scopeId,
|
|
markedRefs: ctx.base.marked
|
|
});
|
|
return onSerialize(serializeTopCross(serial, node), initial);
|
|
},
|
|
onError: options.onError,
|
|
onDone: options.onDone
|
|
});
|
|
startStreamParse(ctx, source);
|
|
return destroyStreamParse.bind(null, ctx);
|
|
}
|
|
function toCrossJSONStream(source, options) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createStreamParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
plugins,
|
|
refs: options.refs,
|
|
disabledFeatures: options.disabledFeatures,
|
|
depthLimit: options.depthLimit,
|
|
onParse: options.onParse,
|
|
onError: options.onError,
|
|
onDone: options.onDone
|
|
});
|
|
startStreamParse(ctx, source);
|
|
return destroyStreamParse.bind(null, ctx);
|
|
}
|
|
//#endregion
|
|
//#region src/core/Serializer.ts
|
|
var Serializer = class {
|
|
constructor(options) {
|
|
this.alive = true;
|
|
this.flushed = false;
|
|
this.done = false;
|
|
this.pending = 0;
|
|
this.cleanups = [];
|
|
this.refs = /* @__PURE__ */ new Map();
|
|
this.keys = /* @__PURE__ */ new Set();
|
|
this.ids = 0;
|
|
this.options = options;
|
|
this.plugins = require_shared.resolvePlugins(options.plugins);
|
|
}
|
|
write(key, value) {
|
|
const options = this.options;
|
|
if (this.alive && !this.flushed && options) {
|
|
this.pending++;
|
|
this.keys.add(key);
|
|
let starting = true;
|
|
const cleanup = crossSerializeStream(value, {
|
|
plugins: this.plugins,
|
|
scopeId: options.scopeId,
|
|
refs: this.refs,
|
|
disabledFeatures: options.disabledFeatures,
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
onError: (error) => {
|
|
let failure;
|
|
try {
|
|
if (options.onError) options.onError(error);
|
|
else if (starting) throw error instanceof require_shared.SerovalParserError ? error : new require_shared.SerovalParserError(error);
|
|
} catch (handlerError) {
|
|
failure = { value: handlerError };
|
|
}
|
|
try {
|
|
this.finishWrite();
|
|
} catch (finishError) {
|
|
failure ?? (failure = { value: finishError });
|
|
}
|
|
if (failure) throw failure.value;
|
|
},
|
|
onSerialize: (data, initial) => {
|
|
const current = this.options;
|
|
if (this.alive && current) current.onData(initial ? current.globalIdentifier + "[\"" + require_shared.serializeString(key) + "\"]=" + data : data);
|
|
},
|
|
onDone: () => {
|
|
this.finishWrite();
|
|
}
|
|
});
|
|
starting = false;
|
|
if (this.alive) this.cleanups.push(cleanup);
|
|
else cleanup();
|
|
}
|
|
}
|
|
finishWrite() {
|
|
if (this.alive) {
|
|
this.pending--;
|
|
if (this.pending <= 0 && this.flushed && !this.done) this.close();
|
|
}
|
|
}
|
|
getNextID() {
|
|
while (this.keys.has("" + this.ids)) this.ids++;
|
|
return "" + this.ids;
|
|
}
|
|
push(value) {
|
|
const newID = this.getNextID();
|
|
this.write(newID, value);
|
|
return newID;
|
|
}
|
|
flush() {
|
|
if (this.alive) {
|
|
this.flushed = true;
|
|
if (this.pending <= 0 && !this.done) this.close();
|
|
}
|
|
}
|
|
close() {
|
|
this.finish();
|
|
}
|
|
finish() {
|
|
if (this.alive) {
|
|
this.alive = false;
|
|
const options = this.options;
|
|
this.options = void 0;
|
|
this.plugins = void 0;
|
|
const cleanups = this.cleanups;
|
|
this.cleanups = [];
|
|
let failure;
|
|
for (let index = 0, length = cleanups.length; index < length; index++) try {
|
|
cleanups[index]();
|
|
} catch (error) {
|
|
failure ?? (failure = { value: error });
|
|
}
|
|
this.refs.clear();
|
|
if (!this.done) {
|
|
this.done = true;
|
|
try {
|
|
options?.onDone?.();
|
|
} catch (error) {
|
|
failure ?? (failure = { value: error });
|
|
}
|
|
}
|
|
if (failure) throw failure.value;
|
|
}
|
|
}
|
|
};
|
|
//#endregion
|
|
//#region src/core/tree/write.ts
|
|
function serialize(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createSyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures
|
|
});
|
|
const tree = parseTop(ctx, source);
|
|
return serializeTopVanilla(createVanillaSerializerContext({
|
|
plugins,
|
|
features: ctx.base.features,
|
|
markedRefs: ctx.base.marked
|
|
}), tree);
|
|
}
|
|
async function serializeAsync(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createAsyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures
|
|
});
|
|
const tree = await parseTopAsync(ctx, source);
|
|
return serializeTopVanilla(createVanillaSerializerContext({
|
|
plugins,
|
|
features: ctx.base.features,
|
|
markedRefs: ctx.base.marked
|
|
}), tree);
|
|
}
|
|
function toJSON(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createSyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures
|
|
});
|
|
return {
|
|
t: parseTop(ctx, source),
|
|
f: ctx.base.features,
|
|
m: Array.from(ctx.base.marked)
|
|
};
|
|
}
|
|
async function toJSONAsync(source, options = {}) {
|
|
const plugins = require_shared.resolvePlugins(options.plugins);
|
|
const ctx = createAsyncParserContext({
|
|
compactArrayBufferViews: options.compactArrayBufferViews,
|
|
depthLimit: options.depthLimit,
|
|
plugins,
|
|
disabledFeatures: options.disabledFeatures
|
|
});
|
|
return {
|
|
t: await parseTopAsync(ctx, source),
|
|
f: ctx.base.features,
|
|
m: Array.from(ctx.base.marked)
|
|
};
|
|
}
|
|
function compileJSON(source, options = {}) {
|
|
return serializeTopVanilla(createVanillaSerializerContext({
|
|
plugins: require_shared.resolvePlugins(options.plugins),
|
|
features: source.f,
|
|
markedRefs: source.m
|
|
}), source.t);
|
|
}
|
|
//#endregion
|
|
Object.defineProperty(exports, "Serializer", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return Serializer;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "compileJSON", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return compileJSON;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "crossSerialize", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return crossSerialize;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "crossSerializeAsync", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return crossSerializeAsync;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "crossSerializeStream", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return crossSerializeStream;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "serialize", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return serialize;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "serializeAsync", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return serializeAsync;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "toCrossJSON", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return toCrossJSON;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "toCrossJSONAsync", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return toCrossJSONAsync;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "toCrossJSONStream", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return toCrossJSONStream;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "toJSON", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return toJSON;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "toJSONAsync", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return toJSONAsync;
|
|
}
|
|
});
|