New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

seroval

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

seroval - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0-alpha.0

dist/types/assert.d.ts

5

dist/types/index.d.ts

@@ -0,6 +1,7 @@

import { Options } from './tree';
import { AsyncServerValue, PrimitiveValue, ServerValue, CommonServerValue, SemiPrimitiveValue, ErrorValue } from './types';
export { AsyncServerValue, ServerValue, PrimitiveValue, CommonServerValue, SemiPrimitiveValue, ErrorValue, };
export declare function serialize(source: ServerValue): string;
export declare function serializeAsync(source: AsyncServerValue): Promise<string>;
export declare function serialize<T extends ServerValue>(source: T, options?: Partial<Options>): string;
export declare function serializeAsync<T extends AsyncServerValue>(source: T, options?: Partial<Options>): Promise<string>;
export declare function deserialize<T extends AsyncServerValue>(source: string): T;
export default serialize;

69

dist/types/tree.d.ts
import { AsyncServerValue, PrimitiveValue, ServerValue, TypedArrayValue } from './types';
import { Feature } from './compat';
interface IndexAssignment {

@@ -26,5 +27,9 @@ type: 'index';

assignments: Assignment[];
features: Set<Feature>;
}
export declare function createSerializationContext(): SerializationContext;
export declare function resolvePatches(ctx: SerializationContext): string;
export interface Options {
target: string | string[];
}
export declare function createSerializationContext(options?: Partial<Options>): SerializationContext;
export declare function resolvePatches(ctx: SerializationContext): string | undefined;
/**

@@ -59,37 +64,51 @@ * Creates a reference ID from the given values

type: SerovalNodeType.TypedArray,
value: {
constructor: string;
array: TypedArrayValue;
},
value: [constructor: string, array: TypedArrayValue],
id: number
];
type SerovalNode = SerovalPrimitiveNode | SerovalReferenceNode | SerovalSemiPrimitiveNode | [type: SerovalNodeType.Set, value: SerovalNode[], id: number] | [type: SerovalNodeType.Map, value: [key: SerovalNode, value: SerovalNode][], id: number] | [type: SerovalNodeType.Array, value: SerovalNode[], id: number] | [type: SerovalNodeType.Object, value: Record<string, SerovalNode>, id: number] | [type: SerovalNodeType.NullConstructor, value: Record<string, SerovalNode>, id: number] | [type: SerovalNodeType.Promise, value: SerovalNode, id: number] | [
type SerovalDictionaryNode = [key: string[], value: SerovalNode[], size: number];
type SerovalSetNode = [type: SerovalNodeType.Set, value: SerovalNode[], id: number];
type SerovalMapNode = [
type: SerovalNodeType.Map,
value: [key: SerovalNode[], value: SerovalNode[], size: number],
id: number
];
type SerovalArrayNode = [type: SerovalNodeType.Array, value: SerovalNode[], id: number];
type SerovalObjectNode = [type: SerovalNodeType.Object, value: SerovalDictionaryNode, id: number];
type SerovalNullConstructorNode = [
type: SerovalNodeType.NullConstructor,
value: SerovalDictionaryNode,
id: number
];
type SerovalPromiseNode = [type: SerovalNodeType.Promise, value: SerovalNode, id: number];
type SerovalErrorNode = [
type: SerovalNodeType.Error,
value: {
constructor: string;
message: string;
options?: SerovalNode;
cause?: SerovalNode;
},
value: [
constructor: string,
message: string,
options?: SerovalDictionaryNode
],
id: number
] | [
];
type SerovalAggregateErrorNode = [
type: SerovalNodeType.AggregateError,
value: {
message: string;
options?: SerovalNode;
cause?: SerovalNode;
errors: SerovalNode;
},
value: [
message: string,
options: SerovalDictionaryNode | undefined,
errors: SerovalNode
],
id: number
] | [
];
type SerovalIterableNode = [
type: SerovalNodeType.Iterable,
value: {
items: SerovalNode;
options?: SerovalNode;
},
value: [
options: SerovalDictionaryNode | undefined,
items: SerovalNode
],
id: 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 declare function serializeTree(ctx: SerializationContext, [type, value, id]: SerovalNode): string;
export {};
{
"name": "seroval",
"type": "module",
"version": "0.3.0",
"version": "0.4.0-alpha.0",
"files": [

@@ -67,3 +67,3 @@ "dist",

},
"gitHead": "3be59fefb6ffb73b1bad693d956d1fd9d8d6d720"
"gitHead": "4b01f180e0a2548360ec618fe8b6cd4d36fccaff"
}

@@ -61,7 +61,7 @@ # seroval

```js
((h,j,k,m)=>(m={number:[0.4178420745429774,-0,NaN,1/0,-1/0],string:["hello world","\x3Cscript>Hello World\x3C/script>"],boolean:[!0,!1],null:null,undefined:void 0,bigint:9007199254740991n,array:h=[,,,,j=new Map([["hello","world"],["mutual",k=new Set(["hello","world"])]])],regexp:/[a-z0-9]+/i,date:new Date("2023-03-13T00:52:11.335Z"),map:j,set:k},h[3]=h,j.set("self",j),k.add(k).add(h),m.self=m,m))()
((h,j,k,m)=>(m={number:[0.5623457676854244,-0,NaN,1/0,-1/0],string:["hello world","\x3Cscript>Hello World\x3C/script>"],boolean:[!0,!1],null:null,undefined:void 0,bigint:9007199254740991n,array:h=[,,,,j=new Map([["hello","world"],["mutual",k=new Set(["hello","world"])]])],regexp:/[a-z0-9]+/i,date:new Date("2023-03-14T11:16:24.879Z"),map:j,set:k},h[3]=h,j.set("self",j),k.add(k).add(h),m.self=m,m))()
// Formatted for readability
((h, j, k, m) => (m = {
number: [0.4178420745429774, -0, NaN, 1/0, -1/0],
number: [0.5623457676854244, -0, NaN, 1 / 0, -1 / 0],
string: ["hello world", "\x3Cscript>Hello World\x3C/script>"],

@@ -77,3 +77,3 @@ boolean: [!0, !1],

regexp: /[a-z0-9]+/i,
date: new Date("2023-03-13T00:52:11.335Z"),
date: new Date("2023-03-14T11:16:24.879Z"),
map: j,

@@ -201,2 +201,87 @@ set: k

## Compat
`serialize` and `serializeAsync` can accept a `{ target: string | string[] }` options. The `target` property decides what the serialization output would look like. The default target is `es2023`.
```js
import { serialize } from 'seroval';
const y = Object.create(null);
y.self = y;
y.example = 'Hello World';
function serializeWithTarget(value, target) {
console.log('Target is', target)
const result = serialize(value, {
target,
});
console.log(result);
}
serializeWithTarget(y, 'es5');
serializeWithTarget(y, 'es2023');
```
Output
```
Target is es5
(function(h){return (h=Object.create(null),h.self=h,h.example="Hello World",h)})()
Target is es2023
(h=>(h=Object.assign(Object.create(null),{example:"Hello World"}),h.self=h,h))()
```
You can also combine targets:
```js
serialize(value, {
targets: ['chrome85', 'edge85'],
});
```
Supported runtimes:
- `es`
- Valid values are `es5`, `es6` and above (e.g. `es2020`).
- Desktop
- `chrome`
- `edge`
- `safari`
- `firefox`
- `opera`
- Mobile
- `ios`
- `samsung`
- Runtimes
- `deno`
- `node`
> **Note**
> Version for runtimes excluding `es` can use semver format (`major.minor.patch`) e.g. `chrome110`, `node0.12`
Feature flags and compat attempt:
- `AggregateError`
- Compiles down to `Error` instead.
- `Array.prototype.values`
- Used for iterables, uses `Symbol.iterator` instead.
- Arrow functions
- Uses function expressions for top-level, and either method shorthands or function expressions for iterables.
- `BigInt`
- Throws when attempted to use, includes `BigInt64Array` and `BigUint64Array`
- `Map`
- Throws when attempted to use.
- Method shorthands
- Uses function expressions instead.
- `Object.assign`
- Uses manual object assignments instead.
- `Promise`
- Throws when attempted to use, specially in `serializeAsync`.
- `Set`
- Throws when attempted to use.
- `Symbol.iterator`
- Throws when attempted to use.
- `TypedArray`
- Throws when attempted to use.
## Sponsors

@@ -203,0 +288,0 @@

/* eslint-disable no-await-in-loop */
import { isPrimitive } from './checks';
import serializePrimitive from './serialize-primitive';
import {

@@ -10,4 +9,6 @@ createRef,

getRefParam,
Options,
resolvePatches,
SerializationContext,
serializePrimitive,
serializeTree,

@@ -34,5 +35,5 @@ } from './tree';

function finalize<T extends ServerValue | AsyncServerValue>(
function finalize<T extends NonPrimitiveServerValue<ServerValue | AsyncServerValue>>(
ctx: SerializationContext,
source: NonPrimitiveServerValue<T>,
source: T,
result: string,

@@ -42,13 +43,24 @@ ) {

if (ctx.vars.length) {
// Get (or create) a ref from the source
const index = getRefParam(ctx, createRef(ctx, source));
const patches = resolvePatches(ctx);
const params = ctx.vars.length > 1
? `(${ctx.vars.join(',')})`
let body = result;
if (patches) {
// Get (or create) a ref from the source
const index = getRefParam(ctx, createRef(ctx, source));
if (result.startsWith(`${index}=`)) {
body = `${result},${patches}${index}`;
} else {
body = `${index}=${result},${patches}${index}`;
}
}
let params = ctx.vars.length > 1
? ctx.vars.join(',')
: ctx.vars[0];
// Source is probably already assigned
if (result.startsWith(`${index}=`)) {
return `(${params}=>(${result},${patches}${index}))()`;
if (ctx.features.has('arrow-function')) {
params = ctx.vars.length > 1 || ctx.vars.length === 0
? `(${params})`
: params;
return `(${params}=>(${body}))()`;
}
return `(${params}=>(${index}=${result},${patches}${index}))()`;
return `(function(${params}){return ${body}})()`;
}

@@ -61,7 +73,10 @@ if (source.constructor === Object) {

export function serialize(source: ServerValue) {
export function serialize<T extends ServerValue>(
source: T,
options?: Partial<Options>,
) {
const ctx = createSerializationContext(options);
if (isPrimitive(source)) {
return serializePrimitive(source);
return serializePrimitive(ctx, source);
}
const ctx = createSerializationContext();
const tree = generateTreeSync(ctx, source);

@@ -72,7 +87,10 @@ const result = serializeTree(ctx, tree);

export async function serializeAsync(source: AsyncServerValue) {
export async function serializeAsync<T extends AsyncServerValue>(
source: T,
options?: Partial<Options>,
) {
const ctx = createSerializationContext(options);
if (isPrimitive(source)) {
return serializePrimitive(source);
return serializePrimitive(ctx, source);
}
const ctx = createSerializationContext();
const tree = await generateTreeAsync(ctx, source);

@@ -79,0 +97,0 @@ const result = serializeTree(ctx, tree);

@@ -19,3 +19,4 @@ /* eslint-disable guard-for-in */

import getIdentifier from './get-identifier';
import serializePrimitive from './serialize-primitive';
import { Feature, parseTargets } from './compat';
import assert from './assert';

@@ -59,5 +60,17 @@ interface IndexAssignment {

assignments: Assignment[];
// Supported features
features: Set<Feature>;
}
export function createSerializationContext(): SerializationContext {
export interface Options {
target: string | string[];
}
const DEFAULT_OPTIONS: Options = {
target: 'es2023',
};
export function createSerializationContext(options: Partial<Options> = {}): SerializationContext {
// eslint-disable-next-line prefer-object-spread
const result = Object.assign({}, DEFAULT_OPTIONS, options || {});
return {

@@ -70,2 +83,3 @@ markedRefs: [],

validRefs: new Map(),
features: parseTargets(result.target),
};

@@ -87,8 +101,8 @@ }

function mergeAssignments(ctx: SerializationContext) {
function mergeAssignments(assignments: Assignment[]) {
const newAssignments = [];
let current = ctx.assignments[0];
let current = assignments[0];
let prev = current;
for (let i = 1, len = ctx.assignments.length; i < len; i += 1) {
const item = ctx.assignments[i];
for (let i = 1, len = assignments.length; i < len; i++) {
const item = assignments[i];
if (item.type === prev.type) {

@@ -135,13 +149,18 @@ if (item.type === 'index' && item.value === prev.value) {

export function resolvePatches(ctx: SerializationContext) {
if (ctx.assignments.length) {
function resolveAssignments(assignments: Assignment[]) {
if (assignments.length) {
let result = '';
for (const assignment of mergeAssignments(ctx)) {
result += `${getAssignmentExpression(assignment)},`;
const merged = mergeAssignments(assignments);
for (let i = 0, len = merged.length; i < len; i++) {
result += `${getAssignmentExpression(merged[i])},`;
}
return result;
}
return '';
return undefined;
}
export function resolvePatches(ctx: SerializationContext) {
return resolveAssignments(ctx.assignments);
}
/**

@@ -311,10 +330,5 @@ * Increments the number of references the referenced value has

const EXCLUDED_ERROR_KEYS = {
name: true,
cause: true,
stack: true,
message: true,
};
function getErrorOptions(error: Error) {
function getErrorOptions(
error: Error,
) {
let options: Record<string, any> | undefined;

@@ -332,3 +346,3 @@ const constructor = getErrorConstructor(error);

for (const name of names) {
if (!(name in EXCLUDED_ERROR_KEYS)) {
if (name !== 'name' && name !== 'message') {
options = options || {};

@@ -377,6 +391,49 @@ options[name] = error[name as keyof Error];

type: SerovalNodeType.TypedArray,
value: { constructor: string, array: TypedArrayValue },
value: [constructor: string, array: TypedArrayValue],
id: number
];
type SerovalDictionaryNode = [key: string[], value: SerovalNode[], size: number];
type SerovalSetNode = [type: SerovalNodeType.Set, value: SerovalNode[], id: number];
type SerovalMapNode = [
type: SerovalNodeType.Map,
value: [key: SerovalNode[], value: SerovalNode[], size: number],
id: number
];
type SerovalArrayNode = [type: SerovalNodeType.Array, value: SerovalNode[], id: number];
type SerovalObjectNode = [type: SerovalNodeType.Object, value: SerovalDictionaryNode, id: number];
type SerovalNullConstructorNode = [
type: SerovalNodeType.NullConstructor,
value: SerovalDictionaryNode,
id: number,
];
type SerovalPromiseNode = [type: SerovalNodeType.Promise, value: SerovalNode, id: number];
type SerovalErrorNode = [
type: SerovalNodeType.Error,
value: [
constructor: string,
message: string,
options?: SerovalDictionaryNode,
],
id: number
];
type SerovalAggregateErrorNode = [
type: SerovalNodeType.AggregateError,
value: [
message: string,
options: SerovalDictionaryNode | undefined,
errors: SerovalNode,
],
id: number
];
type SerovalIterableNode = [
type: SerovalNodeType.Iterable,
value: [
options: SerovalDictionaryNode | undefined,
items: SerovalNode,
],
id: number,
];
type SerovalNode =

@@ -386,36 +443,11 @@ | SerovalPrimitiveNode

| SerovalSemiPrimitiveNode
| [type: SerovalNodeType.Set, value: SerovalNode[], id: number]
| [type: SerovalNodeType.Map, value: [key: SerovalNode, value: SerovalNode][], id: number]
| [type: SerovalNodeType.Array, value: SerovalNode[], id: number]
| [type: SerovalNodeType.Object, value: Record<string, SerovalNode>, id: number]
| [type: SerovalNodeType.NullConstructor, value: Record<string, SerovalNode>, id: number]
| [type: SerovalNodeType.Promise, value: SerovalNode, id: number]
| [
type: SerovalNodeType.Error,
value: {
constructor: string;
message: string;
options?: SerovalNode;
cause?: SerovalNode;
},
id: number
]
| [
type: SerovalNodeType.AggregateError,
value: {
message: string;
options?: SerovalNode;
cause?: SerovalNode;
errors: SerovalNode;
},
id: number
]
| [
type: SerovalNodeType.Iterable,
value: {
items: SerovalNode,
options?: SerovalNode,
},
id: number,
];
| SerovalSetNode
| SerovalMapNode
| SerovalArrayNode
| SerovalObjectNode
| SerovalNullConstructorNode
| SerovalPromiseNode
| SerovalErrorNode
| SerovalAggregateErrorNode
| SerovalIterableNode;

@@ -450,2 +482,3 @@ function isReferenceInStack(

function generateSemiPrimitiveValue(
ctx: SerializationContext,
current: unknown,

@@ -461,33 +494,52 @@ id: number,

if (constructorCheck<Int8Array>(current, Int8Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Int8Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int8Array"');
return [SerovalNodeType.TypedArray, ['Int8Array', current], id];
}
if (constructorCheck<Int16Array>(current, Int16Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Int16Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int16Array"');
return [SerovalNodeType.TypedArray, ['Int16Array', current], id];
}
if (constructorCheck<Int32Array>(current, Int32Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Int32Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Int32Array"');
return [SerovalNodeType.TypedArray, ['Int32Array', current], id];
}
if (constructorCheck<Uint8Array>(current, Uint8Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Uint8Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint8Array"');
return [SerovalNodeType.TypedArray, ['Uint8Array', current], id];
}
if (constructorCheck<Uint16Array>(current, Uint16Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Uint16Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint16Array"');
return [SerovalNodeType.TypedArray, ['Uint16Array', current], id];
}
if (constructorCheck<Uint32Array>(current, Uint32Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Uint32Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint32Array"');
return [SerovalNodeType.TypedArray, ['Uint32Array', current], id];
}
if (constructorCheck<Uint8ClampedArray>(current, Uint8ClampedArray)) {
return [SerovalNodeType.TypedArray, { constructor: 'Uint8ClampedArray', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Uint8ClampedArray"');
return [SerovalNodeType.TypedArray, ['Uint8ClampedArray', current], id];
}
if (constructorCheck<Float32Array>(current, Float32Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Float32Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Float32Array"');
return [SerovalNodeType.TypedArray, ['Float32Array', current], id];
}
if (constructorCheck<Float64Array>(current, Float64Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'Float64Array', array: current }, id];
assert(ctx.features.has('typed-arrays'), 'Unsupported value type "Float64Array"');
return [SerovalNodeType.TypedArray, ['Float64Array', current], id];
}
if (constructorCheck<BigInt64Array>(current, BigInt64Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'BigInt64Array', array: current }, id];
assert(
ctx.features.has('typed-arrays')
&& ctx.features.has('bigint'),
'Unsupported value type "BigInt64Array"',
);
return [SerovalNodeType.TypedArray, ['BigInt64Array', current], id];
}
if (constructorCheck<BigUint64Array>(current, BigUint64Array)) {
return [SerovalNodeType.TypedArray, { constructor: 'BigUint64Array', array: current }, id];
assert(
ctx.features.has('typed-arrays')
&& ctx.features.has('bigint'),
'Unsupported value type "BigUint64Array"',
);
return [SerovalNodeType.TypedArray, ['BigUint64Array', current], id];
}

@@ -501,2 +553,27 @@ return undefined;

): SerovalNode {
function serializeProperties(
properties: Record<string, unknown>,
): SerovalDictionaryNode {
const keyNodes: string[] = [];
const valueNodes: SerovalNode[] = [];
const deferredKeys: string[] = [];
const deferredValues: ServerValue[] = [];
const keys = Object.keys(properties);
let deferredSize = 0;
for (const key of keys) {
if (isIterable(properties[key])) {
deferredKeys.push(key);
deferredValues.push(properties[key] as ServerValue);
deferredSize++;
} else {
keyNodes.push(key);
valueNodes.push(generateTreeSync(ctx, properties[key] as ServerValue));
}
}
for (let i = 0; i < deferredSize; i++) {
keyNodes.push(deferredKeys[i]);
valueNodes.push(generateTreeSync(ctx, deferredValues[i]));
}
return [keyNodes, valueNodes, keys.length];
}
if (isPrimitive(current)) {

@@ -511,3 +588,3 @@ return [SerovalNodeType.Primitive, current];

}
const semiPrimitive = generateSemiPrimitiveValue(current, id);
const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
if (semiPrimitive) {

@@ -517,2 +594,3 @@ return semiPrimitive;

if (constructorCheck<Set<ServerValue>>(current, Set)) {
assert(ctx.features.has('set'), 'Unsupported type "Set"');
const nodes: SerovalNode[] = [];

@@ -529,4 +607,4 @@ const deferred: ServerValue[] = [];

// Parse deferred items
for (const item of deferred) {
nodes.push(generateTreeSync(ctx, item));
for (let i = 0, len = deferred.length; i < len; i++) {
nodes.push(generateTreeSync(ctx, deferred[i]));
}

@@ -536,88 +614,78 @@ return [SerovalNodeType.Set, nodes, id];

if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) {
const nodes: [key: SerovalNode, value: SerovalNode][] = [];
const deferred: [ServerValue, ServerValue][] = [];
assert(ctx.features.has('map'), 'Unsupported type "Map"');
const keyNodes: SerovalNode[] = [];
const valueNodes: SerovalNode[] = [];
const deferredKey: ServerValue[] = [];
const deferredValue: ServerValue[] = [];
let deferredSize = 0;
for (const [key, value] of current.entries()) {
// Either key or value might be an iterable
if (isIterable(key) || isIterable(value)) {
deferred.push([key, value]);
deferredKey.push(key);
deferredValue.push(value);
deferredSize++;
} else {
const keyNode = generateTreeSync(ctx, key);
const valueNode = generateTreeSync(ctx, value);
nodes.push([keyNode, valueNode]);
keyNodes.push(generateTreeSync(ctx, key));
valueNodes.push(generateTreeSync(ctx, value));
}
}
for (const [key, value] of deferred) {
const keyNode = generateTreeSync(ctx, key);
const valueNode = generateTreeSync(ctx, value);
nodes.push([keyNode, valueNode]);
for (let i = 0; i < deferredSize; i++) {
keyNodes.push(generateTreeSync(ctx, deferredKey[i]));
valueNodes.push(generateTreeSync(ctx, deferredValue[i]));
}
return [SerovalNodeType.Map, nodes, id];
return [SerovalNodeType.Map, [keyNodes, valueNodes, current.size], id];
}
if (Array.isArray(current)) {
const nodes = new Array<SerovalNode>(current.length);
const deferred = new Array<ServerValue>(current.length);
for (const key in current) {
const item = current[key];
if (isIterable(item)) {
deferred[key] = item;
} else {
nodes[key] = generateTreeSync(ctx, item);
const size = current.length;
const nodes = new Array<SerovalNode>(size);
const deferred = new Array<ServerValue>(size);
for (let i = 0; i < size; i++) {
if (i in current) {
if (isIterable(current[i])) {
deferred[i] = current[i];
} else {
nodes[i] = generateTreeSync(ctx, current[i]);
}
}
}
for (const key in deferred) {
nodes[key] = generateTreeSync(ctx, deferred[key]);
for (let i = 0; i < size; i++) {
if (i in deferred) {
nodes[i] = generateTreeSync(ctx, deferred[i]);
}
}
return [SerovalNodeType.Array, nodes, id];
}
if (current instanceof AggregateError) {
if (current instanceof AggregateError && ctx.features.has('aggregate-error')) {
const options = getErrorOptions(current);
return [SerovalNodeType.AggregateError, {
message: current.message,
options: options
? generateTreeSync(ctx, options)
: undefined,
cause: 'cause' in current
? generateTreeSync(ctx, { cause: current.cause as ServerValue })
: undefined,
errors: generateTreeSync(ctx, current.errors as ServerValue),
}, id];
const optionsNode = options
? serializeProperties(options)
: undefined;
const errorsNode = generateTreeSync(ctx, current.errors as ServerValue);
return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
}
if (current instanceof Error) {
const options = getErrorOptions(current);
return [SerovalNodeType.Error, {
constructor: getErrorConstructor(current),
message: current.message,
options: options ? generateTreeSync(ctx, options) : undefined,
cause: 'cause' in current
? generateTreeSync(ctx, { cause: current.cause as ServerValue })
: undefined,
}, id];
const optionsNode = options
? serializeProperties(options)
: undefined;
return [
SerovalNodeType.Error,
[getErrorConstructor(current), current.message, optionsNode],
id,
];
}
if (isIterable(current)) {
assert(ctx.features.has('symbol-iterator'), 'Unsupported type "Iterable"');
const options = getIterableOptions(current);
return [SerovalNodeType.Iterable, {
return [SerovalNodeType.Iterable, [
// Parse options first before the items
options: options
? generateTreeSync(ctx, options as ServerValue)
: undefined,
items: generateTreeSync(ctx, Array.from(current)),
}, id];
options ? serializeProperties(options) : undefined,
generateTreeSync(ctx, Array.from(current)),
], id];
}
const empty = current.constructor == null;
if (current.constructor === Object || empty) {
const nodes: Record<string, SerovalNode> = {};
const deferred: Record<string, ServerValue> = {};
for (const [key, item] of Object.entries(current)) {
if (isIterable(item)) {
deferred[key] = item as ServerValue;
} else {
nodes[key] = generateTreeSync(ctx, item as ServerValue);
}
}
for (const [key, item] of Object.entries(deferred)) {
nodes[key] = generateTreeSync(ctx, item);
}
return [
empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
nodes,
serializeProperties(current as Record<string, unknown>),
id,

@@ -633,2 +701,27 @@ ];

): Promise<SerovalNode> {
async function serializeProperties(
properties: Record<string, unknown>,
): Promise<SerovalDictionaryNode> {
const keyNodes: string[] = [];
const valueNodes: SerovalNode[] = [];
const deferredKeys: string[] = [];
const deferredValues: ServerValue[] = [];
const keys = Object.keys(properties);
let deferredSize = 0;
for (const key of keys) {
if (isIterable(properties[key])) {
deferredKeys.push(key);
deferredValues.push(properties[key] as ServerValue);
deferredSize++;
} else {
keyNodes.push(key);
valueNodes.push(await generateTreeAsync(ctx, properties[key] as ServerValue));
}
}
for (let i = 0; i < deferredSize; i++) {
keyNodes.push(deferredKeys[i]);
valueNodes.push(await generateTreeAsync(ctx, deferredValues[i]));
}
return [keyNodes, valueNodes, keys.length];
}
if (isPrimitive(current)) {

@@ -641,3 +734,3 @@ return [SerovalNodeType.Primitive, current];

}
const semiPrimitive = generateSemiPrimitiveValue(current, id);
const semiPrimitive = generateSemiPrimitiveValue(ctx, current, id);
if (semiPrimitive) {

@@ -647,2 +740,3 @@ return semiPrimitive;

if (isPromise(current)) {
assert(ctx.features.has('promise'), 'Unsupported type "Promise"');
return current.then(async (value) => [

@@ -654,5 +748,6 @@ SerovalNodeType.Promise,

}
if (constructorCheck<Set<ServerValue>>(current, Set)) {
if (constructorCheck<Set<AsyncServerValue>>(current, Set)) {
assert(ctx.features.has('set'), 'Unsupported type "Set"');
const nodes: SerovalNode[] = [];
const deferred: ServerValue[] = [];
const deferred: AsyncServerValue[] = [];
for (const item of current.keys()) {

@@ -665,39 +760,48 @@ if (isIterable(item)) {

}
for (const item of deferred) {
nodes.push(await generateTreeAsync(ctx, item));
for (let i = 0, len = deferred.length; i < len; i++) {
nodes.push(await generateTreeAsync(ctx, deferred[i]));
}
return [SerovalNodeType.Set, nodes, id];
}
if (constructorCheck<Map<ServerValue, ServerValue>>(current, Map)) {
const nodes: [key: SerovalNode, value: SerovalNode][] = [];
const deferred: [ServerValue, ServerValue][] = [];
if (constructorCheck<Map<AsyncServerValue, AsyncServerValue>>(current, Map)) {
assert(ctx.features.has('map'), 'Unsupported type "Map"');
const keyNodes: SerovalNode[] = [];
const valueNodes: SerovalNode[] = [];
const deferredKey: AsyncServerValue[] = [];
const deferredValue: AsyncServerValue[] = [];
let deferredSize = 0;
for (const [key, value] of current.entries()) {
if (isIterable(key) || isIterable(value)) {
deferred.push([key, value]);
deferredKey.push(key);
deferredValue.push(value);
deferredSize++;
} else {
const keyNode = await generateTreeAsync(ctx, key);
const valueNode = await generateTreeAsync(ctx, value);
nodes.push([keyNode, valueNode]);
keyNodes.push(await generateTreeAsync(ctx, key));
valueNodes.push(await generateTreeAsync(ctx, value));
}
}
for (const [key, value] of deferred) {
const keyNode = await generateTreeAsync(ctx, key);
const valueNode = await generateTreeAsync(ctx, value);
nodes.push([keyNode, valueNode]);
for (let i = 0; i < deferredSize; i++) {
keyNodes.push(await generateTreeAsync(ctx, deferredKey[i]));
valueNodes.push(await generateTreeAsync(ctx, deferredValue[i]));
}
return [SerovalNodeType.Map, nodes, id];
return [SerovalNodeType.Map, [keyNodes, valueNodes, current.size], id];
}
if (Array.isArray(current)) {
const nodes = new Array<SerovalNode>(current.length);
const deferred = new Array<AsyncServerValue>(current.length);
for (const key in current) {
const item = current[key];
if (isIterable(item)) {
deferred[key] = item;
} else {
nodes[key] = await generateTreeAsync(ctx, item);
const size = current.length;
const nodes = new Array<SerovalNode>(size);
const deferred = new Array<AsyncServerValue>(size);
for (let i = 0; i < size; i++) {
const item = current[i];
if (i in current) {
if (isIterable(item)) {
deferred[i] = item;
} else {
nodes[i] = await generateTreeAsync(ctx, item);
}
}
}
for (const key in deferred) {
nodes[key] = await generateTreeAsync(ctx, deferred[key]);
for (let i = 0; i < size; i++) {
if (i in deferred) {
nodes[i] = await generateTreeAsync(ctx, deferred[i]);
}
}

@@ -708,50 +812,34 @@ return [SerovalNodeType.Array, nodes, id];

const options = getErrorOptions(current);
return [SerovalNodeType.AggregateError, {
message: current.message,
options: options
? generateTreeSync(ctx, options)
: undefined,
cause: 'cause' in current
? await generateTreeAsync(ctx, { cause: current.cause as ServerValue })
: undefined,
errors: await generateTreeAsync(ctx, current.errors as ServerValue),
}, id];
const optionsNode = options
? await serializeProperties(options)
: undefined;
const errorsNode = await generateTreeAsync(ctx, current.errors as ServerValue);
return [SerovalNodeType.AggregateError, [current.message, optionsNode, errorsNode], id];
}
if (current instanceof Error) {
const options = getErrorOptions(current);
return [SerovalNodeType.Error, {
constructor: getErrorConstructor(current),
message: current.message,
options: options ? await generateTreeAsync(ctx, options) : undefined,
cause: 'cause' in current
? await generateTreeAsync(ctx, { cause: current.cause as ServerValue })
: undefined,
}, id];
const optionsNode = options
? await serializeProperties(options)
: undefined;
return [
SerovalNodeType.Error,
[getErrorConstructor(current), current.message, optionsNode],
id,
];
}
if (isIterable(current)) {
assert(ctx.features.has('symbol-iterator'), 'Unsupported type "Iterable"');
const options = getIterableOptions(current);
return [SerovalNodeType.Iterable, {
options: options
? await generateTreeAsync(ctx, options as ServerValue)
return [SerovalNodeType.Iterable, [
options
? await serializeProperties(options)
: undefined,
items: await generateTreeAsync(ctx, Array.from(current)),
}, id];
await generateTreeAsync(ctx, Array.from(current)),
], id];
}
const empty = current.constructor == null;
if (current.constructor === Object || empty) {
const nodes: Record<string, SerovalNode> = {};
const deferred: Record<string, AsyncServerValue> = {};
for (const [key, item] of Object.entries(current)) {
if (isIterable(item)) {
deferred[key] = item as ServerValue;
} else {
nodes[key] = await generateTreeAsync(ctx, item as ServerValue);
}
}
for (const [key, item] of Object.entries(deferred)) {
nodes[key] = await generateTreeAsync(ctx, item);
}
return [
empty ? SerovalNodeType.NullConstructor : SerovalNodeType.Object,
nodes,
await serializeProperties(current as Record<string, unknown>),
id,

@@ -763,2 +851,39 @@ ];

export function serializePrimitive(
ctx: SerializationContext,
value: PrimitiveValue,
): string {
// Shortened forms
if (value === true) {
return '!0';
}
if (value === false) {
return '!1';
}
if (value === undefined) {
return 'void 0';
}
if (value === null) {
return 'null';
}
if (typeof value === 'bigint') {
assert(ctx.features.has('bigint'), 'Unsupported type "BigInt"');
return `${value}n`;
}
if (typeof value === 'string') {
return quote(value);
}
// negative 0 isn't the same as 0
if (Object.is(value, -0)) {
return '-0';
}
if (Object.is(value, Infinity)) {
return '1/0';
}
if (Object.is(value, -Infinity)) {
return '-1/0';
}
return String(value);
}
export function serializeTree(

@@ -768,5 +893,75 @@ ctx: SerializationContext,

): string {
function serializeAssignments(
targetRef: number,
[keys, values, size]: SerovalDictionaryNode,
) {
ctx.stack.push(targetRef);
const mainAssignments: Assignment[] = [];
for (let i = 0; i < size; i++) {
const parentStack = ctx.stack;
ctx.stack = [];
const refParam = serializeTree(ctx, values[i]);
ctx.stack = parentStack;
const key = keys[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 parentAssignment = ctx.assignments;
ctx.assignments = mainAssignments;
if (
check >= 0
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
) {
if (!Number.isNaN(check)) {
createObjectComputedAssign(ctx, targetRef, key, refParam);
} else {
createObjectIdentifierAssign(ctx, targetRef, key, refParam);
}
} else {
createObjectComputedAssign(ctx, targetRef, quote(key), refParam);
}
ctx.assignments = parentAssignment;
}
ctx.stack.pop();
return resolveAssignments(mainAssignments);
}
function serializeObject(
sourceID: number,
[keys, values, size]: SerovalDictionaryNode,
) {
let result = '';
ctx.stack.push(sourceID);
for (let i = 0; i < size; i++) {
const key = keys[i];
const val = values[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
if (
check >= 0
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
) {
if (isReferenceInStack(ctx, val)) {
const refParam = getRefParam(ctx, val[1]);
if (!Number.isNaN(check)) {
createObjectComputedAssign(ctx, sourceID, key, refParam);
} else {
createObjectIdentifierAssign(ctx, sourceID, key, refParam);
}
} else {
result += `${key}:${serializeTree(ctx, val)},`;
}
} else if (isReferenceInStack(ctx, val)) {
const refParam = getRefParam(ctx, val[1]);
createObjectComputedAssign(ctx, sourceID, quote(key), refParam);
} else {
result += `${quote(key)}:${serializeTree(ctx, val)},`;
}
}
ctx.stack.pop();
return `{${result.substring(0, result.length - 1)}}`;
}
switch (type) {
case SerovalNodeType.Primitive:
return serializePrimitive(value);
return serializePrimitive(ctx, value);
case SerovalNodeType.Reference:

@@ -798,15 +993,20 @@ return getRefParam(ctx, value);

// BigInt typed arrays are broken for toString()
const values = [...value.array].map(serializePrimitive).join(',');
let args = `[${values}]`;
if (value.array.byteOffset !== 0) {
args += `,${value.array.byteOffset}`;
let values = '';
for (let i = 0, len = value[1].length; i < len; i++) {
values += `${serializePrimitive(ctx, value[1][i])},`;
}
return assignRef(ctx, id, `new ${value.constructor}(${args})`);
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})`);
}
case SerovalNodeType.Set: {
let serialized = 'new Set';
if (value.length) {
const values: string[] = [];
const size = value.length;
if (size) {
let result = '';
ctx.stack.push(id);
for (const item of value) {
for (let i = 0; i < size; i++) {
const item = value[i];
if (isReferenceInStack(ctx, item)) {

@@ -816,8 +1016,8 @@ createSetAdd(ctx, id, getRefParam(ctx, item[1]));

// Push directly
values.push(serializeTree(ctx, item));
result += `${serializeTree(ctx, item)},`;
}
}
ctx.stack.pop();
if (values.length) {
serialized += `([${values.join(',')}])`;
if (result) {
serialized += `([${result.substring(0, result.length - 1)}])`;
}

@@ -829,7 +1029,10 @@ }

let serialized = 'new Map';
if (value.length) {
const values: string[] = [];
const size = value[2];
if (size) {
let result = '';
ctx.stack.push(id);
for (const [key, val] of value) {
for (let i = 0; i < size; i++) {
// Check if key is a parent
const key = value[0][i];
const val = value[1][i];
if (isReferenceInStack(ctx, key)) {

@@ -865,3 +1068,3 @@ // Create reference for the map instance

} else {
values.push(`[${serializeTree(ctx, key)},${serializeTree(ctx, val)}]`);
result += `[${serializeTree(ctx, key)},${serializeTree(ctx, val)}],`;
}

@@ -873,4 +1076,4 @@ }

// can be used instead
if (values.length) {
serialized += `([${values.join(',')}])`;
if (result) {
serialized += `([${result.substring(0, result.length - 1)}])`;
}

@@ -884,6 +1087,6 @@ }

// the holes of the Array
const size = value.length;
let values = '';
ctx.stack.push(id);
for (let i = 0, len = value.length; i < len; i++) {
for (let i = 0; i < size; i++) {
const item = value[i];

@@ -898,3 +1101,3 @@ // Check if index is a hole

values += serializeTree(ctx, item);
if (i < value.length - 1) {
if (i < size - 1) {
values += ',';

@@ -913,14 +1116,20 @@ }

// Serialize the required arguments
const args = [serializeTree(ctx, value.errors), quote(value.message)];
// cause is an optional argument
if (value.cause) {
args.push(serializeTree(ctx, value.cause));
}
let serialized = `new AggregateError(${args.join(',')})`;
ctx.stack.push(id);
let serialized = `new AggregateError(${serializeTree(ctx, value[2])},${quote(value[0])})`;
ctx.stack.pop();
// `AggregateError` might've been extended
// either through class or custom properties
// Make sure to assign extra properties
if (value.options) {
const options = serializeTree(ctx, value.options);
serialized = `Object.assign(${serialized},${options})`;
if (value[1]) {
if (ctx.features.has('object-assign')) {
const options = serializeObject(id, value[1]);
serialized = `Object.assign(${serialized},${options})`;
} else {
markRef(ctx, id);
const assignments = serializeAssignments(id, value[1]);
if (assignments) {
const ref = getRefParam(ctx, id);
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
}
}
}

@@ -930,11 +1139,16 @@ return assignRef(ctx, id, serialized);

case SerovalNodeType.Error: {
const args = [quote(value.message)];
if (value.cause) {
args.push(serializeTree(ctx, value.cause));
let serialized = `new ${value[0]}(${quote(value[1])})`;
if (value[2]) {
if (ctx.features.has('object-assign')) {
const options = serializeObject(id, value[2]);
serialized = `Object.assign(${serialized},${options})`;
} else {
markRef(ctx, id);
const assignments = serializeAssignments(id, value[2]);
if (assignments) {
const ref = getRefParam(ctx, id);
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
}
}
}
let serialized = `new ${value.constructor}(${args.join(',')})`;
if (value.options) {
const options = serializeTree(ctx, value.options);
serialized = `Object.assign(${serialized},${options})`;
}
return assignRef(ctx, id, serialized);

@@ -945,50 +1159,51 @@ }

ctx.stack = [];
const values = serializeTree(ctx, value.items);
const values = serializeTree(ctx, value[1]);
ctx.stack = parent;
let serialized = `{[Symbol.iterator]:()=>${values}.values()}`;
if (value.options) {
ctx.stack.push(id);
const options = serializeTree(ctx, value.options);
ctx.stack.pop();
serialized = `Object.assign(${serialized},${options})`;
let serialized: string;
if (ctx.features.has('array-values')) {
serialized = `${values}.values()`;
} else {
serialized = `${values}[Symbol.iterator]()`;
}
if (ctx.features.has('arrow-function')) {
serialized = `{[Symbol.iterator]:()=>${serialized}}`;
} else if (ctx.features.has('method-shorthand')) {
serialized = `{[Symbol.iterator](){return ${serialized}}}`;
} else {
serialized = `{[Symbol.iterator]:function(){return ${serialized}}}`;
}
if (value[0]) {
if (ctx.features.has('object-assign')) {
const options = serializeObject(id, value[0]);
serialized = `Object.assign(${serialized},${options})`;
} else {
markRef(ctx, id);
const assignments = serializeAssignments(id, value[0]);
if (assignments) {
const ref = getRefParam(ctx, id);
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
}
}
}
return assignRef(ctx, id, serialized);
}
case SerovalNodeType.NullConstructor:
case SerovalNodeType.Object: {
const values: string[] = [];
ctx.stack.push(id);
for (const [key, val] of Object.entries(value)) {
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
if (
check >= 0
|| (Number.isNaN(check) && /^([$A-Z_][0-9A-Z_$]*)$/i.test(key))
) {
if (isReferenceInStack(ctx, val)) {
const refParam = getRefParam(ctx, val[1]);
if (!Number.isNaN(check)) {
createObjectComputedAssign(ctx, id, key, refParam);
} else {
createObjectIdentifierAssign(ctx, id, key, refParam);
}
} else {
values.push(`${key}:${serializeTree(ctx, val)}`);
}
} else if (isReferenceInStack(ctx, val)) {
const refParam = getRefParam(ctx, val[1]);
createObjectComputedAssign(ctx, id, quote(key), refParam);
} else {
values.push(`${quote(key)}:${serializeTree(ctx, val)}`);
case SerovalNodeType.NullConstructor: {
let serialized = 'Object.create(null)';
if (ctx.features.has('object-assign')) {
const fields = serializeObject(id, value);
if (fields !== '{}') {
serialized = `Object.assign(${serialized},${fields})`;
}
} else {
markRef(ctx, id);
const assignments = serializeAssignments(id, value);
if (assignments) {
const ref = getRefParam(ctx, id);
return `(${assignRef(ctx, id, serialized)},${assignments}${ref})`;
}
}
ctx.stack.pop();
let serialized = `{${values.join(',')}}`;
if (type === SerovalNodeType.NullConstructor) {
serialized = `Object.assign(Object.create(null),${serialized})`;
}
return assignRef(ctx, id, serialized);
}
case SerovalNodeType.Object:
return assignRef(ctx, id, serializeObject(id, value));
default:

@@ -995,0 +1210,0 @@ throw new Error('Unsupported type');

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc