Comparing version 0.4.0-alpha.3 to 0.4.0-alpha.4
@@ -22,15 +22,16 @@ /** | ||
export declare const enum Feature { | ||
AggregateError = 0, | ||
ArrayPrototypeValues = 1, | ||
ArrowFunction = 2, | ||
BigInt = 3, | ||
Map = 4, | ||
MethodShorthand = 5, | ||
ObjectAssign = 6, | ||
Promise = 7, | ||
Set = 8, | ||
SymbolIterator = 9, | ||
TypedArray = 10 | ||
AggregateError = 1, | ||
ArrayPrototypeValues = 2, | ||
ArrowFunction = 4, | ||
BigInt = 8, | ||
Map = 16, | ||
MethodShorthand = 32, | ||
ObjectAssign = 64, | ||
Promise = 128, | ||
Set = 256, | ||
SymbolIterator = 512, | ||
TypedArray = 1024, | ||
BigIntTypedArray = 2048 | ||
} | ||
export type Target = [platform: Platform, version: Version]; | ||
export declare function parseTargets(targets: string | string[]): Set<Feature>; | ||
export declare function parseTargets(targets: string | string[]): number; |
@@ -7,2 +7,6 @@ import { Options } from './tree'; | ||
export declare function deserialize<T extends AsyncServerValue>(source: string): T; | ||
export declare function toJSON<T extends ServerValue>(source: T, options?: Partial<Options>): string; | ||
export declare function toJSONAsync<T extends AsyncServerValue>(source: T, options?: Partial<Options>): Promise<string>; | ||
export declare function compileJSON(source: string): string; | ||
export declare function fromJSON<T extends AsyncServerValue>(source: string): T; | ||
export default serialize; |
@@ -1,3 +0,2 @@ | ||
import { AsyncServerValue, PrimitiveValue, ServerValue, TypedArrayValue } from './types'; | ||
import { Feature } from './compat'; | ||
import { AsyncServerValue, PrimitiveValue, ServerValue } from './types'; | ||
interface IndexAssignment { | ||
@@ -20,10 +19,14 @@ type: 'index'; | ||
export type Assignment = IndexAssignment | MapAssignment | SetAssignment; | ||
export interface ParserContext { | ||
refs: Map<unknown, number>; | ||
markedRefs: Set<number>; | ||
features: number; | ||
} | ||
export interface SerializationContext { | ||
stack: number[]; | ||
refs: Map<unknown, number>; | ||
validRefs: Map<number, number>; | ||
markedRefs: boolean[]; | ||
markedRefs: Set<number>; | ||
vars: string[]; | ||
assignments: Assignment[]; | ||
features: Set<Feature>; | ||
features: number; | ||
} | ||
@@ -33,9 +36,10 @@ export interface Options { | ||
} | ||
export declare function createSerializationContext(options?: Partial<Options>): SerializationContext; | ||
export declare function createParserContext(options?: Partial<Options>): ParserContext; | ||
export interface SerializationOptions { | ||
markedRefs: number[] | Set<number>; | ||
features: number; | ||
} | ||
export declare function createSerializationContext(options: SerializationOptions): SerializationContext; | ||
export declare function resolvePatches(ctx: SerializationContext): string | undefined; | ||
/** | ||
* Creates a reference ID from the given values | ||
*/ | ||
export declare function createRef(ctx: SerializationContext, index: unknown): number; | ||
/** | ||
* Creates the reference param (identifier) from the given reference ID | ||
@@ -47,22 +51,28 @@ * Calling this function means the value has been referenced somewhere | ||
Primitive = 0, | ||
Reference = 1, | ||
Date = 2, | ||
RegExp = 3, | ||
Set = 4, | ||
Map = 5, | ||
Array = 6, | ||
Object = 7, | ||
NullConstructor = 8, | ||
Promise = 9, | ||
Error = 10, | ||
AggregateError = 11, | ||
Iterable = 12, | ||
TypedArray = 13 | ||
BigInt = 1, | ||
Reference = 2, | ||
Date = 3, | ||
RegExp = 4, | ||
Set = 5, | ||
Map = 6, | ||
Array = 7, | ||
Object = 8, | ||
NullConstructor = 9, | ||
Promise = 10, | ||
Error = 11, | ||
AggregateError = 12, | ||
Iterable = 13, | ||
TypedArray = 14, | ||
BigIntTypedArray = 15 | ||
} | ||
type SerovalPrimitiveNode = [type: SerovalNodeType.Primitive, value: PrimitiveValue]; | ||
type SerovalReferenceNode = [type: SerovalNodeType.Reference, value: number]; | ||
type SerovalSemiPrimitiveNode = [type: SerovalNodeType.Date, value: Date, id: number] | [type: SerovalNodeType.RegExp, value: RegExp, id: number] | [ | ||
type SerovalSemiPrimitiveNode = [type: SerovalNodeType.BigInt, value: string] | [type: SerovalNodeType.Date, value: string, id: number] | [type: SerovalNodeType.RegExp, value: string, id: number] | [ | ||
type: SerovalNodeType.TypedArray, | ||
value: [constructor: string, array: TypedArrayValue], | ||
value: [constructor: string, array: string, byteOffset: number], | ||
id: number | ||
] | [ | ||
type: SerovalNodeType.BigIntTypedArray, | ||
value: [constructor: string, array: string, byteOffset: number], | ||
id: number | ||
]; | ||
@@ -110,7 +120,7 @@ type SerovalDictionaryNode = [key: string[], value: SerovalNode[], size: number]; | ||
]; | ||
type SerovalNode = SerovalPrimitiveNode | SerovalReferenceNode | SerovalSemiPrimitiveNode | SerovalSetNode | SerovalMapNode | SerovalArrayNode | SerovalObjectNode | SerovalNullConstructorNode | SerovalPromiseNode | SerovalErrorNode | SerovalAggregateErrorNode | SerovalIterableNode; | ||
export declare function generateTreeSync(ctx: SerializationContext, current: ServerValue): SerovalNode; | ||
export declare function generateTreeAsync(ctx: SerializationContext, current: AsyncServerValue): Promise<SerovalNode>; | ||
export declare function serializePrimitive(ctx: SerializationContext, value: PrimitiveValue): string; | ||
export type SerovalNode = SerovalPrimitiveNode | SerovalReferenceNode | SerovalSemiPrimitiveNode | SerovalSetNode | SerovalMapNode | SerovalArrayNode | SerovalObjectNode | SerovalNullConstructorNode | SerovalPromiseNode | SerovalErrorNode | SerovalAggregateErrorNode | SerovalIterableNode; | ||
export declare function parseSync(ctx: ParserContext, current: ServerValue): readonly [SerovalNode, number, boolean]; | ||
export declare function parseAsync(ctx: ParserContext, current: AsyncServerValue): Promise<readonly [SerovalNode, number, boolean]>; | ||
export declare function serializePrimitive(value: PrimitiveValue): string | number | null; | ||
export declare function serializeTree(ctx: SerializationContext, [type, value, id]: SerovalNode): string; | ||
export {}; |
@@ -1,5 +0,5 @@ | ||
export type PrimitiveValue = boolean | string | number | undefined | null | bigint; | ||
export type PrimitiveValue = boolean | string | number | undefined | null; | ||
export type ErrorValue = Error | AggregateError | EvalError | RangeError | ReferenceError | TypeError | SyntaxError | URIError; | ||
export type TypedArrayValue = Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array | BigInt64Array | BigUint64Array; | ||
export type SemiPrimitiveValue = RegExp | Date | TypedArrayValue; | ||
export type SemiPrimitiveValue = RegExp | Date | TypedArrayValue | bigint; | ||
export type CommonServerValue = PrimitiveValue | SemiPrimitiveValue | ErrorValue; | ||
@@ -6,0 +6,0 @@ export type ServerValue = CommonServerValue | Array<ServerValue> | readonly ServerValue[] | Iterable<ServerValue> | { |
{ | ||
"name": "seroval", | ||
"type": "module", | ||
"version": "0.4.0-alpha.3", | ||
"version": "0.4.0-alpha.4", | ||
"files": [ | ||
@@ -67,3 +67,3 @@ "dist", | ||
}, | ||
"gitHead": "22da3fb0f57fdb4a935cac70d1b9a7e5fdc02816" | ||
"gitHead": "1e96ac1ab8304fb09ac7a1bc451b7768f12b7f6b" | ||
} |
@@ -9,4 +9,3 @@ import { AsyncServerValue, PrimitiveValue } from './types'; | ||
return type === 'number' | ||
|| type === 'string' | ||
|| type === 'bigint'; | ||
|| type === 'string'; | ||
} | ||
@@ -13,0 +12,0 @@ |
@@ -43,13 +43,14 @@ /* eslint-disable guard-for-in */ | ||
export const enum Feature { | ||
AggregateError, | ||
ArrayPrototypeValues, | ||
ArrowFunction, | ||
BigInt, | ||
Map, | ||
MethodShorthand, | ||
ObjectAssign, | ||
Promise, | ||
Set, | ||
SymbolIterator, | ||
TypedArray, | ||
AggregateError = 0x01, | ||
ArrayPrototypeValues = 0x02, | ||
ArrowFunction = 0x04, | ||
BigInt = 0x08, | ||
Map = 0x10, | ||
MethodShorthand = 0x20, | ||
ObjectAssign = 0x40, | ||
Promise = 0x80, | ||
Set = 0x100, | ||
SymbolIterator = 0x200, | ||
TypedArray = 0x400, | ||
BigIntTypedArray = 0x800, | ||
} | ||
@@ -205,2 +206,15 @@ | ||
}, | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array#browser_compatibility | ||
[Feature.BigIntTypedArray]: { | ||
es: [2020, 0, 0], | ||
chrome: [67, 0, 0], | ||
edge: [79, 0, 0], | ||
firefox: [68, 0, 0], | ||
opera: [54, 6, 0], | ||
safari: [15, 1, 0], | ||
ios: [15, 2, 0], | ||
samsung: [9, 0, 0], | ||
deno: [1, 0, 0], | ||
node: [10, 4, 0], | ||
}, | ||
}; | ||
@@ -231,5 +245,5 @@ | ||
export function parseTargets(targets: string | string[]): Set<Feature> { | ||
export function parseTargets(targets: string | string[]): number { | ||
const parsed = getTargetVersions(targets); | ||
const flags = new Set<Feature>(); | ||
let flags = 0; | ||
@@ -244,3 +258,3 @@ for (const key in VERSION_TABLE) { | ||
if (flag) { | ||
flags.add(Number(key) as unknown as Feature); | ||
flags |= Number(key); | ||
} | ||
@@ -247,0 +261,0 @@ } |
@@ -5,8 +5,8 @@ /* eslint-disable no-await-in-loop */ | ||
import { | ||
createRef, | ||
createParserContext, | ||
createSerializationContext, | ||
generateTreeAsync, | ||
generateTreeSync, | ||
getRefParam, | ||
Options, | ||
parseAsync, | ||
parseSync, | ||
resolvePatches, | ||
@@ -16,6 +16,6 @@ SerializationContext, | ||
serializeTree, | ||
SerovalNode, | ||
} from './tree'; | ||
import { | ||
AsyncServerValue, | ||
NonPrimitiveServerValue, | ||
PrimitiveValue, | ||
@@ -37,5 +37,6 @@ ServerValue, | ||
function finalize<T extends NonPrimitiveServerValue<ServerValue | AsyncServerValue>>( | ||
function finalize( | ||
ctx: SerializationContext, | ||
source: T, | ||
rootID: number, | ||
isObject: boolean, | ||
result: string, | ||
@@ -49,3 +50,3 @@ ) { | ||
// Get (or create) a ref from the source | ||
const index = getRefParam(ctx, createRef(ctx, source)); | ||
const index = getRefParam(ctx, rootID); | ||
if (result.startsWith(`${index}=`)) { | ||
@@ -61,3 +62,3 @@ body = `${result},${patches}${index}`; | ||
// Source is probably already assigned | ||
if (ctx.features.has(Feature.ArrowFunction)) { | ||
if (ctx.features & Feature.ArrowFunction) { | ||
params = ctx.vars.length > 1 || ctx.vars.length === 0 | ||
@@ -70,3 +71,3 @@ ? `(${params})` | ||
} | ||
if (source.constructor === Object) { | ||
if (isObject) { | ||
return `(${result})`; | ||
@@ -81,9 +82,10 @@ } | ||
) { | ||
const ctx = createSerializationContext(options); | ||
const ctx = createParserContext(options); | ||
if (isPrimitive(source)) { | ||
return serializePrimitive(ctx, source); | ||
return String(serializePrimitive(source)); | ||
} | ||
const tree = generateTreeSync(ctx, source); | ||
const result = serializeTree(ctx, tree); | ||
return finalize(ctx, source, result); | ||
const [tree, rootID, isObject] = parseSync(ctx, source); | ||
const serial = createSerializationContext(ctx); | ||
const result = serializeTree(serial, tree); | ||
return finalize(serial, rootID, isObject, result); | ||
} | ||
@@ -95,9 +97,10 @@ | ||
) { | ||
const ctx = createSerializationContext(options); | ||
const ctx = createParserContext(options); | ||
if (isPrimitive(source)) { | ||
return serializePrimitive(ctx, source); | ||
return String(serializePrimitive(source)); | ||
} | ||
const tree = await generateTreeAsync(ctx, source); | ||
const result = serializeTree(ctx, tree); | ||
return finalize(ctx, source, result); | ||
const [tree, rootID, isObject] = await parseAsync(ctx, source); | ||
const serial = createSerializationContext(ctx); | ||
const result = serializeTree(serial, tree); | ||
return finalize(serial, rootID, isObject, result); | ||
} | ||
@@ -110,2 +113,54 @@ | ||
type SerovalJSON = [ | ||
tree: SerovalNode, | ||
root: number, | ||
isObject: boolean, | ||
feature: number, | ||
markedRefs: number[], | ||
]; | ||
export function toJSON<T extends ServerValue>( | ||
source: T, | ||
options?: Partial<Options>, | ||
) { | ||
const ctx = createParserContext(options); | ||
const [tree, root, isObject] = parseSync(ctx, source); | ||
return JSON.stringify([ | ||
tree, | ||
root, | ||
isObject, | ||
ctx.features, | ||
[...ctx.markedRefs], | ||
]); | ||
} | ||
export async function toJSONAsync<T extends AsyncServerValue>( | ||
source: T, | ||
options?: Partial<Options>, | ||
) { | ||
const ctx = createParserContext(options); | ||
const [tree, root, isObject] = await parseAsync(ctx, source); | ||
return JSON.stringify([ | ||
tree, | ||
root, | ||
isObject, | ||
ctx.features, | ||
[...ctx.markedRefs], | ||
]); | ||
} | ||
export function compileJSON(source: string): string { | ||
const parsed = JSON.parse(source) as SerovalJSON; | ||
const serial = createSerializationContext({ | ||
features: parsed[3], | ||
markedRefs: parsed[4], | ||
}); | ||
const result = serializeTree(serial, parsed[0]); | ||
return finalize(serial, parsed[1], parsed[2], result); | ||
} | ||
export function fromJSON<T extends AsyncServerValue>(source: string): T { | ||
return deserialize<T>(compileJSON(source)); | ||
} | ||
export default serialize; |
318
src/tree.ts
@@ -17,3 +17,2 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ | ||
ServerValue, | ||
TypedArrayValue, | ||
} from './types'; | ||
@@ -49,10 +48,14 @@ import getIdentifier from './get-identifier'; | ||
export interface ParserContext { | ||
refs: Map<unknown, number>; | ||
markedRefs: Set<number>; | ||
features: number; | ||
} | ||
export interface SerializationContext { | ||
stack: number[]; | ||
// Value-to-ref map | ||
refs: Map<unknown, number>; | ||
// Map tree refs to actual refs | ||
validRefs: Map<number, number>; | ||
// Refs that are...referenced | ||
markedRefs: boolean[]; | ||
markedRefs: Set<number>; | ||
// Variables | ||
@@ -63,3 +66,3 @@ vars: string[]; | ||
// Supported features | ||
features: Set<Feature>; | ||
features: number; | ||
} | ||
@@ -75,13 +78,25 @@ | ||
export function createSerializationContext(options: Partial<Options> = {}): SerializationContext { | ||
export function createParserContext(options: Partial<Options> = {}): ParserContext { | ||
// eslint-disable-next-line prefer-object-spread | ||
const result = Object.assign({}, DEFAULT_OPTIONS, options || {}); | ||
return { | ||
markedRefs: [], | ||
markedRefs: new Set(), | ||
refs: new Map(), | ||
features: parseTargets(result.target), | ||
}; | ||
} | ||
export interface SerializationOptions { | ||
markedRefs: number[] | Set<number>; | ||
features: number; | ||
} | ||
export function createSerializationContext(options: SerializationOptions): SerializationContext { | ||
return { | ||
stack: [], | ||
refs: new Map(), | ||
vars: [], | ||
assignments: [], | ||
validRefs: new Map(), | ||
features: parseTargets(result.target), | ||
features: options.features, | ||
markedRefs: new Set(options.markedRefs), | ||
}; | ||
@@ -169,23 +184,7 @@ } | ||
*/ | ||
function markRef(ctx: SerializationContext, current: number) { | ||
ctx.markedRefs[current] = true; | ||
function markRef(ctx: ParserContext | SerializationContext, current: number) { | ||
ctx.markedRefs.add(current); | ||
} | ||
/** | ||
* Creates a reference ID from the given values | ||
*/ | ||
export function createRef( | ||
ctx: SerializationContext, | ||
index: unknown, | ||
) { | ||
const current = ctx.refs.get(index); | ||
if (current != null) { | ||
return current; | ||
} | ||
const id = ctx.refs.size; | ||
ctx.refs.set(index, id); | ||
return id; | ||
} | ||
/** | ||
* Creates a new reference ID from a given reference ID | ||
@@ -233,3 +232,3 @@ * This new reference ID means that the reference itself | ||
) { | ||
if (ctx.markedRefs[index]) { | ||
if (ctx.markedRefs.has(index)) { | ||
return `${getRefParam(ctx, index)}=${value}`; | ||
@@ -240,2 +239,18 @@ } | ||
/** | ||
* Creates a reference ID from the given values | ||
*/ | ||
function createRef( | ||
ctx: ParserContext, | ||
index: unknown, | ||
) { | ||
const current = ctx.refs.get(index); | ||
if (current != null) { | ||
return current; | ||
} | ||
const id = ctx.refs.size; | ||
ctx.refs.set(index, id); | ||
return id; | ||
} | ||
function createAssignment( | ||
@@ -291,3 +306,3 @@ ctx: SerializationContext, | ||
function createObjectIdentifierAssign( | ||
function createObjectAssign( | ||
ctx: SerializationContext, | ||
@@ -297,17 +312,9 @@ ref: number, | ||
value: string, | ||
computed: boolean, | ||
) { | ||
markRef(ctx, ref); | ||
createAssignment(ctx, `${getRefParam(ctx, ref)}.${key}`, value); | ||
const member = computed ? `[${key}]` : `.${key}`; | ||
createAssignment(ctx, `${getRefParam(ctx, ref)}${member}`, value); | ||
} | ||
function createObjectComputedAssign( | ||
ctx: SerializationContext, | ||
ref: number, | ||
key: string, | ||
value: string, | ||
) { | ||
markRef(ctx, ref); | ||
createAssignment(ctx, `${getRefParam(ctx, ref)}[${key}]`, value); | ||
} | ||
function getErrorConstructor(error: ErrorValue) { | ||
@@ -372,2 +379,3 @@ if (error instanceof EvalError) { | ||
Primitive, | ||
BigInt, | ||
Reference, | ||
@@ -386,2 +394,3 @@ Date, | ||
TypedArray, | ||
BigIntTypedArray, | ||
} | ||
@@ -392,8 +401,14 @@ | ||
type SerovalSemiPrimitiveNode = | ||
| [type: SerovalNodeType.Date, value: Date, id: number] | ||
| [type: SerovalNodeType.RegExp, value: RegExp, id: number] | ||
| [type: SerovalNodeType.BigInt, value: string] | ||
| [type: SerovalNodeType.Date, value: string, id: number] | ||
| [type: SerovalNodeType.RegExp, value: string, id: number] | ||
| [ | ||
type: SerovalNodeType.TypedArray, | ||
value: [constructor: string, array: TypedArrayValue], | ||
value: [constructor: string, array: string, byteOffset: number], | ||
id: number | ||
] | ||
| [ | ||
type: SerovalNodeType.BigIntTypedArray, | ||
value: [constructor: string, array: string, byteOffset: number], | ||
id: number | ||
]; | ||
@@ -444,3 +459,3 @@ | ||
type SerovalNode = | ||
export type SerovalNode = | ||
| SerovalPrimitiveNode | ||
@@ -467,3 +482,3 @@ | SerovalReferenceNode | ||
function generateRef( | ||
ctx: SerializationContext, | ||
ctx: ParserContext, | ||
current: unknown, | ||
@@ -488,63 +503,85 @@ ): number | SerovalReferenceNode { | ||
function generateSemiPrimitiveValue( | ||
ctx: SerializationContext, | ||
ctx: ParserContext, | ||
current: unknown, | ||
id: number, | ||
): SerovalNode | undefined { | ||
if (typeof current === 'bigint') { | ||
assert(ctx.features & Feature.BigInt, 'Unsupported type "BigInt"'); | ||
return [SerovalNodeType.BigInt, `${current}n`]; | ||
} | ||
if (constructorCheck<Date>(current, Date)) { | ||
return [SerovalNodeType.Date, current, id]; | ||
return [SerovalNodeType.Date, current.toISOString(), id]; | ||
} | ||
if (constructorCheck<RegExp>(current, RegExp)) { | ||
return [SerovalNodeType.RegExp, current, id]; | ||
return [SerovalNodeType.RegExp, String(current), id]; | ||
} | ||
if (constructorCheck<Int8Array>(current, Int8Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Int8Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int8Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Int8Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int8Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Int16Array>(current, Int16Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Int16Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int16Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Int16Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int16Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Int32Array>(current, Int32Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Int32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int32Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Int32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Int32Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Uint8Array>(current, Uint8Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Uint8Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint8Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Uint8Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint8Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Uint16Array>(current, Uint16Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Uint16Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint16Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Uint16Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint16Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Uint32Array>(current, Uint32Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Uint32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint32Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Uint32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Uint32Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Uint8ClampedArray>(current, Uint8ClampedArray)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Uint8ClampedArray"'); | ||
return [SerovalNodeType.TypedArray, ['Uint8ClampedArray', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Uint8ClampedArray"'); | ||
return [SerovalNodeType.TypedArray, ['Uint8ClampedArray', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Float32Array>(current, Float32Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Float32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Float32Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Float32Array"'); | ||
return [SerovalNodeType.TypedArray, ['Float32Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<Float64Array>(current, Float64Array)) { | ||
assert(ctx.features.has(Feature.TypedArray), 'Unsupported value type "Float64Array"'); | ||
return [SerovalNodeType.TypedArray, ['Float64Array', current], id]; | ||
assert(ctx.features & Feature.TypedArray, 'Unsupported value type "Float64Array"'); | ||
return [SerovalNodeType.TypedArray, ['Float64Array', current.toString(), current.byteOffset], id]; | ||
} | ||
if (constructorCheck<BigInt64Array>(current, BigInt64Array)) { | ||
assert( | ||
ctx.features.has(Feature.TypedArray) | ||
&& ctx.features.has(Feature.BigInt), | ||
ctx.features & (Feature.BigIntTypedArray), | ||
'Unsupported value type "BigInt64Array"', | ||
); | ||
return [SerovalNodeType.TypedArray, ['BigInt64Array', current], id]; | ||
let result = ''; | ||
const cap = current.length - 1; | ||
for (let i = 0; i < cap; i++) { | ||
result += `${current[i]}n,`; | ||
} | ||
result += `"${current[cap]}"`; | ||
return [ | ||
SerovalNodeType.BigIntTypedArray, | ||
['BigInt64Array', result, current.byteOffset], | ||
id, | ||
]; | ||
} | ||
if (constructorCheck<BigUint64Array>(current, BigUint64Array)) { | ||
assert( | ||
ctx.features.has(Feature.TypedArray) | ||
&& ctx.features.has(Feature.BigInt), | ||
ctx.features & (Feature.BigIntTypedArray), | ||
'Unsupported value type "BigUint64Array"', | ||
); | ||
return [SerovalNodeType.TypedArray, ['BigUint64Array', current], id]; | ||
let result = ''; | ||
const cap = current.length - 1; | ||
for (let i = 0; i < cap; i++) { | ||
result += `"${current[i]}n",`; | ||
} | ||
result += `"${current[cap]}"`; | ||
return [ | ||
SerovalNodeType.BigIntTypedArray, | ||
['BigInt64Array', result, current.byteOffset], | ||
id, | ||
]; | ||
} | ||
@@ -555,3 +592,3 @@ return undefined; | ||
function serializePropertiesSync( | ||
ctx: SerializationContext, | ||
ctx: ParserContext, | ||
properties: Record<string, unknown>, | ||
@@ -585,8 +622,8 @@ ): SerovalDictionaryNode { | ||
export function generateTreeSync( | ||
ctx: SerializationContext, | ||
function generateTreeSync( | ||
ctx: ParserContext, | ||
current: ServerValue, | ||
): SerovalNode { | ||
if (isPrimitive(current)) { | ||
return [SerovalNodeType.Primitive, current]; | ||
return [SerovalNodeType.Primitive, serializePrimitive(current)]; | ||
} | ||
@@ -604,3 +641,3 @@ // Non-primitive values needs a reference ID | ||
if (constructorCheck<Set<ServerValue>>(current, Set)) { | ||
assert(ctx.features.has(Feature.Set), 'Unsupported type "Set"'); | ||
assert(ctx.features & Feature.Set, 'Unsupported type "Set"'); | ||
const len = current.size; | ||
@@ -626,3 +663,3 @@ const nodes = new Array<SerovalNode>(len); | ||
if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) { | ||
assert(ctx.features.has(Feature.Map), 'Unsupported type "Map"'); | ||
assert(ctx.features & Feature.Map, 'Unsupported type "Map"'); | ||
const len = current.size; | ||
@@ -673,3 +710,3 @@ const keyNodes = new Array<SerovalNode>(len); | ||
} | ||
if (current instanceof AggregateError && ctx.features.has(Feature.AggregateError)) { | ||
if (current instanceof AggregateError && ctx.features & Feature.AggregateError) { | ||
const options = getErrorOptions(current); | ||
@@ -694,3 +731,3 @@ const optionsNode = options | ||
if (isIterable(current)) { | ||
assert(ctx.features.has(Feature.SymbolIterator), 'Unsupported type "Iterable"'); | ||
assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"'); | ||
const options = getIterableOptions(current); | ||
@@ -714,4 +751,12 @@ return [SerovalNodeType.Iterable, [ | ||
export function parseSync( | ||
ctx: ParserContext, | ||
current: ServerValue, | ||
) { | ||
const result = generateTreeSync(ctx, current); | ||
return [result, createRef(ctx, current), result[0] === SerovalNodeType.Object] as const; | ||
} | ||
async function serializePropertiesAsync( | ||
ctx: SerializationContext, | ||
ctx: ParserContext, | ||
properties: Record<string, unknown>, | ||
@@ -745,8 +790,8 @@ ): Promise<SerovalDictionaryNode> { | ||
export async function generateTreeAsync( | ||
ctx: SerializationContext, | ||
async function generateTreeAsync( | ||
ctx: ParserContext, | ||
current: AsyncServerValue, | ||
): Promise<SerovalNode> { | ||
if (isPrimitive(current)) { | ||
return [SerovalNodeType.Primitive, current]; | ||
return [SerovalNodeType.Primitive, serializePrimitive(current)]; | ||
} | ||
@@ -762,3 +807,3 @@ const id = generateRef(ctx, current); | ||
if (isPromise(current)) { | ||
assert(ctx.features.has(Feature.Promise), 'Unsupported type "Promise"'); | ||
assert(ctx.features & Feature.Promise, 'Unsupported type "Promise"'); | ||
return current.then(async (value) => [ | ||
@@ -771,3 +816,3 @@ SerovalNodeType.Promise, | ||
if (constructorCheck<Set<AsyncServerValue>>(current, Set)) { | ||
assert(ctx.features.has(Feature.Set), 'Unsupported type "Set"'); | ||
assert(ctx.features & Feature.Set, 'Unsupported type "Set"'); | ||
const len = current.size; | ||
@@ -793,3 +838,3 @@ const nodes = new Array<SerovalNode>(len); | ||
if (constructorCheck<Map<AsyncServerValue, AsyncServerValue>>(current, Map)) { | ||
assert(ctx.features.has(Feature.Map), 'Unsupported type "Map"'); | ||
assert(ctx.features & Feature.Map, 'Unsupported type "Map"'); | ||
const len = current.size; | ||
@@ -841,3 +886,3 @@ const keyNodes = new Array<SerovalNode>(len); | ||
} | ||
if (current instanceof AggregateError) { | ||
if (current instanceof AggregateError && ctx.features & Feature.AggregateError) { | ||
const options = getErrorOptions(current); | ||
@@ -862,3 +907,3 @@ const optionsNode = options | ||
if (isIterable(current)) { | ||
assert(ctx.features.has(Feature.SymbolIterator), 'Unsupported type "Iterable"'); | ||
assert(ctx.features & Feature.SymbolIterator, 'Unsupported type "Iterable"'); | ||
const options = getIterableOptions(current); | ||
@@ -883,6 +928,13 @@ return [SerovalNodeType.Iterable, [ | ||
export async function parseAsync( | ||
ctx: ParserContext, | ||
current: AsyncServerValue, | ||
) { | ||
const result = await generateTreeAsync(ctx, current); | ||
return [result, createRef(ctx, current), result[0] === SerovalNodeType.Object] as const; | ||
} | ||
export function serializePrimitive( | ||
ctx: SerializationContext, | ||
value: PrimitiveValue, | ||
): string { | ||
): string | number | null { | ||
// Shortened forms | ||
@@ -899,8 +951,4 @@ if (value === true) { | ||
if (value === null) { | ||
return 'null'; | ||
return null; | ||
} | ||
if (typeof value === 'bigint') { | ||
assert(ctx.features.has(Feature.BigInt), 'Unsupported type "BigInt"'); | ||
return `${value}n`; | ||
} | ||
if (typeof value === 'string') { | ||
@@ -919,3 +967,3 @@ return quote(value); | ||
} | ||
return String(value); | ||
return value; | ||
} | ||
@@ -943,11 +991,10 @@ | ||
// so that we don't have to serialize the key and wrap with brackets | ||
if (IDENTIFIER_CHECK.test(key) || check >= 0) { | ||
if (!Number.isNaN(check)) { | ||
createObjectComputedAssign(ctx, targetRef, key, refParam); | ||
} else { | ||
createObjectIdentifierAssign(ctx, targetRef, key, refParam); | ||
} | ||
} else { | ||
createObjectComputedAssign(ctx, targetRef, quote(key), refParam); | ||
} | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
createObjectAssign( | ||
ctx, | ||
targetRef, | ||
isIdentifier ? key : quote(key), | ||
refParam, | ||
!(isIdentifier && Number.isNaN(check)), | ||
); | ||
ctx.assignments = parentAssignment; | ||
@@ -972,18 +1019,15 @@ } | ||
// so that we don't have to serialize the key and wrap with brackets | ||
const isIdentifier = IDENTIFIER_CHECK.test(key) || check >= 0; | ||
const isIdentifier = check >= 0 || IDENTIFIER_CHECK.test(key); | ||
const validKey = isIdentifier ? key : quote(key); | ||
if (isReferenceInStack(ctx, val)) { | ||
const refParam = getRefParam(ctx, val[1]); | ||
if (isIdentifier) { | ||
if (!Number.isNaN(check)) { | ||
createObjectComputedAssign(ctx, sourceID, key, refParam); | ||
} else { | ||
createObjectIdentifierAssign(ctx, sourceID, key, refParam); | ||
} | ||
} else { | ||
createObjectComputedAssign(ctx, sourceID, quote(key), refParam); | ||
} | ||
} else if (isIdentifier) { | ||
result += `${key}:${serializeTree(ctx, val)},`; | ||
createObjectAssign( | ||
ctx, | ||
sourceID, | ||
validKey, | ||
refParam, | ||
!(isIdentifier && Number.isNaN(check)), | ||
); | ||
} else { | ||
result += `${quote(key)}:${serializeTree(ctx, val)},`; | ||
result += `${validKey}:${serializeTree(ctx, val)},`; | ||
} | ||
@@ -1001,3 +1045,3 @@ } | ||
case SerovalNodeType.Primitive: | ||
return serializePrimitive(ctx, value); | ||
return String(value); | ||
case SerovalNodeType.Reference: | ||
@@ -1023,16 +1067,14 @@ return getRefParam(ctx, value); | ||
} | ||
case SerovalNodeType.BigInt: | ||
return value; | ||
case SerovalNodeType.Date: | ||
return assignRef(ctx, id, `new Date("${value.toISOString()}")`); | ||
return assignRef(ctx, id, `new Date("${value}")`); | ||
case SerovalNodeType.RegExp: | ||
return assignRef(ctx, id, String(value)); | ||
return assignRef(ctx, id, value); | ||
case SerovalNodeType.BigIntTypedArray: | ||
case SerovalNodeType.TypedArray: { | ||
// BigInt typed arrays are broken for toString() | ||
let values = ''; | ||
for (let i = 0, len = value[1].length; i < len; i++) { | ||
values += `${serializePrimitive(ctx, value[1][i])},`; | ||
let args = `[${value[1]}]`; | ||
if (value[2] !== 0) { | ||
args += `,${value[2]}`; | ||
} | ||
let args = values ? `[${values.substring(0, values.length - 1)}]` : '[]'; | ||
if (value[1].byteOffset !== 0) { | ||
args += `,${value[1].byteOffset}`; | ||
} | ||
return assignRef(ctx, id, `new ${value[0]}(${args})`); | ||
@@ -1125,3 +1167,3 @@ } | ||
// Check if index is a hole | ||
if (i in value) { | ||
if (item) { | ||
// Check if item is a parent | ||
@@ -1154,3 +1196,3 @@ if (isReferenceInStack(ctx, item)) { | ||
if (value[1]) { | ||
if (ctx.features.has(Feature.ObjectAssign)) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
const options = serializeObject(ctx, id, value[1]); | ||
@@ -1172,3 +1214,3 @@ serialized = `Object.assign(${serialized},${options})`; | ||
if (value[2]) { | ||
if (ctx.features.has(Feature.ObjectAssign)) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
const options = serializeObject(ctx, id, value[2]); | ||
@@ -1193,3 +1235,3 @@ serialized = `Object.assign(${serialized},${options})`; | ||
let serialized: string; | ||
if (ctx.features.has(Feature.ArrayPrototypeValues)) { | ||
if (ctx.features & Feature.ArrayPrototypeValues) { | ||
serialized = `${values}.values()`; | ||
@@ -1199,5 +1241,5 @@ } else { | ||
} | ||
if (ctx.features.has(Feature.ArrowFunction)) { | ||
if (ctx.features & Feature.ArrowFunction) { | ||
serialized = `{[Symbol.iterator]:()=>${serialized}}`; | ||
} else if (ctx.features.has(Feature.MethodShorthand)) { | ||
} else if (ctx.features & Feature.MethodShorthand) { | ||
serialized = `{[Symbol.iterator](){return ${serialized}}}`; | ||
@@ -1208,3 +1250,3 @@ } else { | ||
if (value[0]) { | ||
if (ctx.features.has(Feature.ObjectAssign)) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
const options = serializeObject(ctx, id, value[0]); | ||
@@ -1225,3 +1267,3 @@ serialized = `Object.assign(${serialized},${options})`; | ||
let serialized = 'Object.create(null)'; | ||
if (ctx.features.has(Feature.ObjectAssign)) { | ||
if (ctx.features & Feature.ObjectAssign) { | ||
const fields = serializeObject(ctx, id, value); | ||
@@ -1228,0 +1270,0 @@ if (fields !== '{}') { |
@@ -7,4 +7,3 @@ // Values that are non-recursive | ||
| undefined | ||
| null | ||
| bigint; | ||
| null; | ||
@@ -37,3 +36,4 @@ export type ErrorValue = | ||
| Date | ||
| TypedArrayValue; | ||
| TypedArrayValue | ||
| bigint; | ||
@@ -40,0 +40,0 @@ export type CommonServerValue = |
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
365199
4655