Skip to content

Commit

Permalink
Rework streaming format
Browse files Browse the repository at this point in the history
  • Loading branch information
lxsmnsyc committed Sep 8, 2023
1 parent a90b0c4 commit c44dc35
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 61 deletions.
7 changes: 4 additions & 3 deletions packages/seroval/src/core/Serializer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { ALL_ENABLED } from './compat';
import { crossSerializeStream } from './cross';
import { getCrossReferenceHeader } from './keys';
import { serializeString } from './string';

export interface SerializerOptions {
globalIdentifier: string;
disabledFeatures?: number;
onHeader: (result: string) => void;
onData: (result: string) => void;
}

Expand All @@ -20,7 +18,10 @@ export default class Serializer {
constructor(
private options: SerializerOptions,
) {
options.onHeader(getCrossReferenceHeader(ALL_ENABLED ^ (options.disabledFeatures || 0)));
}

getHeader(): string {
return this.options.globalIdentifier + '={};' + getCrossReferenceHeader();
}

write(key: string, value: unknown): void {
Expand Down
33 changes: 9 additions & 24 deletions packages/seroval/src/core/cross/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Feature } from '../compat';
import {
GLOBAL_CONTEXT_KEY,
GLOBAL_CONTEXT_PARAM,
ROOT_REFERENCE,
getCrossReferenceHeader,
} from '../keys';
import type { SerovalNode } from '../types';
import parseAsync from './async';
Expand All @@ -26,27 +23,20 @@ function finalize(
result: string,
): string {
const patches = resolvePatches(ctx);

if (ctx.features & Feature.ArrowFunction) {
if (patches) {
if (id == null) {
const params = '(' + GLOBAL_CONTEXT_PARAM + ',' + ROOT_REFERENCE + ')';
const body = ROOT_REFERENCE + '=' + result + ',' + patches + ROOT_REFERENCE;
return '(' + params + '=>(' + body + '))';
}
return '((' + GLOBAL_CONTEXT_PARAM + ')=>(' + result + ',' + patches + getRefExpr(id) + '))(' + GLOBAL_CONTEXT_KEY + ')';
}
return '((' + GLOBAL_CONTEXT_PARAM + ')=>' + result + ')(' + GLOBAL_CONTEXT_KEY + ')';
}
if (patches) {
if (id == null) {
const params = '(' + GLOBAL_CONTEXT_PARAM + ',' + ROOT_REFERENCE + ')';
if (ctx.features & Feature.ArrowFunction) {
const params = '(' + ROOT_REFERENCE + ')';
const body = ROOT_REFERENCE + '=' + result + ',' + patches + ROOT_REFERENCE;
return '(' + params + '=>(' + body + '))()';
}
const params = '(' + ROOT_REFERENCE + ')';
const body = ROOT_REFERENCE + '=' + result + ',' + patches + ROOT_REFERENCE;
return '(function' + params + '{return ' + body + '})(' + GLOBAL_CONTEXT_KEY + ')';
return '(function' + params + '{return ' + body + '})()';
}
return '(function(' + GLOBAL_CONTEXT_PARAM + '){return ' + result + ',' + patches + getRefExpr(id) + '})(' + GLOBAL_CONTEXT_KEY + ')';
return '(' + result + ',' + patches + getRefExpr(id) + ')';
}
return '(function(' + GLOBAL_CONTEXT_PARAM + '){return ' + result + '})(' + GLOBAL_CONTEXT_KEY + ')';
return result;
}

export function crossSerialize<T>(
Expand Down Expand Up @@ -130,7 +120,6 @@ export function compileCrossJSON(source: SerovalCrossJSON): string {
// }

export interface CrossSerializeStreamOptions extends CrossParserContextOptions {
onHeader?: (script: string) => void;
onSerialize: (data: string, initial: boolean) => void;
}

Expand All @@ -157,10 +146,6 @@ export function crossSerializeStream<T>(
},
});

if (options.onHeader) {
options.onHeader(getCrossReferenceHeader(ctx.features));
}

ctx.onParse(crossParseStream(ctx, source), true);

return () => {
Expand Down
13 changes: 6 additions & 7 deletions packages/seroval/src/core/cross/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,15 @@ import {
import type { Assignment } from '../assignments';
import { resolveAssignments, resolveFlags } from '../assignments';
import {
GLOBAL_CONTEXT_PARAM,
GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR,
GLOBAL_CONTEXT_REFERENCES,
GLOBAL_CONTEXT_REJECTERS,
GLOBAL_CONTEXT_RESOLVERS,
GLOBAL_CONTEXT_REJECT,
GLOBAL_CONTEXT_RESOLVE,
REFERENCES_KEY,
} from '../keys';

export function getRefExpr(id: number): string {
return GLOBAL_CONTEXT_PARAM + '.' + GLOBAL_CONTEXT_REFERENCES + '[' + id + ']';
return GLOBAL_CONTEXT_REFERENCES + '[' + id + ']';
}

function pushObjectFlag(
Expand Down Expand Up @@ -718,14 +717,14 @@ function serializeFulfilled(
ctx: CrossSerializerContext,
node: SerovalFulfilledNode,
): string {
const callee = node.s ? GLOBAL_CONTEXT_RESOLVERS : GLOBAL_CONTEXT_REJECTERS;
return GLOBAL_CONTEXT_PARAM + '.' + GLOBAL_CONTEXT_REFERENCES + '[' + node.i + '].' + callee + '(' + crossSerializeTree(ctx, node.f) + ')';
const callee = node.s ? GLOBAL_CONTEXT_RESOLVE : GLOBAL_CONTEXT_REJECT;
return GLOBAL_CONTEXT_REFERENCES + '[' + node.i + '].' + callee + '(' + crossSerializeTree(ctx, node.f) + ')';
}

function serializeResolver(
node: SerovalResolverNode,
): string {
return assignIndexedValue(node.i, GLOBAL_CONTEXT_PARAM + '.' + GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR + '()');
return assignIndexedValue(node.i, GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR + '()');
}

export default function crossSerializeTree(
Expand Down
31 changes: 9 additions & 22 deletions packages/seroval/src/core/keys.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
import { Feature } from './compat';

// Used for mapping isomorphic references
export const REFERENCES_KEY = '__SEROVAL_REFS__';

export const GLOBAL_CONTEXT_KEY = '__SEROVAL__';

export const GLOBAL_CONTEXT_REFERENCES = 'r';
export const GLOBAL_CONTEXT_REFERENCES = '$R';

export const GLOBAL_CONTEXT_RESOLVERS = 's';
export const GLOBAL_CONTEXT_RESOLVE = 's';

export const GLOBAL_CONTEXT_REJECTERS = 'f';
export const GLOBAL_CONTEXT_REJECT = 'f';

export const GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR = 'p';
export const GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR = '$P';

export const ROOT_REFERENCE = 't';

export const GLOBAL_CONTEXT_PARAM = 'o';

const GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_FUNCTION_BODY = `(s,f,p){return p=new Promise(function(a,b){s=a,f=b}),p.${GLOBAL_CONTEXT_RESOLVERS}=s,p.${GLOBAL_CONTEXT_REJECTERS}=f,p}`;
const GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_ARROW_BODY = `(s,f,p)=>(p=new Promise((a,b)=>{s=a,f=b}),p.${GLOBAL_CONTEXT_RESOLVERS}=s,p.${GLOBAL_CONTEXT_REJECTERS}=f,p)`;
const GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_FUNCTION_BODY = `(s,f,p){return p=new Promise(function(a,b){s=a,f=b}),p.${GLOBAL_CONTEXT_RESOLVE}=s,p.${GLOBAL_CONTEXT_REJECT}=f,p}`;

function getPromiseConstructor(features: number): string {
if (features & Feature.ArrowFunction) {
return `${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}:${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_ARROW_BODY}`;
}
if (features & Feature.MethodShorthand) {
return `${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_FUNCTION_BODY}`;
}
return `${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}:function${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_FUNCTION_BODY}`;
function getPromiseConstructor(): string {
return `function ${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR_FUNCTION_BODY}`;
}

export function getCrossReferenceHeader(features: number): string {
return `${GLOBAL_CONTEXT_KEY}={${GLOBAL_CONTEXT_REFERENCES}:[],${getPromiseConstructor(features)}};`;
export function getCrossReferenceHeader(): string {
return `${GLOBAL_CONTEXT_REFERENCES}=[];${getPromiseConstructor()};`;
}
2 changes: 2 additions & 0 deletions packages/seroval/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export { createReference } from './core/reference';
export * from './core/tree';
export * from './core/cross';

export { getCrossReferenceHeader } from './core/keys';

export { default as Serializer } from './core/Serializer';

export default serialize;
11 changes: 6 additions & 5 deletions packages/seroval/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import { Serializer } from './dist/esm/development/index.mjs';

const serializer = new Serializer({
globalIdentifier: '__SOLID__',
onHeader(value) {
console.log('HEADER', value);
},
onData(value) {
console.log([value]);
},
});

console.log('HEADER', [serializer.getHeader()]);

const delay = (value, ms) => new Promise(r => setTimeout(r, ms, value));

const source = {
const source = ({
a: delay('A', 300),
b: delay('B', 200),
c: delay('C', 100),
};
});

source.d = delay(source, 400);

serializer.write('1', source);

0 comments on commit c44dc35

Please sign in to comment.