Comparing version 0.4.0-alpha.13 to 0.4.0-alpha.14
interface IndexAssignment { | ||
type: 'index'; | ||
source: string; | ||
value: string; | ||
t: 'index'; | ||
s: string; | ||
k: undefined; | ||
v: string; | ||
} | ||
interface MapAssignment { | ||
type: 'map'; | ||
source: string; | ||
key: string; | ||
value: string; | ||
t: 'map'; | ||
s: string; | ||
k: string; | ||
v: string; | ||
} | ||
interface SetAssignment { | ||
type: 'set'; | ||
source: string; | ||
value: string; | ||
t: 'set'; | ||
s: string; | ||
k: undefined; | ||
v: string; | ||
} | ||
export type Assignment = IndexAssignment | MapAssignment | SetAssignment; | ||
export interface Options { | ||
target: string | string[]; | ||
} | ||
export declare class ParserContext { | ||
export interface ParserContext { | ||
refs: Map<unknown, number>; | ||
markedRefs: Record<number, number>; | ||
markedRefs: Set<number>; | ||
features: number; | ||
constructor(options?: Partial<Options>); | ||
} | ||
export interface SerializationOptions { | ||
markedRefs: Record<number, number>; | ||
features: number; | ||
} | ||
export declare class SerializationContext { | ||
export interface SerializationContext { | ||
stack: number[]; | ||
validRefs: number[]; | ||
refSize: number; | ||
markedRefs: Set<number>; | ||
vars: string[]; | ||
assignments: Assignment[]; | ||
features: number; | ||
markedRefs: Record<number, number>; | ||
constructor(options: SerializationOptions); | ||
} | ||
export interface Options { | ||
target: string | string[]; | ||
} | ||
export declare function createParserContext(options?: Partial<Options>): ParserContext; | ||
export interface SerializationOptions { | ||
markedRefs: number[] | Set<number>; | ||
features: number; | ||
} | ||
export declare function createSerializationContext(options: SerializationOptions): SerializationContext; | ||
/** | ||
@@ -42,0 +44,0 @@ * Increments the number of references the referenced value has |
@@ -1,2 +0,5 @@ | ||
import { SerovalPrimitiveNode } from './types'; | ||
import { ParserContext } from '../context'; | ||
import { BigIntTypedArrayValue, TypedArrayValue } from '../types'; | ||
import { SerovalBigIntNode, SerovalBigIntTypedArrayNode, SerovalDateNode, SerovalPrimitiveNode, SerovalReferenceNode, SerovalRegExpNode, SerovalTypedArrayNode } from './types'; | ||
export declare function createPrimitiveNode(value: string | number | null): SerovalPrimitiveNode; | ||
export declare const TRUE_NODE: SerovalPrimitiveNode; | ||
@@ -9,1 +12,7 @@ export declare const FALSE_NODE: SerovalPrimitiveNode; | ||
export declare const NEG_INFINITY_NODE: SerovalPrimitiveNode; | ||
export declare function createBigIntNode(ctx: ParserContext, current: bigint): SerovalBigIntNode; | ||
export declare function createReferenceNode(id: number): SerovalReferenceNode; | ||
export declare function createDateNode(id: number, current: Date): SerovalDateNode; | ||
export declare function createRegExpNode(id: number, current: RegExp): SerovalRegExpNode; | ||
export declare function createTypedArrayNode(ctx: ParserContext, id: number, current: TypedArrayValue): SerovalTypedArrayNode; | ||
export declare function createBigIntTypedArrayNode(ctx: ParserContext, id: number, current: BigIntTypedArrayValue): SerovalBigIntTypedArrayNode; |
import { SerializationContext } from '../context'; | ||
import { SerovalNode } from './types'; | ||
import { SerovalAggregateErrorNode, SerovalArrayNode, SerovalBigIntTypedArrayNode, SerovalErrorNode, SerovalIterableNode, SerovalMapNode, SerovalNode, SerovalNullConstructorNode, SerovalObjectRecordNode, SerovalPromiseNode, SerovalReferenceNode, SerovalSetNode, SerovalTypedArrayNode } from './types'; | ||
export declare function resolvePatches(ctx: SerializationContext): string | undefined; | ||
export default function serializeTree(ctx: SerializationContext, node: SerovalNode): string; | ||
export default class SerovalSerializer { | ||
ctx: SerializationContext; | ||
constructor(ctx: SerializationContext); | ||
assignRef(index: number, value: string): string; | ||
isReferenceInStack(node: SerovalNode): node is SerovalReferenceNode; | ||
serializeNodeList(node: SerovalArrayNode | SerovalIterableNode): string; | ||
serializeArray(node: SerovalArrayNode): string; | ||
serializeObject(sourceID: number, node: SerovalObjectRecordNode): string; | ||
serializeWithObjectAssign(value: SerovalObjectRecordNode, id: number, serialized: string): string; | ||
serializeAssignments(sourceID: number, node: SerovalObjectRecordNode): string | undefined; | ||
serializeNullConstructor(node: SerovalNullConstructorNode): string; | ||
serializeSet(node: SerovalSetNode): string; | ||
serializeMap(node: SerovalMapNode): string; | ||
serializeAggregateError(node: SerovalAggregateErrorNode): string; | ||
serializeError(node: SerovalErrorNode): string; | ||
serializePromise(node: SerovalPromiseNode): string; | ||
serializeTypedArray(node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode): string; | ||
serializeIterable(node: SerovalIterableNode): string; | ||
serialize(node: SerovalNode): string; | ||
} |
@@ -1,8 +0,5 @@ | ||
import { SerializationContext } from '../context'; | ||
import { AsyncServerValue, ErrorValue } from '../types'; | ||
import { SerovalNode, SerovalReferenceNode } from './types'; | ||
export declare function getErrorConstructor(error: ErrorValue): "EvalError" | "RangeError" | "ReferenceError" | "SyntaxError" | "TypeError" | "URIError" | "Error"; | ||
export declare function getErrorOptions(error: Error): Record<string, any> | undefined; | ||
export declare function getIterableOptions(obj: Iterable<any>): Record<string, unknown> | undefined; | ||
export declare function isReferenceInStack(ctx: SerializationContext, node: SerovalNode): node is SerovalReferenceNode; | ||
export declare function isIterable(value: unknown): value is Iterable<AsyncServerValue>; |
@@ -1,2 +0,2 @@ | ||
import { BigIntTypedArrayValue, PrimitiveValue, TypedArrayValue } from '../types'; | ||
import { PrimitiveValue } from '../types'; | ||
export declare const enum SerovalNodeType { | ||
@@ -21,53 +21,45 @@ Primitive = 0, | ||
export interface SerovalBaseNode { | ||
t?: SerovalNodeType; | ||
s?: PrimitiveValue; | ||
i?: number; | ||
l?: number; | ||
c?: string; | ||
d?: SerovalDictionaryNode; | ||
m?: string; | ||
n?: SerovalNode; | ||
a?: SerovalNode[]; | ||
t: SerovalNodeType; | ||
s: PrimitiveValue | undefined; | ||
i: number | undefined; | ||
l: number | undefined; | ||
c: string | undefined; | ||
d: SerovalDictionaryNode | undefined; | ||
m: string | undefined; | ||
n: SerovalNode | undefined; | ||
a: SerovalNode[] | undefined; | ||
} | ||
export declare class SerovalObjectRecordNode { | ||
export interface SerovalObjectRecordNode { | ||
k: string[]; | ||
v: SerovalNode[]; | ||
s: number; | ||
constructor(k: string[], v: SerovalNode[], s: number); | ||
} | ||
export declare class SerovalMapRecordNode { | ||
export interface SerovalMapRecordNode { | ||
k: SerovalNode[]; | ||
v: SerovalNode[]; | ||
s: number; | ||
constructor(k: SerovalNode[], v: SerovalNode[], s: number); | ||
} | ||
export type SerovalDictionaryNode = SerovalObjectRecordNode | SerovalMapRecordNode; | ||
export declare class SerovalPrimitiveNode implements SerovalBaseNode { | ||
export interface SerovalPrimitiveNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Primitive; | ||
s: string | number | null; | ||
constructor(current: string | number | null); | ||
} | ||
export declare class SerovalReferenceNode implements SerovalBaseNode { | ||
export interface SerovalReferenceNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Reference; | ||
i: number; | ||
constructor(id: number); | ||
} | ||
export declare class SerovalBigIntNode implements SerovalBaseNode { | ||
export interface SerovalBigIntNode extends SerovalBaseNode { | ||
t: SerovalNodeType.BigInt; | ||
s: string; | ||
constructor(current: bigint); | ||
} | ||
export declare class SerovalDateNode implements SerovalBaseNode { | ||
export interface SerovalDateNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Date; | ||
i: number; | ||
s: string; | ||
constructor(id: number, current: Date); | ||
} | ||
export declare class SerovalRegExpNode implements SerovalBaseNode { | ||
export interface SerovalRegExpNode extends SerovalBaseNode { | ||
t: SerovalNodeType.RegExp; | ||
i: number; | ||
s: string; | ||
constructor(id: number, current: RegExp); | ||
} | ||
export declare class SerovalTypedArrayNode implements SerovalBaseNode { | ||
export interface SerovalTypedArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.TypedArray; | ||
@@ -78,5 +70,4 @@ i: number; | ||
c: string; | ||
constructor(id: number, current: TypedArrayValue); | ||
} | ||
export declare class SerovalBigIntTypedArrayNode implements SerovalBaseNode { | ||
export interface SerovalBigIntTypedArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.BigIntTypedArray; | ||
@@ -87,64 +78,54 @@ i: number; | ||
c: string; | ||
constructor(id: number, current: BigIntTypedArrayValue); | ||
} | ||
export type SerovalSemiPrimitiveNode = SerovalBigIntNode | SerovalDateNode | SerovalRegExpNode | SerovalTypedArrayNode | SerovalBigIntTypedArrayNode; | ||
export declare class SerovalSetNode implements SerovalBaseNode { | ||
export interface SerovalSetNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Set; | ||
a: SerovalNode[]; | ||
i: number; | ||
a: SerovalNode[]; | ||
constructor(id: number, a: SerovalNode[]); | ||
} | ||
export declare class SerovalMapNode implements SerovalBaseNode { | ||
export interface SerovalMapNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Map; | ||
d: SerovalMapRecordNode; | ||
i: number; | ||
d: SerovalMapRecordNode; | ||
constructor(id: number, d: SerovalMapRecordNode); | ||
} | ||
export declare class SerovalArrayNode implements SerovalBaseNode { | ||
export interface SerovalArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Array; | ||
a: SerovalNode[]; | ||
i: number; | ||
a: SerovalNode[]; | ||
constructor(id: number, a: SerovalNode[]); | ||
} | ||
export declare class SerovalObjectNode implements SerovalBaseNode { | ||
export interface SerovalObjectNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Object; | ||
d: SerovalObjectRecordNode; | ||
i: number; | ||
d: SerovalObjectRecordNode; | ||
constructor(id: number, d: SerovalObjectRecordNode); | ||
} | ||
export declare class SerovalNullConstructorNode implements SerovalBaseNode { | ||
export interface SerovalNullConstructorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.NullConstructor; | ||
d: SerovalObjectRecordNode; | ||
i: number; | ||
d: SerovalObjectRecordNode; | ||
constructor(id: number, d: SerovalObjectRecordNode); | ||
} | ||
export declare class SerovalPromiseNode implements SerovalBaseNode { | ||
export interface SerovalPromiseNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Promise; | ||
n: SerovalNode; | ||
i: number; | ||
n: SerovalNode; | ||
constructor(id: number, n: SerovalNode); | ||
} | ||
export declare class SerovalErrorNode implements SerovalBaseNode { | ||
export interface SerovalErrorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Error; | ||
i: number; | ||
c: string; | ||
m: string; | ||
d: SerovalObjectRecordNode | undefined; | ||
constructor(id: number, c: string, m: string, d: SerovalObjectRecordNode | undefined); | ||
i: number; | ||
} | ||
export declare class SerovalAggregateErrorNode implements SerovalBaseNode { | ||
export interface SerovalAggregateErrorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.AggregateError; | ||
i: number; | ||
m: string; | ||
d: SerovalObjectRecordNode | undefined; | ||
n: SerovalNode; | ||
constructor(id: number, m: string, d: SerovalObjectRecordNode | undefined, n: SerovalNode); | ||
i: number; | ||
} | ||
export declare class SerovalIterableNode implements SerovalBaseNode { | ||
export interface SerovalIterableNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Iterable; | ||
i: number; | ||
d: SerovalObjectRecordNode | undefined; | ||
a: SerovalNode[]; | ||
constructor(id: number, d: SerovalObjectRecordNode | undefined, a: SerovalNode[]); | ||
i: number; | ||
} | ||
export type SerovalNode = SerovalPrimitiveNode | SerovalReferenceNode | SerovalSemiPrimitiveNode | SerovalSetNode | SerovalMapNode | SerovalArrayNode | SerovalObjectNode | SerovalNullConstructorNode | SerovalPromiseNode | SerovalErrorNode | SerovalAggregateErrorNode | SerovalIterableNode; |
{ | ||
"name": "seroval", | ||
"type": "module", | ||
"version": "0.4.0-alpha.13", | ||
"version": "0.4.0-alpha.14", | ||
"files": [ | ||
@@ -67,3 +67,3 @@ "dist", | ||
}, | ||
"gitHead": "3f71e898ce27170bade38120eddc9afe96390498" | ||
"gitHead": "7a287f3985c45ba331913f7633ca1ebf31c502ac" | ||
} |
@@ -1,3 +0,1 @@ | ||
/* eslint-disable prefer-object-spread */ | ||
/* eslint-disable max-classes-per-file */ | ||
import { parseTargets } from './compat'; | ||
@@ -7,18 +5,20 @@ import getIdentifier from './get-identifier'; | ||
interface IndexAssignment { | ||
type: 'index'; | ||
source: string; | ||
value: string; | ||
t: 'index'; | ||
s: string; | ||
k: undefined; | ||
v: string; | ||
} | ||
interface MapAssignment { | ||
type: 'map'; | ||
source: string; | ||
key: string; | ||
value: string; | ||
t: 'map'; | ||
s: string; | ||
k: string; | ||
v: string; | ||
} | ||
interface SetAssignment { | ||
type: 'set'; | ||
source: string; | ||
value: string; | ||
t: 'set'; | ||
s: string; | ||
k: undefined; | ||
v: string; | ||
} | ||
@@ -32,2 +32,23 @@ | ||
export interface ParserContext { | ||
refs: Map<unknown, number>; | ||
markedRefs: Set<number>; | ||
features: number; | ||
} | ||
export interface SerializationContext { | ||
stack: number[]; | ||
// Map tree refs to actual refs | ||
validRefs: number[]; | ||
refSize: number; | ||
// Refs that are...referenced | ||
markedRefs: Set<number>; | ||
// Variables | ||
vars: string[]; | ||
// Array of assignments to be done (used for recursion) | ||
assignments: Assignment[]; | ||
// Supported features | ||
features: number; | ||
} | ||
export interface Options { | ||
@@ -41,44 +62,27 @@ target: string | string[]; | ||
export class ParserContext { | ||
refs = new Map<unknown, number>(); | ||
markedRefs: Record<number, number> = {}; | ||
features: number; | ||
constructor(options: Partial<Options> = {}) { | ||
const result = Object.assign({}, DEFAULT_OPTIONS, options || {}); | ||
this.features = parseTargets(result.target); | ||
} | ||
export function createParserContext(options: Partial<Options> = {}): ParserContext { | ||
// eslint-disable-next-line prefer-object-spread | ||
const result = Object.assign({}, DEFAULT_OPTIONS, options || {}); | ||
return { | ||
markedRefs: new Set(), | ||
refs: new Map(), | ||
features: parseTargets(result.target), | ||
}; | ||
} | ||
export interface SerializationOptions { | ||
markedRefs: Record<number, number>; | ||
markedRefs: number[] | Set<number>; | ||
features: number; | ||
} | ||
export class SerializationContext { | ||
stack: number[] = []; | ||
// Map tree refs to actual refs | ||
validRefs: number[] = []; | ||
refSize = 0; | ||
// Variables | ||
vars: string[] = []; | ||
// Array of assignments to be done (used for recursion) | ||
assignments: Assignment[] = []; | ||
// Supported features | ||
features: number; | ||
// Refs that are...referenced | ||
markedRefs: Record<number, number>; | ||
constructor(options: SerializationOptions) { | ||
this.features = options.features; | ||
this.markedRefs = options.markedRefs; | ||
} | ||
export function createSerializationContext(options: SerializationOptions): SerializationContext { | ||
return { | ||
stack: [], | ||
vars: [], | ||
assignments: [], | ||
validRefs: [], | ||
refSize: 0, | ||
features: options.features, | ||
markedRefs: new Set(options.markedRefs), | ||
}; | ||
} | ||
@@ -90,3 +94,3 @@ | ||
export function markRef(ctx: ParserContext | SerializationContext, current: number) { | ||
ctx.markedRefs[current] = 1; | ||
ctx.markedRefs.add(current); | ||
} | ||
@@ -93,0 +97,0 @@ |
@@ -7,6 +7,7 @@ /* eslint-disable no-await-in-loop */ | ||
Options, | ||
ParserContext, | ||
createParserContext, | ||
createSerializationContext, | ||
} from './context'; | ||
import parseAsync from './tree/async'; | ||
import serializeTree, { resolvePatches } from './tree/serialize'; | ||
import SerovalSerializer, { resolvePatches } from './tree/serialize'; | ||
import parseSync from './tree/sync'; | ||
@@ -73,6 +74,6 @@ import { SerovalNode } from './tree/types'; | ||
) { | ||
const ctx = new ParserContext(options); | ||
const ctx = createParserContext(options); | ||
const [tree, rootID, isObject] = parseSync(ctx, source); | ||
const serial = new SerializationContext(ctx); | ||
const result = serializeTree(serial, tree); | ||
const serial = createSerializationContext(ctx); | ||
const result = new SerovalSerializer(serial).serialize(tree); | ||
return finalize(serial, rootID, isObject, result); | ||
@@ -85,6 +86,6 @@ } | ||
) { | ||
const ctx = new ParserContext(options); | ||
const ctx = createParserContext(options); | ||
const [tree, rootID, isObject] = await parseAsync(ctx, source); | ||
const serial = new SerializationContext(ctx); | ||
const result = serializeTree(serial, tree); | ||
const serial = createSerializationContext(ctx); | ||
const result = new SerovalSerializer(serial).serialize(tree); | ||
return finalize(serial, rootID, isObject, result); | ||
@@ -98,9 +99,9 @@ } | ||
type SerovalJSON = [ | ||
tree: SerovalNode, | ||
root: number, | ||
isObject: boolean, | ||
feature: number, | ||
markedRefs: Record<number, number>, | ||
]; | ||
interface SerovalJSON { | ||
t: SerovalNode, | ||
r: number, | ||
i: boolean, | ||
f: number, | ||
m: number[], | ||
} | ||
@@ -111,11 +112,11 @@ export function toJSON<T extends ServerValue>( | ||
) { | ||
const ctx = new ParserContext(options); | ||
const ctx = createParserContext(options); | ||
const [tree, root, isObject] = parseSync(ctx, source); | ||
return JSON.stringify([ | ||
tree, | ||
root, | ||
isObject, | ||
ctx.features, | ||
ctx.markedRefs, | ||
]); | ||
return JSON.stringify({ | ||
t: tree, | ||
r: root, | ||
i: isObject, | ||
f: ctx.features, | ||
m: Array.from(ctx.markedRefs), | ||
}); | ||
} | ||
@@ -127,11 +128,11 @@ | ||
) { | ||
const ctx = new ParserContext(options); | ||
const ctx = createParserContext(options); | ||
const [tree, root, isObject] = await parseAsync(ctx, source); | ||
return JSON.stringify([ | ||
tree, | ||
root, | ||
isObject, | ||
ctx.features, | ||
ctx.markedRefs, | ||
]); | ||
return JSON.stringify({ | ||
t: tree, | ||
r: root, | ||
i: isObject, | ||
f: ctx.features, | ||
m: Array.from(ctx.markedRefs), | ||
}); | ||
} | ||
@@ -141,8 +142,8 @@ | ||
const parsed = JSON.parse(source) as SerovalJSON; | ||
const serial = new SerializationContext({ | ||
features: parsed[3], | ||
markedRefs: parsed[4], | ||
const serial = createSerializationContext({ | ||
features: parsed.f, | ||
markedRefs: parsed.m, | ||
}); | ||
const result = serializeTree(serial, parsed[0]); | ||
return finalize(serial, parsed[1], parsed[2], result); | ||
const result = new SerovalSerializer(serial).serialize(parsed.t); | ||
return finalize(serial, parsed.r, parsed.i, result); | ||
} | ||
@@ -149,0 +150,0 @@ |
@@ -13,5 +13,10 @@ /* eslint-disable no-await-in-loop */ | ||
import { | ||
createBigIntNode, | ||
createBigIntTypedArrayNode, | ||
createDateNode, | ||
createPrimitiveNode, | ||
createReferenceNode, | ||
createRegExpNode, | ||
createTypedArrayNode, | ||
FALSE_NODE, | ||
INFINITY_NODE, | ||
NEG_INFINITY_NODE, | ||
NEG_ZERO_NODE, | ||
@@ -31,9 +36,5 @@ NULL_NODE, | ||
SerovalArrayNode, | ||
SerovalBigIntNode, | ||
SerovalBigIntTypedArrayNode, | ||
SerovalDateNode, | ||
SerovalErrorNode, | ||
SerovalIterableNode, | ||
SerovalMapNode, | ||
SerovalMapRecordNode, | ||
SerovalNode, | ||
@@ -44,8 +45,4 @@ SerovalNodeType, | ||
SerovalObjectRecordNode, | ||
SerovalPrimitiveNode, | ||
SerovalPromiseNode, | ||
SerovalReferenceNode, | ||
SerovalRegExpNode, | ||
SerovalSetNode, | ||
SerovalTypedArrayNode, | ||
} from './types'; | ||
@@ -100,3 +97,13 @@ | ||
): Promise<SerovalArrayNode> { | ||
return new SerovalArrayNode(id, await this.generateNodeList(current)); | ||
return { | ||
t: SerovalNodeType.Array, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: await this.generateNodeList(current), | ||
n: undefined, | ||
}; | ||
} | ||
@@ -132,3 +139,13 @@ | ||
} | ||
return new SerovalMapNode(id, new SerovalMapRecordNode(keyNodes, valueNodes, len)); | ||
return { | ||
t: SerovalNodeType.Map, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: { k: keyNodes, v: valueNodes, s: len }, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -158,3 +175,13 @@ | ||
} | ||
return new SerovalSetNode(id, nodes); | ||
return { | ||
t: SerovalNodeType.Set, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: nodes, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -190,3 +217,7 @@ | ||
} | ||
return new SerovalObjectRecordNode(keyNodes, valueNodes, size); | ||
return { | ||
k: keyNodes, | ||
v: valueNodes, | ||
s: size, | ||
}; | ||
} | ||
@@ -200,10 +231,16 @@ | ||
const options = getIterableOptions(current); | ||
return new SerovalIterableNode( | ||
id, | ||
return { | ||
t: SerovalNodeType.Iterable, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
// Parse options first before the items | ||
options | ||
d: options | ||
? await this.generateProperties(options as Record<string, AsyncServerValue>) | ||
: undefined, | ||
await this.generateNodeList(Array.from(current)), | ||
); | ||
a: await this.generateNodeList(Array.from(current)), | ||
n: undefined, | ||
}; | ||
} | ||
@@ -216,3 +253,14 @@ | ||
assert(this.ctx.features & Feature.Promise, 'Unsupported type "Promise"'); | ||
return current.then(async (value) => new SerovalPromiseNode(id, await this.parse(value))); | ||
return current.then(async (value) => ({ | ||
t: SerovalNodeType.Promise, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
// Parse options first before the items | ||
d: undefined, | ||
a: undefined, | ||
n: await this.parse(value), | ||
})); | ||
} | ||
@@ -231,7 +279,13 @@ | ||
} | ||
const properties = await this.generateProperties(current as Record<string, AsyncServerValue>); | ||
if (empty) { | ||
return new SerovalNullConstructorNode(id, properties); | ||
} | ||
return new SerovalObjectNode(id, properties); | ||
return { | ||
t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: await this.generateProperties(current as Record<string, AsyncServerValue>), | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -247,8 +301,13 @@ | ||
: undefined; | ||
return new SerovalAggregateErrorNode( | ||
id, | ||
current.message, | ||
optionsNode, | ||
await this.parse(current.errors), | ||
); | ||
return { | ||
t: SerovalNodeType.AggregateError, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: current.message, | ||
d: optionsNode, | ||
a: undefined, | ||
n: await this.parse(current.errors), | ||
}; | ||
} | ||
@@ -264,3 +323,13 @@ | ||
: undefined; | ||
return new SerovalErrorNode(id, getErrorConstructor(current), current.message, optionsNode); | ||
return { | ||
t: SerovalNodeType.Error, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: getErrorConstructor(current), | ||
m: current.message, | ||
d: optionsNode, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -275,3 +344,3 @@ | ||
case 'string': | ||
return new SerovalPrimitiveNode(quote(current)); | ||
return createPrimitiveNode(quote(current)); | ||
case 'number': | ||
@@ -282,11 +351,10 @@ if (Object.is(current, -0)) { | ||
if (Object.is(current, Infinity)) { | ||
return INFINITY_NODE; | ||
return createPrimitiveNode('1/0'); | ||
} | ||
if (Object.is(current, -Infinity)) { | ||
return NEG_INFINITY_NODE; | ||
return createPrimitiveNode('-1/0'); | ||
} | ||
return new SerovalPrimitiveNode(current); | ||
return createPrimitiveNode(current); | ||
case 'bigint': | ||
assert(this.ctx.features & Feature.BigInt, 'Unsupported type "BigInt"'); | ||
return new SerovalBigIntNode(current); | ||
return createBigIntNode(this.ctx, current); | ||
case 'object': { | ||
@@ -299,4 +367,4 @@ if (!current) { | ||
const id = createRef(this.ctx, current, true); | ||
if (this.ctx.markedRefs[id]) { | ||
return new SerovalReferenceNode(id); | ||
if (this.ctx.markedRefs.has(id)) { | ||
return createReferenceNode(id); | ||
} | ||
@@ -308,5 +376,5 @@ if (Array.isArray(current)) { | ||
case Date: | ||
return new SerovalDateNode(id, current as Date); | ||
return createDateNode(id, current as Date); | ||
case RegExp: | ||
return new SerovalRegExpNode(id, current as RegExp); | ||
return createRegExpNode(id, current as RegExp); | ||
case Promise: | ||
@@ -323,11 +391,6 @@ return this.generatePromiseNode(id, current as Promise<AsyncServerValue>); | ||
case Float64Array: | ||
assert(this.ctx.features & Feature.TypedArray, `Unsupported value type "${current.constructor.name}"`); | ||
return new SerovalTypedArrayNode(id, current as TypedArrayValue); | ||
return createTypedArrayNode(this.ctx, id, current as TypedArrayValue); | ||
case BigInt64Array: | ||
case BigUint64Array: | ||
assert( | ||
this.ctx.features & (Feature.BigIntTypedArray), | ||
`Unsupported value type "${current.constructor.name}"`, | ||
); | ||
return new SerovalBigIntTypedArrayNode(id, current as BigIntTypedArrayValue); | ||
return createBigIntTypedArrayNode(this.ctx, id, current as BigIntTypedArrayValue); | ||
case Map: | ||
@@ -334,0 +397,0 @@ return this.generateMapNode(id, current as Map<AsyncServerValue, AsyncServerValue>); |
@@ -0,11 +1,147 @@ | ||
import assert from '../assert'; | ||
import { Feature } from '../compat'; | ||
import { ParserContext } from '../context'; | ||
import { BigIntTypedArrayValue, TypedArrayValue } from '../types'; | ||
import { | ||
SerovalBigIntNode, | ||
SerovalBigIntTypedArrayNode, | ||
SerovalDateNode, | ||
SerovalNodeType, | ||
SerovalPrimitiveNode, | ||
SerovalReferenceNode, | ||
SerovalRegExpNode, | ||
SerovalTypedArrayNode, | ||
} from './types'; | ||
export const TRUE_NODE = new SerovalPrimitiveNode('!0'); | ||
export const FALSE_NODE = new SerovalPrimitiveNode('!1'); | ||
export const UNDEFINED_NODE = new SerovalPrimitiveNode('void 0'); | ||
export const NULL_NODE = new SerovalPrimitiveNode(null); | ||
export const NEG_ZERO_NODE = new SerovalPrimitiveNode('-0'); | ||
export const INFINITY_NODE = new SerovalPrimitiveNode('1/0'); | ||
export const NEG_INFINITY_NODE = new SerovalPrimitiveNode('-1/0'); | ||
export function createPrimitiveNode( | ||
value: string | number | null, | ||
): SerovalPrimitiveNode { | ||
return { | ||
t: SerovalNodeType.Primitive, | ||
i: undefined, | ||
s: value, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
export const TRUE_NODE = createPrimitiveNode('!0'); | ||
export const FALSE_NODE = createPrimitiveNode('!1'); | ||
export const UNDEFINED_NODE = createPrimitiveNode('void 0'); | ||
export const NULL_NODE = createPrimitiveNode(null); | ||
export const NEG_ZERO_NODE = createPrimitiveNode('-0'); | ||
export const INFINITY_NODE = createPrimitiveNode('1/0'); | ||
export const NEG_INFINITY_NODE = createPrimitiveNode('-1/0'); | ||
export function createBigIntNode( | ||
ctx: ParserContext, | ||
current: bigint, | ||
): SerovalBigIntNode { | ||
assert(ctx.features & Feature.BigInt, 'Unsupported type "BigInt"'); | ||
return { | ||
t: SerovalNodeType.BigInt, | ||
i: undefined, | ||
s: `${current}n`, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
export function createReferenceNode(id: number): SerovalReferenceNode { | ||
return { | ||
t: SerovalNodeType.Reference, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
export function createDateNode(id: number, current: Date): SerovalDateNode { | ||
return { | ||
t: SerovalNodeType.Date, | ||
i: id, | ||
s: current.toISOString(), | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
n: undefined, | ||
a: undefined, | ||
}; | ||
} | ||
export function createRegExpNode(id: number, current: RegExp): SerovalRegExpNode { | ||
return { | ||
t: SerovalNodeType.RegExp, | ||
i: id, | ||
s: String(current), | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
export function createTypedArrayNode( | ||
ctx: ParserContext, | ||
id: number, | ||
current: TypedArrayValue, | ||
): SerovalTypedArrayNode { | ||
const constructor = current.constructor.name; | ||
assert(ctx.features & Feature.TypedArray, `Unsupported value type "${constructor}"`); | ||
return { | ||
t: SerovalNodeType.TypedArray, | ||
i: id, | ||
s: current.toString(), | ||
l: current.byteOffset, | ||
c: constructor, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
export function createBigIntTypedArrayNode( | ||
ctx: ParserContext, | ||
id: number, | ||
current: BigIntTypedArrayValue, | ||
): SerovalBigIntTypedArrayNode { | ||
const constructor = current.constructor.name; | ||
assert( | ||
ctx.features & (Feature.BigIntTypedArray), | ||
`Unsupported value type "${constructor}"`, | ||
); | ||
let result = ''; | ||
const cap = current.length - 1; | ||
for (let i = 0; i < cap; i++) { | ||
result += `${current[i]}n,`; | ||
} | ||
result += `"${current[cap]}"`; | ||
return { | ||
t: SerovalNodeType.BigIntTypedArray, | ||
i: id, | ||
s: result, | ||
l: (current as BigInt64Array).byteOffset, | ||
c: constructor, | ||
m: undefined, | ||
d: undefined, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} |
@@ -10,3 +10,2 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ | ||
import quote from '../quote'; | ||
import { isReferenceInStack } from './shared'; | ||
import { | ||
@@ -24,2 +23,3 @@ SerovalAggregateErrorNode, | ||
SerovalPromiseNode, | ||
SerovalReferenceNode, | ||
SerovalSetNode, | ||
@@ -30,9 +30,9 @@ SerovalTypedArrayNode, | ||
function getAssignmentExpression(assignment: Assignment): string { | ||
switch (assignment.type) { | ||
switch (assignment.t) { | ||
case 'index': | ||
return `${assignment.source}=${assignment.value}`; | ||
return `${assignment.s}=${assignment.v}`; | ||
case 'map': | ||
return `${assignment.source}.set(${assignment.key},${assignment.value})`; | ||
return `${assignment.s}.set(${assignment.k},${assignment.v})`; | ||
case 'set': | ||
return `${assignment.source}.add(${assignment.value})`; | ||
return `${assignment.s}.add(${assignment.v})`; | ||
default: | ||
@@ -49,25 +49,27 @@ return ''; | ||
const item = assignments[i]; | ||
if (item.type === prev.type) { | ||
if (item.type === 'index' && item.value === prev.value) { | ||
if (item.t === prev.t) { | ||
if (item.t === 'index' && item.v === prev.v) { | ||
// Merge if the right-hand value is the same | ||
// saves at least 2 chars | ||
current = { | ||
type: 'index', | ||
source: item.source, | ||
value: getAssignmentExpression(current), | ||
t: 'index', | ||
s: item.s, | ||
k: undefined, | ||
v: getAssignmentExpression(current), | ||
}; | ||
} else if (item.type === 'map' && item.source === prev.source) { | ||
} else if (item.t === 'map' && item.s === prev.s) { | ||
// Maps has chaining methods, merge if source is the same | ||
current = { | ||
type: 'map', | ||
source: getAssignmentExpression(current), | ||
key: item.key, | ||
value: item.value, | ||
t: 'map', | ||
s: getAssignmentExpression(current), | ||
k: item.k, | ||
v: item.v, | ||
}; | ||
} else if (item.type === 'set' && item.source === prev.source) { | ||
} else if (item.t === 'set' && item.s === prev.s) { | ||
// Sets has chaining methods too | ||
current = { | ||
type: 'set', | ||
source: getAssignmentExpression(current), | ||
value: item.value, | ||
t: 'set', | ||
s: getAssignmentExpression(current), | ||
k: undefined, | ||
v: item.v, | ||
}; | ||
@@ -112,12 +114,2 @@ } else { | ||
*/ | ||
function assignRef( | ||
ctx: SerializationContext, | ||
index: number, | ||
value: string, | ||
) { | ||
if (ctx.markedRefs[index]) { | ||
return `${getRefParam(ctx, index)}=${value}`; | ||
} | ||
return value; | ||
} | ||
@@ -130,5 +122,6 @@ function createAssignment( | ||
ctx.assignments.push({ | ||
type: 'index', | ||
source, | ||
value, | ||
t: 'index', | ||
s: source, | ||
k: undefined, | ||
v: value, | ||
}); | ||
@@ -144,5 +137,6 @@ } | ||
ctx.assignments.push({ | ||
type: 'set', | ||
source: getRefParam(ctx, ref), | ||
value, | ||
t: 'set', | ||
s: getRefParam(ctx, ref), | ||
k: undefined, | ||
v: value, | ||
}); | ||
@@ -159,6 +153,6 @@ } | ||
ctx.assignments.push({ | ||
type: 'map', | ||
source: getRefParam(ctx, ref), | ||
key, | ||
value, | ||
t: 'map', | ||
s: getRefParam(ctx, ref), | ||
k: key, | ||
v: value, | ||
}); | ||
@@ -189,372 +183,377 @@ } | ||
function serializeAssignments( | ||
ctx: SerializationContext, | ||
targetRef: number, | ||
node: SerovalObjectRecordNode, | ||
) { | ||
ctx.stack.push(targetRef); | ||
const mainAssignments: Assignment[] = []; | ||
for (let i = 0; i < node.s; i++) { | ||
const parentStack = ctx.stack; | ||
ctx.stack = []; | ||
const refParam = serializeTree(ctx, node.v[i]); | ||
ctx.stack = parentStack; | ||
const key = node.k[i]; | ||
const check = Number(key); | ||
const parentAssignment = ctx.assignments; | ||
ctx.assignments = mainAssignments; | ||
// Test if key is a valid number or JS identifier | ||
// so that we don't have to serialize the key and wrap with brackets | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
if (isIdentifier && Number.isNaN(check)) { | ||
createObjectAssign(ctx, targetRef, key, refParam); | ||
} else { | ||
createArrayAssign(ctx, targetRef, isIdentifier ? key : quote(key), refParam); | ||
} | ||
ctx.assignments = parentAssignment; | ||
export default class SerovalSerializer { | ||
ctx: SerializationContext; | ||
constructor(ctx: SerializationContext) { | ||
this.ctx = ctx; | ||
} | ||
ctx.stack.pop(); | ||
return resolveAssignments(mainAssignments); | ||
} | ||
function serializeObject( | ||
ctx: SerializationContext, | ||
sourceID: number, | ||
node: SerovalObjectRecordNode, | ||
) { | ||
if (node.s === 0) { | ||
return '{}'; | ||
} | ||
let result = ''; | ||
ctx.stack.push(sourceID); | ||
for (let i = 0; i < node.s; i++) { | ||
const key = node.k[i]; | ||
const val = node.v[i]; | ||
const check = Number(key); | ||
// Test if key is a valid number or JS identifier | ||
// so that we don't have to serialize the key and wrap with brackets | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
if (isReferenceInStack(ctx, val)) { | ||
const refParam = getRefParam(ctx, val.i); | ||
if (isIdentifier && Number.isNaN(check)) { | ||
createObjectAssign(ctx, sourceID, key, refParam); | ||
} else { | ||
createArrayAssign(ctx, sourceID, isIdentifier ? key : quote(key), refParam); | ||
} | ||
} else { | ||
result += `${isIdentifier ? key : quote(key)}:${serializeTree(ctx, val)},`; | ||
assignRef(index: number, value: string) { | ||
if (this.ctx.markedRefs.has(index)) { | ||
return `${getRefParam(this.ctx, index)}=${value}`; | ||
} | ||
return value; | ||
} | ||
ctx.stack.pop(); | ||
return `{${result.substring(0, result.length - 1)}}`; | ||
} | ||
function serializePromise( | ||
ctx: SerializationContext, | ||
node: SerovalPromiseNode, | ||
) { | ||
let serialized: string; | ||
// Check if resolved value is a parent expression | ||
if (isReferenceInStack(ctx, node.n)) { | ||
// A Promise trick, reference the value | ||
// inside the `then` expression so that | ||
// the Promise evaluates after the parent | ||
// has initialized | ||
serialized = `Promise.resolve().then(()=>${getRefParam(ctx, node.n.i)})`; | ||
} else { | ||
ctx.stack.push(node.i); | ||
const result = serializeTree(ctx, node.n); | ||
ctx.stack.pop(); | ||
// just inline the value/reference here | ||
serialized = `Promise.resolve(${result})`; | ||
isReferenceInStack( | ||
node: SerovalNode, | ||
): node is SerovalReferenceNode { | ||
return node.t === SerovalNodeType.Reference && this.ctx.stack.includes(node.i); | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeTypedArray( | ||
ctx: SerializationContext, | ||
node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode, | ||
) { | ||
let args = `[${node.s}]`; | ||
if (node.l !== 0) { | ||
args += `,${node.l}`; | ||
} | ||
return assignRef(ctx, node.i, `new ${node.c}(${args})`); | ||
} | ||
function serializeSet( | ||
ctx: SerializationContext, | ||
node: SerovalSetNode, | ||
) { | ||
let serialized = 'new Set'; | ||
const size = node.a.length; | ||
if (size) { | ||
let result = ''; | ||
ctx.stack.push(node.i); | ||
serializeNodeList( | ||
node: SerovalArrayNode | SerovalIterableNode, | ||
) { | ||
// This is different than Map and Set | ||
// because we also need to serialize | ||
// the holes of the Array | ||
const size = node.a.length; | ||
let values = ''; | ||
for (let i = 0; i < size; i++) { | ||
const item = node.a[i]; | ||
if (isReferenceInStack(ctx, item)) { | ||
createSetAdd(ctx, node.i, getRefParam(ctx, item.i)); | ||
// Check if index is a hole | ||
if (item) { | ||
// Check if item is a parent | ||
if (this.isReferenceInStack(item)) { | ||
createArrayAssign(this.ctx, node.i, i, getRefParam(this.ctx, item.i)); | ||
values += ','; | ||
} else { | ||
values += this.serialize(item); | ||
if (i < size - 1) { | ||
values += ','; | ||
} | ||
} | ||
} else { | ||
// Push directly | ||
result += `${serializeTree(ctx, item)},`; | ||
// Add an empty item | ||
values += ','; | ||
} | ||
} | ||
ctx.stack.pop(); | ||
if (result) { | ||
serialized += `([${result.substring(0, result.length - 1)}])`; | ||
} | ||
return `[${values}]`; | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeMap( | ||
ctx: SerializationContext, | ||
node: SerovalMapNode, | ||
) { | ||
let serialized = 'new Map'; | ||
if (node.d.s) { | ||
serializeArray( | ||
node: SerovalArrayNode, | ||
) { | ||
this.ctx.stack.push(node.i); | ||
const result = this.serializeNodeList(node); | ||
this.ctx.stack.pop(); | ||
return this.assignRef(node.i, result); | ||
} | ||
serializeObject( | ||
sourceID: number, | ||
node: SerovalObjectRecordNode, | ||
) { | ||
if (node.s === 0) { | ||
return '{}'; | ||
} | ||
let result = ''; | ||
ctx.stack.push(node.i); | ||
for (let i = 0; i < node.d.s; i++) { | ||
// Check if key is a parent | ||
const key = node.d.k[i]; | ||
const val = node.d.v[i]; | ||
if (isReferenceInStack(ctx, key)) { | ||
// Create reference for the map instance | ||
const keyRef = getRefParam(ctx, key.i); | ||
// Check if value is a parent | ||
if (isReferenceInStack(ctx, val)) { | ||
const valueRef = getRefParam(ctx, val.i); | ||
// Register an assignment since | ||
// both key and value are a parent of this | ||
// Map instance | ||
createMapSet(ctx, node.i, keyRef, valueRef); | ||
this.ctx.stack.push(sourceID); | ||
for (let i = 0; i < node.s; i++) { | ||
const key = node.k[i]; | ||
const val = node.v[i]; | ||
const check = Number(key); | ||
// Test if key is a valid number or JS identifier | ||
// so that we don't have to serialize the key and wrap with brackets | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
if (this.isReferenceInStack(val)) { | ||
const refParam = getRefParam(this.ctx, val.i); | ||
if (isIdentifier && Number.isNaN(check)) { | ||
createObjectAssign(this.ctx, sourceID, key, refParam); | ||
} else { | ||
// Reset the stack | ||
// This is required because the serialized | ||
// value is no longer part of the expression | ||
// tree and has been moved to the deferred | ||
// assignment | ||
const parent = ctx.stack; | ||
ctx.stack = []; | ||
createMapSet(ctx, node.i, keyRef, serializeTree(ctx, val)); | ||
ctx.stack = parent; | ||
createArrayAssign(this.ctx, sourceID, isIdentifier ? key : quote(key), refParam); | ||
} | ||
} else if (isReferenceInStack(ctx, val)) { | ||
// Create ref for the Map instance | ||
const valueRef = getRefParam(ctx, val.i); | ||
// Reset stack for the key serialization | ||
const parent = ctx.stack; | ||
ctx.stack = []; | ||
createMapSet(ctx, node.i, serializeTree(ctx, key), valueRef); | ||
ctx.stack = parent; | ||
} else { | ||
result += `[${serializeTree(ctx, key)},${serializeTree(ctx, val)}],`; | ||
result += `${isIdentifier ? key : quote(key)}:${this.serialize(val)},`; | ||
} | ||
} | ||
ctx.stack.pop(); | ||
// Check if there are any values | ||
// so that the empty Map constructor | ||
// can be used instead | ||
if (result) { | ||
serialized += `([${result.substring(0, result.length - 1)}])`; | ||
this.ctx.stack.pop(); | ||
return `{${result.substring(0, result.length - 1)}}`; | ||
} | ||
serializeWithObjectAssign( | ||
value: SerovalObjectRecordNode, | ||
id: number, | ||
serialized: string, | ||
) { | ||
const fields = this.serializeObject(id, value); | ||
if (fields !== '{}') { | ||
return `Object.assign(${serialized},${fields})`; | ||
} | ||
return serialized; | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeNodeList( | ||
ctx: SerializationContext, | ||
node: SerovalArrayNode | SerovalIterableNode, | ||
) { | ||
// This is different than Map and Set | ||
// because we also need to serialize | ||
// the holes of the Array | ||
const size = node.a.length; | ||
let values = ''; | ||
for (let i = 0; i < size; i++) { | ||
const item = node.a[i]; | ||
// Check if index is a hole | ||
if (item) { | ||
// Check if item is a parent | ||
if (isReferenceInStack(ctx, item)) { | ||
createArrayAssign(ctx, node.i, i, getRefParam(ctx, item.i)); | ||
values += ','; | ||
serializeAssignments( | ||
sourceID: number, | ||
node: SerovalObjectRecordNode, | ||
) { | ||
this.ctx.stack.push(sourceID); | ||
const mainAssignments: Assignment[] = []; | ||
for (let i = 0; i < node.s; i++) { | ||
const parentStack = this.ctx.stack; | ||
this.ctx.stack = []; | ||
const refParam = this.serialize(node.v[i]); | ||
this.ctx.stack = parentStack; | ||
const key = node.k[i]; | ||
const check = Number(key); | ||
const parentAssignment = this.ctx.assignments; | ||
this.ctx.assignments = mainAssignments; | ||
// Test if key is a valid number or JS identifier | ||
// so that we don't have to serialize the key and wrap with brackets | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
if (isIdentifier && Number.isNaN(check)) { | ||
createObjectAssign(this.ctx, sourceID, key, refParam); | ||
} else { | ||
values += serializeTree(ctx, item); | ||
if (i < size - 1) { | ||
values += ','; | ||
} | ||
createArrayAssign(this.ctx, sourceID, isIdentifier ? key : quote(key), refParam); | ||
} | ||
this.ctx.assignments = parentAssignment; | ||
} | ||
this.ctx.stack.pop(); | ||
return resolveAssignments(mainAssignments); | ||
} | ||
serializeNullConstructor( | ||
node: SerovalNullConstructorNode, | ||
) { | ||
let serialized = 'Object.create(null)'; | ||
if (this.ctx.features & Feature.ObjectAssign) { | ||
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized); | ||
} else { | ||
// Add an empty item | ||
values += ','; | ||
markRef(this.ctx, node.i); | ||
const assignments = this.serializeAssignments(node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(this.ctx, node.i); | ||
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`; | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
return `[${values}]`; | ||
} | ||
function serializeArray( | ||
ctx: SerializationContext, | ||
node: SerovalArrayNode, | ||
) { | ||
ctx.stack.push(node.i); | ||
const result = serializeNodeList(ctx, node); | ||
ctx.stack.pop(); | ||
return assignRef(ctx, node.i, result); | ||
} | ||
serializeSet( | ||
node: SerovalSetNode, | ||
) { | ||
let serialized = 'new Set'; | ||
const size = node.a.length; | ||
if (size) { | ||
let result = ''; | ||
this.ctx.stack.push(node.i); | ||
for (let i = 0; i < size; i++) { | ||
const item = node.a[i]; | ||
if (this.isReferenceInStack(item)) { | ||
createSetAdd(this.ctx, node.i, getRefParam(this.ctx, item.i)); | ||
} else { | ||
// Push directly | ||
result += `${this.serialize(item)},`; | ||
} | ||
} | ||
this.ctx.stack.pop(); | ||
if (result) { | ||
serialized += `([${result.substring(0, result.length - 1)}])`; | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
function serializeWithObjectAssign( | ||
ctx: SerializationContext, | ||
value: SerovalObjectRecordNode, | ||
id: number, | ||
serialized: string, | ||
) { | ||
const fields = serializeObject(ctx, id, value); | ||
if (fields !== '{}') { | ||
return `Object.assign(${serialized},${fields})`; | ||
serializeMap( | ||
node: SerovalMapNode, | ||
) { | ||
let serialized = 'new Map'; | ||
if (node.d.s) { | ||
let result = ''; | ||
this.ctx.stack.push(node.i); | ||
for (let i = 0; i < node.d.s; i++) { | ||
// Check if key is a parent | ||
const key = node.d.k[i]; | ||
const val = node.d.v[i]; | ||
if (this.isReferenceInStack(key)) { | ||
// Create reference for the map instance | ||
const keyRef = getRefParam(this.ctx, key.i); | ||
// Check if value is a parent | ||
if (this.isReferenceInStack(val)) { | ||
const valueRef = getRefParam(this.ctx, val.i); | ||
// Register an assignment since | ||
// both key and value are a parent of this | ||
// Map instance | ||
createMapSet(this.ctx, node.i, keyRef, valueRef); | ||
} else { | ||
// Reset the stack | ||
// This is required because the serialized | ||
// value is no longer part of the expression | ||
// tree and has been moved to the deferred | ||
// assignment | ||
const parent = this.ctx.stack; | ||
this.ctx.stack = []; | ||
createMapSet(this.ctx, node.i, keyRef, this.serialize(val)); | ||
this.ctx.stack = parent; | ||
} | ||
} else if (this.isReferenceInStack(val)) { | ||
// Create ref for the Map instance | ||
const valueRef = getRefParam(this.ctx, val.i); | ||
// Reset stack for the key serialization | ||
const parent = this.ctx.stack; | ||
this.ctx.stack = []; | ||
createMapSet(this.ctx, node.i, this.serialize(key), valueRef); | ||
this.ctx.stack = parent; | ||
} else { | ||
result += `[${this.serialize(key)},${this.serialize(val)}],`; | ||
} | ||
} | ||
this.ctx.stack.pop(); | ||
// Check if there are any values | ||
// so that the empty Map constructor | ||
// can be used instead | ||
if (result) { | ||
serialized += `([${result.substring(0, result.length - 1)}])`; | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
return serialized; | ||
} | ||
function serializeAggregateError( | ||
ctx: SerializationContext, | ||
node: SerovalAggregateErrorNode, | ||
) { | ||
// Serialize the required arguments | ||
ctx.stack.push(node.i); | ||
let serialized = `new AggregateError(${serializeTree(ctx, node.n)},${quote(node.m)})`; | ||
ctx.stack.pop(); | ||
// `AggregateError` might've been extended | ||
// either through class or custom properties | ||
// Make sure to assign extra properties | ||
if (node.d) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized); | ||
} else { | ||
markRef(ctx, node.i); | ||
const assignments = serializeAssignments(ctx, node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(ctx, node.i); | ||
return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`; | ||
serializeAggregateError( | ||
node: SerovalAggregateErrorNode, | ||
) { | ||
// Serialize the required arguments | ||
this.ctx.stack.push(node.i); | ||
let serialized = `new AggregateError(${this.serialize(node.n)},${quote(node.m)})`; | ||
this.ctx.stack.pop(); | ||
// `AggregateError` might've been extended | ||
// either through class or custom properties | ||
// Make sure to assign extra properties | ||
if (node.d) { | ||
if (this.ctx.features & Feature.ObjectAssign) { | ||
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized); | ||
} else { | ||
markRef(this.ctx, node.i); | ||
const assignments = this.serializeAssignments(node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(this.ctx, node.i); | ||
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`; | ||
} | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeError( | ||
ctx: SerializationContext, | ||
node: SerovalErrorNode, | ||
) { | ||
let serialized = `new ${node.c}(${quote(node.m)})`; | ||
if (node.d) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized); | ||
} else { | ||
markRef(ctx, node.i); | ||
const assignments = serializeAssignments(ctx, node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(ctx, node.i); | ||
return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`; | ||
serializeError( | ||
node: SerovalErrorNode, | ||
) { | ||
let serialized = `new ${node.c}(${quote(node.m)})`; | ||
if (node.d) { | ||
if (this.ctx.features & Feature.ObjectAssign) { | ||
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized); | ||
} else { | ||
markRef(this.ctx, node.i); | ||
const assignments = this.serializeAssignments(node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(this.ctx, node.i); | ||
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`; | ||
} | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeIterable( | ||
ctx: SerializationContext, | ||
node: SerovalIterableNode, | ||
) { | ||
const parent = ctx.stack; | ||
ctx.stack = []; | ||
const values = serializeNodeList(ctx, node); | ||
ctx.stack = parent; | ||
let serialized: string; | ||
if (ctx.features & Feature.ArrayPrototypeValues) { | ||
serialized = `${values}.values()`; | ||
} else { | ||
serialized = `${values}[Symbol.iterator]()`; | ||
serializePromise( | ||
node: SerovalPromiseNode, | ||
) { | ||
let serialized: string; | ||
// Check if resolved value is a parent expression | ||
if (this.isReferenceInStack(node.n)) { | ||
// A Promise trick, reference the value | ||
// inside the `then` expression so that | ||
// the Promise evaluates after the parent | ||
// has initialized | ||
serialized = `Promise.resolve().then(()=>${getRefParam(this.ctx, node.n.i)})`; | ||
} else { | ||
this.ctx.stack.push(node.i); | ||
const result = this.serialize(node.n); | ||
this.ctx.stack.pop(); | ||
// just inline the value/reference here | ||
serialized = `Promise.resolve(${result})`; | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
if (ctx.features & Feature.ArrowFunction) { | ||
serialized = `{[Symbol.iterator]:()=>${serialized}}`; | ||
} else if (ctx.features & Feature.MethodShorthand) { | ||
serialized = `{[Symbol.iterator](){return ${serialized}}}`; | ||
} else { | ||
serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`; | ||
serializeTypedArray( | ||
node: SerovalTypedArrayNode | SerovalBigIntTypedArrayNode, | ||
) { | ||
let args = `[${node.s}]`; | ||
if (node.l !== 0) { | ||
args += `,${node.l}`; | ||
} | ||
return this.assignRef(node.i, `new ${node.c}(${args})`); | ||
} | ||
if (node.d) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized); | ||
serializeIterable( | ||
node: SerovalIterableNode, | ||
) { | ||
const parent = this.ctx.stack; | ||
this.ctx.stack = []; | ||
const values = this.serializeNodeList(node); | ||
this.ctx.stack = parent; | ||
let serialized: string; | ||
if (this.ctx.features & Feature.ArrayPrototypeValues) { | ||
serialized = `${values}.values()`; | ||
} else { | ||
markRef(ctx, node.i); | ||
const assignments = serializeAssignments(ctx, node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(ctx, node.i); | ||
return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`; | ||
serialized = `${values}[Symbol.iterator]()`; | ||
} | ||
if (this.ctx.features & Feature.ArrowFunction) { | ||
serialized = `{[Symbol.iterator]:()=>${serialized}}`; | ||
} else if (this.ctx.features & Feature.MethodShorthand) { | ||
serialized = `{[Symbol.iterator](){return ${serialized}}}`; | ||
} else { | ||
serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`; | ||
} | ||
if (node.d) { | ||
if (this.ctx.features & Feature.ObjectAssign) { | ||
serialized = this.serializeWithObjectAssign(node.d, node.i, serialized); | ||
} else { | ||
markRef(this.ctx, node.i); | ||
const assignments = this.serializeAssignments(node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(this.ctx, node.i); | ||
return `(${this.assignRef(node.i, serialized)},${assignments}${ref})`; | ||
} | ||
} | ||
} | ||
return this.assignRef(node.i, serialized); | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
function serializeNullConstructor( | ||
ctx: SerializationContext, | ||
node: SerovalNullConstructorNode, | ||
) { | ||
let serialized = 'Object.create(null)'; | ||
if (ctx.features & Feature.ObjectAssign) { | ||
serialized = serializeWithObjectAssign(ctx, node.d, node.i, serialized); | ||
} else { | ||
markRef(ctx, node.i); | ||
const assignments = serializeAssignments(ctx, node.i, node.d); | ||
if (assignments) { | ||
const ref = getRefParam(ctx, node.i); | ||
return `(${assignRef(ctx, node.i, serialized)},${assignments}${ref})`; | ||
serialize(node: SerovalNode): string { | ||
switch (node.t) { | ||
case SerovalNodeType.Primitive: | ||
return String(node.s); | ||
case SerovalNodeType.BigInt: | ||
return node.s; | ||
case SerovalNodeType.Reference: | ||
return getRefParam(this.ctx, node.i); | ||
case SerovalNodeType.Array: | ||
return this.serializeArray(node); | ||
case SerovalNodeType.Object: | ||
return this.assignRef(node.i, this.serializeObject(node.i, node.d)); | ||
case SerovalNodeType.NullConstructor: | ||
return this.serializeNullConstructor(node); | ||
case SerovalNodeType.Date: | ||
return this.assignRef(node.i, `new Date("${node.s}")`); | ||
case SerovalNodeType.RegExp: | ||
return this.assignRef(node.i, node.s); | ||
case SerovalNodeType.Set: | ||
return this.serializeSet(node); | ||
case SerovalNodeType.Map: | ||
return this.serializeMap(node); | ||
case SerovalNodeType.BigIntTypedArray: | ||
case SerovalNodeType.TypedArray: | ||
return this.serializeTypedArray(node); | ||
case SerovalNodeType.AggregateError: | ||
return this.serializeAggregateError(node); | ||
case SerovalNodeType.Error: | ||
return this.serializeError(node); | ||
case SerovalNodeType.Iterable: | ||
return this.serializeIterable(node); | ||
case SerovalNodeType.Promise: | ||
return this.serializePromise(node); | ||
default: | ||
throw new Error('Unsupported type'); | ||
} | ||
} | ||
return assignRef(ctx, node.i, serialized); | ||
} | ||
export default function serializeTree( | ||
ctx: SerializationContext, | ||
node: SerovalNode, | ||
): string { | ||
switch (node.t) { | ||
case SerovalNodeType.Primitive: | ||
return String(node.s); | ||
case SerovalNodeType.Reference: | ||
return getRefParam(ctx, node.i); | ||
case SerovalNodeType.Promise: | ||
return serializePromise(ctx, node); | ||
case SerovalNodeType.BigInt: | ||
return node.s; | ||
case SerovalNodeType.Date: | ||
return assignRef(ctx, node.i, `new Date("${node.s}")`); | ||
case SerovalNodeType.RegExp: | ||
return assignRef(ctx, node.i, node.s); | ||
case SerovalNodeType.BigIntTypedArray: | ||
case SerovalNodeType.TypedArray: | ||
return serializeTypedArray(ctx, node); | ||
case SerovalNodeType.Set: | ||
return serializeSet(ctx, node); | ||
case SerovalNodeType.Map: | ||
return serializeMap(ctx, node); | ||
case SerovalNodeType.Array: | ||
return serializeArray(ctx, node); | ||
case SerovalNodeType.AggregateError: | ||
return serializeAggregateError(ctx, node); | ||
case SerovalNodeType.Error: | ||
return serializeError(ctx, node); | ||
case SerovalNodeType.Iterable: | ||
return serializeIterable(ctx, node); | ||
case SerovalNodeType.NullConstructor: | ||
return serializeNullConstructor(ctx, node); | ||
case SerovalNodeType.Object: | ||
return assignRef(ctx, node.i, serializeObject(ctx, node.i, node.d)); | ||
default: | ||
throw new Error('Unsupported type'); | ||
} | ||
} |
@@ -1,2 +0,1 @@ | ||
import { SerializationContext } from '../context'; | ||
import { | ||
@@ -6,7 +5,2 @@ AsyncServerValue, | ||
} from '../types'; | ||
import { | ||
SerovalNode, | ||
SerovalReferenceNode, | ||
SerovalNodeType, | ||
} from './types'; | ||
@@ -70,9 +64,2 @@ export function getErrorConstructor(error: ErrorValue) { | ||
export function isReferenceInStack( | ||
ctx: SerializationContext, | ||
node: SerovalNode, | ||
): node is SerovalReferenceNode { | ||
return node.t === SerovalNodeType.Reference && ctx.stack.includes(node.i); | ||
} | ||
export function isIterable( | ||
@@ -79,0 +66,0 @@ value: unknown, |
@@ -8,5 +8,10 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ | ||
import { | ||
createBigIntNode, | ||
createBigIntTypedArrayNode, | ||
createDateNode, | ||
createPrimitiveNode, | ||
createReferenceNode, | ||
createRegExpNode, | ||
createTypedArrayNode, | ||
FALSE_NODE, | ||
INFINITY_NODE, | ||
NEG_INFINITY_NODE, | ||
NEG_ZERO_NODE, | ||
@@ -26,9 +31,5 @@ NULL_NODE, | ||
SerovalArrayNode, | ||
SerovalBigIntNode, | ||
SerovalBigIntTypedArrayNode, | ||
SerovalDateNode, | ||
SerovalErrorNode, | ||
SerovalIterableNode, | ||
SerovalMapNode, | ||
SerovalMapRecordNode, | ||
SerovalNode, | ||
@@ -39,7 +40,3 @@ SerovalNodeType, | ||
SerovalObjectRecordNode, | ||
SerovalPrimitiveNode, | ||
SerovalReferenceNode, | ||
SerovalRegExpNode, | ||
SerovalSetNode, | ||
SerovalTypedArrayNode, | ||
} from './types'; | ||
@@ -80,3 +77,13 @@ | ||
generateArrayNode(id: number, current: ServerValue[]): SerovalArrayNode { | ||
return new SerovalArrayNode(id, this.generateNodeList(current)); | ||
return { | ||
t: SerovalNodeType.Array, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: this.generateNodeList(current), | ||
n: undefined, | ||
}; | ||
} | ||
@@ -112,3 +119,13 @@ | ||
} | ||
return new SerovalMapNode(id, new SerovalMapRecordNode(keyNodes, valueNodes, len)); | ||
return { | ||
t: SerovalNodeType.Map, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: { k: keyNodes, v: valueNodes, s: len }, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -138,3 +155,13 @@ | ||
} | ||
return new SerovalSetNode(id, nodes); | ||
return { | ||
t: SerovalNodeType.Set, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: undefined, | ||
a: nodes, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -181,10 +208,16 @@ | ||
const options = getIterableOptions(current); | ||
return new SerovalIterableNode( | ||
id, | ||
return { | ||
t: SerovalNodeType.Iterable, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
// Parse options first before the items | ||
options | ||
d: options | ||
? this.generateProperties(options as Record<string, ServerValue>) | ||
: undefined, | ||
this.generateNodeList(Array.from(current)), | ||
); | ||
a: this.generateNodeList(Array.from(current)), | ||
n: undefined, | ||
}; | ||
} | ||
@@ -200,6 +233,13 @@ | ||
} | ||
if (empty) { | ||
return new SerovalNullConstructorNode(id, this.generateProperties(current)); | ||
} | ||
return new SerovalObjectNode(id, this.generateProperties(current)); | ||
return { | ||
t: empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: undefined, | ||
d: this.generateProperties(current), | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -215,8 +255,13 @@ | ||
: undefined; | ||
return new SerovalAggregateErrorNode( | ||
id, | ||
current.message, | ||
optionsNode, | ||
this.parse(current.errors), | ||
); | ||
return { | ||
t: SerovalNodeType.AggregateError, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: undefined, | ||
m: current.message, | ||
d: optionsNode, | ||
a: undefined, | ||
n: this.parse(current.errors), | ||
}; | ||
} | ||
@@ -232,3 +277,13 @@ | ||
: undefined; | ||
return new SerovalErrorNode(id, getErrorConstructor(current), current.message, optionsNode); | ||
return { | ||
t: SerovalNodeType.Error, | ||
i: id, | ||
s: undefined, | ||
l: undefined, | ||
c: getErrorConstructor(current), | ||
m: current.message, | ||
d: optionsNode, | ||
a: undefined, | ||
n: undefined, | ||
}; | ||
} | ||
@@ -243,3 +298,3 @@ | ||
case 'string': | ||
return new SerovalPrimitiveNode(quote(current)); | ||
return createPrimitiveNode(quote(current)); | ||
case 'number': | ||
@@ -250,11 +305,10 @@ if (Object.is(current, -0)) { | ||
if (Object.is(current, Infinity)) { | ||
return INFINITY_NODE; | ||
return createPrimitiveNode('1/0'); | ||
} | ||
if (Object.is(current, -Infinity)) { | ||
return NEG_INFINITY_NODE; | ||
return createPrimitiveNode('-1/0'); | ||
} | ||
return new SerovalPrimitiveNode(current); | ||
return createPrimitiveNode(current); | ||
case 'bigint': | ||
assert(this.ctx.features & Feature.BigInt, 'Unsupported type "BigInt"'); | ||
return new SerovalBigIntNode(current); | ||
return createBigIntNode(this.ctx, current); | ||
case 'object': { | ||
@@ -267,4 +321,4 @@ if (!current) { | ||
const id = createRef(this.ctx, current, true); | ||
if (this.ctx.markedRefs[id]) { | ||
return new SerovalReferenceNode(id); | ||
if (this.ctx.markedRefs.has(id)) { | ||
return createReferenceNode(id); | ||
} | ||
@@ -276,5 +330,5 @@ if (Array.isArray(current)) { | ||
case Date: | ||
return new SerovalDateNode(id, current as Date); | ||
return createDateNode(id, current as Date); | ||
case RegExp: | ||
return new SerovalRegExpNode(id, current as RegExp); | ||
return createRegExpNode(id, current as RegExp); | ||
case Int8Array: | ||
@@ -289,11 +343,6 @@ case Int16Array: | ||
case Float64Array: | ||
assert(this.ctx.features & Feature.TypedArray, `Unsupported value type "${current.constructor.name}"`); | ||
return new SerovalTypedArrayNode(id, current as TypedArrayValue); | ||
return createTypedArrayNode(this.ctx, id, current as TypedArrayValue); | ||
case BigInt64Array: | ||
case BigUint64Array: | ||
assert( | ||
this.ctx.features & (Feature.BigIntTypedArray), | ||
`Unsupported value type "${current.constructor.name}"`, | ||
); | ||
return new SerovalBigIntTypedArrayNode(id, current as BigIntTypedArrayValue); | ||
return createBigIntTypedArrayNode(this.ctx, id, current as BigIntTypedArrayValue); | ||
case Map: | ||
@@ -300,0 +349,0 @@ return this.generateMapNode(id, current as Map<ServerValue, ServerValue>); |
@@ -1,3 +0,2 @@ | ||
/* eslint-disable max-classes-per-file */ | ||
import { BigIntTypedArrayValue, PrimitiveValue, TypedArrayValue } from '../types'; | ||
import { PrimitiveValue } from '../types'; | ||
@@ -25,47 +24,31 @@ export const enum SerovalNodeType { | ||
// Type of the node | ||
t?: SerovalNodeType; | ||
t: SerovalNodeType; | ||
// Serialized value | ||
s?: PrimitiveValue; | ||
s: PrimitiveValue | undefined; | ||
// Reference ID | ||
i?: number; | ||
i: number | undefined; | ||
// Size/Byte offset | ||
l?: number; | ||
l: number | undefined; | ||
// Constructor name | ||
c?: string; | ||
c: string | undefined; | ||
// dictionary | ||
d?: SerovalDictionaryNode; | ||
d: SerovalDictionaryNode | undefined; | ||
// message | ||
m?: string; | ||
m: string | undefined; | ||
// next node | ||
n?: SerovalNode; | ||
n: SerovalNode | undefined; | ||
// array of nodes | ||
a?: SerovalNode[]; | ||
a: SerovalNode[] | undefined; | ||
} | ||
export class SerovalObjectRecordNode { | ||
export interface SerovalObjectRecordNode { | ||
k: string[]; | ||
v: SerovalNode[]; | ||
s: number; | ||
constructor(k: string[], v: SerovalNode[], s: number) { | ||
this.k = k; | ||
this.v = v; | ||
this.s = s; | ||
} | ||
} | ||
export class SerovalMapRecordNode { | ||
export interface SerovalMapRecordNode { | ||
k: SerovalNode[]; | ||
v: SerovalNode[]; | ||
s: number; | ||
constructor(k: SerovalNode[], v: SerovalNode[], s: number) { | ||
this.k = k; | ||
this.v = v; | ||
this.s = s; | ||
} | ||
} | ||
@@ -77,101 +60,42 @@ | ||
export class SerovalPrimitiveNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Primitive = SerovalNodeType.Primitive; | ||
s: string | number | null; | ||
constructor(current: string | number | null) { | ||
this.s = current; | ||
} | ||
export interface SerovalPrimitiveNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Primitive; | ||
} | ||
export class SerovalReferenceNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Reference = SerovalNodeType.Reference; | ||
export interface SerovalReferenceNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Reference; | ||
i: number; | ||
constructor(id: number) { | ||
this.i = id; | ||
} | ||
} | ||
export class SerovalBigIntNode implements SerovalBaseNode { | ||
t: SerovalNodeType.BigInt = SerovalNodeType.BigInt; | ||
export interface SerovalBigIntNode extends SerovalBaseNode { | ||
t: SerovalNodeType.BigInt; | ||
s: string; | ||
constructor(current: bigint) { | ||
this.s = `${current}n`; | ||
} | ||
} | ||
export class SerovalDateNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Date = SerovalNodeType.Date; | ||
export interface SerovalDateNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Date; | ||
i: number; | ||
s: string; | ||
constructor(id: number, current: Date) { | ||
this.i = id; | ||
this.s = current.toISOString(); | ||
} | ||
} | ||
export class SerovalRegExpNode implements SerovalBaseNode { | ||
t: SerovalNodeType.RegExp = SerovalNodeType.RegExp; | ||
export interface SerovalRegExpNode extends SerovalBaseNode { | ||
t: SerovalNodeType.RegExp; | ||
i: number; | ||
s: string; | ||
constructor(id: number, current: RegExp) { | ||
this.i = id; | ||
this.s = String(current); | ||
} | ||
} | ||
export class SerovalTypedArrayNode implements SerovalBaseNode { | ||
t: SerovalNodeType.TypedArray = SerovalNodeType.TypedArray; | ||
export interface SerovalTypedArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.TypedArray; | ||
i: number; | ||
s: string; | ||
l: number; | ||
c: string; | ||
constructor(id: number, current: TypedArrayValue) { | ||
this.i = id; | ||
this.s = current.toString(); | ||
this.l = current.byteOffset; | ||
this.c = current.constructor.name; | ||
} | ||
} | ||
export class SerovalBigIntTypedArrayNode implements SerovalBaseNode { | ||
t: SerovalNodeType.BigIntTypedArray = SerovalNodeType.BigIntTypedArray; | ||
export interface SerovalBigIntTypedArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.BigIntTypedArray; | ||
i: number; | ||
s: string; | ||
l: number; | ||
c: string; | ||
constructor(id: number, current: BigIntTypedArrayValue) { | ||
let result = ''; | ||
const cap = current.length - 1; | ||
for (let i = 0; i < cap; i++) { | ||
result += `${current[i]}n,`; | ||
} | ||
result += `"${current[cap]}"`; | ||
this.i = id; | ||
this.s = result; | ||
this.l = current.byteOffset; | ||
this.c = current.constructor.name; | ||
} | ||
} | ||
@@ -186,132 +110,59 @@ | ||
export class SerovalSetNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Set = SerovalNodeType.Set; | ||
export interface SerovalSetNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Set; | ||
a: SerovalNode[]; | ||
i: number; | ||
a: SerovalNode[]; | ||
constructor(id: number, a: SerovalNode[]) { | ||
this.i = id; | ||
this.a = a; | ||
} | ||
} | ||
export class SerovalMapNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Map = SerovalNodeType.Map; | ||
export interface SerovalMapNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Map; | ||
d: SerovalMapRecordNode; | ||
i: number; | ||
d: SerovalMapRecordNode; | ||
constructor(id: number, d: SerovalMapRecordNode) { | ||
this.i = id; | ||
this.d = d; | ||
} | ||
} | ||
export class SerovalArrayNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Array = SerovalNodeType.Array; | ||
export interface SerovalArrayNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Array; | ||
a: SerovalNode[]; | ||
i: number; | ||
a: SerovalNode[]; | ||
constructor(id: number, a: SerovalNode[]) { | ||
this.i = id; | ||
this.a = a; | ||
} | ||
} | ||
export class SerovalObjectNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Object = SerovalNodeType.Object; | ||
export interface SerovalObjectNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Object; | ||
d: SerovalObjectRecordNode; | ||
i: number; | ||
} | ||
export interface SerovalNullConstructorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.NullConstructor; | ||
d: SerovalObjectRecordNode; | ||
constructor(id: number, d: SerovalObjectRecordNode) { | ||
this.i = id; | ||
this.d = d; | ||
} | ||
} | ||
export class SerovalNullConstructorNode implements SerovalBaseNode { | ||
t: SerovalNodeType.NullConstructor = SerovalNodeType.NullConstructor; | ||
i: number; | ||
d: SerovalObjectRecordNode; | ||
constructor(id: number, d: SerovalObjectRecordNode) { | ||
this.i = id; | ||
this.d = d; | ||
} | ||
} | ||
export class SerovalPromiseNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Promise = SerovalNodeType.Promise; | ||
export interface SerovalPromiseNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Promise; | ||
n: SerovalNode; | ||
i: number; | ||
n: SerovalNode; | ||
constructor(id: number, n: SerovalNode) { | ||
this.i = id; | ||
this.n = n; | ||
} | ||
} | ||
export class SerovalErrorNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Error = SerovalNodeType.Error; | ||
i: number; | ||
export interface SerovalErrorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Error; | ||
c: string; | ||
m: string; | ||
d: SerovalObjectRecordNode | undefined; | ||
constructor(id: number, c: string, m: string, d: SerovalObjectRecordNode | undefined) { | ||
this.i = id; | ||
this.c = c; | ||
this.m = m; | ||
this.d = d; | ||
} | ||
i: number; | ||
} | ||
export class SerovalAggregateErrorNode implements SerovalBaseNode { | ||
t: SerovalNodeType.AggregateError = SerovalNodeType.AggregateError; | ||
i: number; | ||
export interface SerovalAggregateErrorNode extends SerovalBaseNode { | ||
t: SerovalNodeType.AggregateError; | ||
m: string; | ||
d: SerovalObjectRecordNode | undefined; | ||
n: SerovalNode; | ||
constructor(id: number, m: string, d: SerovalObjectRecordNode | undefined, n: SerovalNode) { | ||
this.i = id; | ||
this.m = m; | ||
this.d = d; | ||
this.n = n; | ||
} | ||
i: number; | ||
} | ||
export class SerovalIterableNode implements SerovalBaseNode { | ||
t: SerovalNodeType.Iterable = SerovalNodeType.Iterable; | ||
i: number; | ||
export interface SerovalIterableNode extends SerovalBaseNode { | ||
t: SerovalNodeType.Iterable; | ||
d: SerovalObjectRecordNode | undefined; | ||
a: SerovalNode[]; | ||
constructor(id: number, d: SerovalObjectRecordNode | undefined, a: SerovalNode[]) { | ||
this.i = id; | ||
this.d = d; | ||
this.a = a; | ||
} | ||
i: number; | ||
} | ||
@@ -318,0 +169,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
447127
6190