Skip to content

Commit

Permalink
Merge pull request #23 from lxsmnsyc/next
Browse files Browse the repository at this point in the history
0.10.1
  • Loading branch information
lxsmnsyc authored Sep 26, 2023
2 parents 531567f + 5d67f97 commit 9bc2181
Show file tree
Hide file tree
Showing 72 changed files with 2,477 additions and 608 deletions.
2 changes: 2 additions & 0 deletions packages/seroval/src/core/Serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { serializeString } from './string';

export interface SerializerOptions {
globalIdentifier: string;
scopeId?: string;
disabledFeatures?: number;
onData: (result: string) => void;
}
Expand All @@ -22,6 +23,7 @@ export default class Serializer {
write(key: string, value: unknown): void {
if (this.alive) {
this.cleanups.push(crossSerializeStream(value, {
scopeId: this.options.scopeId,
refs: this.refs,
disabledFeatures: this.options.disabledFeatures,
onSerialize: (data, initial) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/seroval/src/core/cross/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import type { BaseParserContext } from '../context';
import type { SerovalNode } from '../types';

export interface CrossParserContextOptions {
scopeId?: string;
refs?: Map<unknown, number>;
disabledFeatures?: number;
}

export interface CrossParserContext {
scopeId?: string;
refs: Map<unknown, number>;
features: number;
}
Expand All @@ -17,6 +19,7 @@ export function createCrossParserContext(
options: CrossParserContextOptions = {},
): CrossParserContext {
return {
scopeId: options.scopeId,
refs: options.refs || new Map<unknown, number>(),
features: ALL_ENABLED ^ (options.disabledFeatures || 0),
};
Expand Down Expand Up @@ -61,6 +64,7 @@ export function createStreamingCrossParserContext(
options.onDone();
}
},
scopeId: options.scopeId,
};
}

Expand Down
39 changes: 22 additions & 17 deletions packages/seroval/src/core/cross/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Feature } from '../compat';
import {
ROOT_REFERENCE,
} from '../keys';
import { GLOBAL_CONTEXT_REFERENCES } from '../keys';
import { serializeString } from '../string';
// import type { SerovalNode } from '../types';
import parseAsync from './async';
import type {
Expand All @@ -19,24 +18,26 @@ import parseSync from './sync';

function finalize(
ctx: CrossSerializerContext,
scopeId: string | undefined,
id: number | undefined,
result: string,
): string {
if (id == null) {
return result;
}
const patches = resolvePatches(ctx);
if (patches) {
if (id == null) {
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 + '})()';
}
return '(' + result + ',' + patches + getRefExpr(id) + ')';
const ref = getRefExpr(id);
const params = scopeId == null ? '' : GLOBAL_CONTEXT_REFERENCES;
const mainBody = patches ? result + ',' + patches : result;
if (params === '') {
return patches ? '(' + mainBody + ref + ')' : mainBody;
}
const args = scopeId == null ? '()' : '(' + GLOBAL_CONTEXT_REFERENCES + '["' + serializeString(scopeId) + '"])';
const body = mainBody + (patches ? ref : '');
if (ctx.features & Feature.ArrowFunction) {
return '(' + params + '=>(' + body + '))' + args;
}
return result;
return '(function(' + params + '){return ' + body + '})' + args;
}

export function crossSerialize<T>(
Expand All @@ -51,6 +52,7 @@ export function crossSerialize<T>(
const result = crossSerializeTree(serial, tree);
return finalize(
serial,
ctx.scopeId,
tree.i,
result,
);
Expand All @@ -68,6 +70,7 @@ export async function crossSerializeAsync<T>(
const result = crossSerializeTree(serial, tree);
return finalize(
serial,
ctx.scopeId,
tree.i,
result,
);
Expand Down Expand Up @@ -121,14 +124,15 @@ export async function crossSerializeAsync<T>(

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

export function crossSerializeStream<T>(
source: T,
options: CrossSerializeStreamOptions,
): () => void {
const ctx = createStreamingCrossParserContext({
scopeId: options.scopeId,
refs: options.refs,
disabledFeatures: options.disabledFeatures,
onParse(node, initial) {
Expand All @@ -139,6 +143,7 @@ export function crossSerializeStream<T>(
options.onSerialize(
finalize(
serial,
ctx.scopeId,
node.i,
crossSerializeTree(serial, node),
),
Expand Down
15 changes: 8 additions & 7 deletions packages/seroval/src/core/cross/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
REFERENCES_KEY,
GLOBAL_CONTEXT_STREAM_CONSTRUCTOR,
GLOBAL_CONTEXT_STREAM_EMIT,
GLOBAL_CONTEXT_API,
} from '../keys';

export function getRefExpr(id: number): string {
Expand Down Expand Up @@ -724,46 +725,46 @@ function serializePromiseResolve(
ctx: CrossSerializerContext,
node: SerovalPromiseResolveNode,
): string {
return GLOBAL_CONTEXT_PROMISE_RESOLVE + '(' + node.i + ',' + crossSerializeTree(ctx, node.f) + ')';
return GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_PROMISE_RESOLVE + '(' + getRefExpr(node.i) + ',' + crossSerializeTree(ctx, node.f) + ')';
}

function serializePromiseReject(
ctx: CrossSerializerContext,
node: SerovalPromiseRejectNode,
): string {
return GLOBAL_CONTEXT_PROMISE_REJECT + '(' + node.i + ',' + crossSerializeTree(ctx, node.f) + ')';
return GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_PROMISE_REJECT + '(' + getRefExpr(node.i) + ',' + crossSerializeTree(ctx, node.f) + ')';
}

function serializePromiseConstructor(
node: SerovalPromiseConstructorNode,
): string {
return assignIndexedValue(node.i, GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR + '()');
return assignIndexedValue(node.i, GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR + '()');
}

function serializeReadableStreamClose(
node: SerovalReadableStreamCloseNode,
): string {
return GLOBAL_CONTEXT_STREAM_EMIT + '(' + node.i + ',2)';
return GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_STREAM_EMIT + '(' + getRefExpr(node.i) + ',2)';
}

function serializeReadableStreamEnqueue(
ctx: CrossSerializerContext,
node: SerovalReadableStreamEnqueueNode,
): string {
return GLOBAL_CONTEXT_STREAM_EMIT + '(' + node.i + ',0,' + crossSerializeTree(ctx, node.f) + ')';
return GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_STREAM_EMIT + '(' + getRefExpr(node.i) + ',0,' + crossSerializeTree(ctx, node.f) + ')';
}

function serializeReadableStreamError(
ctx: CrossSerializerContext,
node: SerovalReadableStreamErrorNode,
): string {
return GLOBAL_CONTEXT_STREAM_EMIT + '(' + node.i + ',1,' + crossSerializeTree(ctx, node.f) + ')';
return GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_STREAM_EMIT + '(' + getRefExpr(node.i) + ',1,' + crossSerializeTree(ctx, node.f) + ')';
}

function serializeReadableStreamConstructor(
node: SerovalReadableStreamConstructorNode,
): string {
return assignIndexedValue(node.i, GLOBAL_CONTEXT_STREAM_CONSTRUCTOR + '()');
return assignIndexedValue(node.i, GLOBAL_CONTEXT_API + '.' + GLOBAL_CONTEXT_STREAM_CONSTRUCTOR + '()');
}

export default function crossSerializeTree(
Expand Down
112 changes: 54 additions & 58 deletions packages/seroval/src/core/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,58 @@
*/

// Global references array. Global because we (ideally) want <script> elements to share it.
var $R = [];

// Promise constructor, used to construct deferred Promises
function $P(success, failure, promise) {
promise = new Promise(function (resolve, reject) {
success = resolve;
failure = reject;
});

promise.s = success;
promise.f = failure;

return promise;
}

// This unsets the custom properties of the Promise instance
function $uP(promise) {
delete promise.s;
delete promise.f;
}

function $Ps(referenceID, data) {
$R[referenceID].s(data);
}

function $Pf(referenceID, data) {
$R[referenceID].f(data);
}

// Unset stream
function $uS(stream) {
delete stream.c;
}

function $Se(referenceID, type, data, stream, controller) {
stream = $R[referenceID];
controller = stream.c;
switch (type) {
case 0: return controller.enqueue(data);
case 1:
$uS(stream);
return controller.error(data);
case 2:
$uS(stream);
return controller.close();
}
}

// ReadableStream constructor
function $S(stream, controller) {
stream = new ReadableStream({
start: function (c) {
controller = c;
self._$ = self._$ || {
// Promise constructor, used to construct deferred Promises
P(success, failure, promise) {
promise = new Promise(function (resolve, reject) {
success = resolve;
failure = reject;
});

promise.s = success;
promise.f = failure;

return promise;
},
// This unsets the custom properties of the Promise instance
uP(promise) {
delete promise.s;
delete promise.f;
},
// Promise resolution
Ps(promise, data) {
promise.s(data);
promise.value = data;
this.uP(promise);
},
Pf(promise, data) {
promise.f(data);
this.uP(promise);
},
// Unset stream
uS(stream) {
delete stream.c;
},
Se(stream, type, data, controller) {
controller = stream.c;
switch (type) {
case 0: return controller.enqueue(data);
case 1:
this.uS(stream);
return controller.error(data);
case 2:
this.uS(stream);
return controller.close();
}
});
stream.c = controller;

return stream;
}
},
// ReadableStream constructor
S(stream, controller) {
stream = new ReadableStream({
start: function (c) {
controller = c;
}
});
stream.c = controller;
return stream;
},
};
46 changes: 27 additions & 19 deletions packages/seroval/src/core/keys.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
import { serializeString } from './string';

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

export const GLOBAL_CONTEXT_API = '_$';

export const GLOBAL_CONTEXT_REFERENCES = '$R';

export const LOCAL_CONTEXT_PROMISE_RESOLVE = 's';

export const LOCAL_CONTEXT_PROMISE_REJECT = 'f';

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

export const GLOBAL_CONTEXT_PROMISE_RESOLVE = '$Ps';
export const GLOBAL_CONTEXT_PROMISE_RESOLVE = 'Ps';

export const GLOBAL_CONTEXT_PROMISE_REJECT = '$Pf';
export const GLOBAL_CONTEXT_PROMISE_REJECT = 'Pf';

export const LOCAL_CONTEXT_STREAM_CONTROLLER = 'c';

export const GLOBAL_CONTEXT_STREAM_CONSTRUCTOR = '$S';

export const GLOBAL_CONTEXT_STREAM_EMIT = '$Se';

export const ROOT_REFERENCE = 't';

export const CROSS_REFERENCE_HEADER = `
var ${GLOBAL_CONTEXT_REFERENCES}=[];
function ${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}(s,f,p){return (p=new Promise(function(a,b){s=a,f=b})).${LOCAL_CONTEXT_PROMISE_RESOLVE}=s,p.${LOCAL_CONTEXT_PROMISE_REJECT}=f,p}
function $uP(i,p){delete (p=${GLOBAL_CONTEXT_REFERENCES}[i]).${LOCAL_CONTEXT_PROMISE_RESOLVE};delete p.${LOCAL_CONTEXT_PROMISE_REJECT}}
function ${GLOBAL_CONTEXT_PROMISE_RESOLVE}(i,d){${GLOBAL_CONTEXT_REFERENCES}[i].${LOCAL_CONTEXT_PROMISE_RESOLVE}(d),$uP(i)}
function ${GLOBAL_CONTEXT_PROMISE_REJECT}(i,d){${GLOBAL_CONTEXT_REFERENCES}[i].${LOCAL_CONTEXT_PROMISE_REJECT}(d),$uP(i)}
function $uS(s){delete s.${LOCAL_CONTEXT_STREAM_CONTROLLER}}
function ${GLOBAL_CONTEXT_STREAM_EMIT}(i,t,d,s,c){switch(c=(s=${GLOBAL_CONTEXT_REFERENCES}[i]).${LOCAL_CONTEXT_STREAM_CONTROLLER},t){case 0:return c.enqueue(d);case 1:return c.error(d),$uS(s);case 2:return c.close(),$uS(s)}}
function ${GLOBAL_CONTEXT_STREAM_CONSTRUCTOR}(s,c){return(s=new ReadableStream({start:function(t){c=t}})).c=c,s}
`;
export const GLOBAL_CONTEXT_STREAM_CONSTRUCTOR = 'S';

export const GLOBAL_CONTEXT_STREAM_EMIT = 'Se';

export const GLOBAL_CONTEXT_API_SCRIPT = `self.${GLOBAL_CONTEXT_API}=self.${GLOBAL_CONTEXT_API}||{`
+ `${GLOBAL_CONTEXT_PROMISE_CONSTRUCTOR}:function(s,f,p){return(p=new Promise(function(a,b){s=a,f=b})).${LOCAL_CONTEXT_PROMISE_RESOLVE}=s,p.${LOCAL_CONTEXT_PROMISE_REJECT}=f,p},`
+ `uP:function(p){delete p.${LOCAL_CONTEXT_PROMISE_RESOLVE};delete p.${LOCAL_CONTEXT_PROMISE_REJECT}},`
+ `${GLOBAL_CONTEXT_PROMISE_RESOLVE}:function(p,d){p.${LOCAL_CONTEXT_PROMISE_RESOLVE}(d),p.value=d,this.uP(p)},`
+ `${GLOBAL_CONTEXT_PROMISE_REJECT}:function(p,d){p.${LOCAL_CONTEXT_PROMISE_REJECT}(d),this.uP(p)},`
+ `uS:function(s){delete s.${LOCAL_CONTEXT_STREAM_CONTROLLER}},`
+ `${GLOBAL_CONTEXT_STREAM_EMIT}:function(s,t,d,c){switch(c=s.${LOCAL_CONTEXT_STREAM_CONTROLLER},t){case 0:return c.enqueue(d);case 1:return(this.uS(s),c.error(d));case 2:return(this.uS(s),c.close())}},`
+ `${GLOBAL_CONTEXT_STREAM_CONSTRUCTOR}:function(s,c){return(s=new ReadableStream({start:function(x){c=x}})).${LOCAL_CONTEXT_STREAM_CONTROLLER}=c,s},`
+ '}';

export function getCrossReferenceHeader(id?: string): string {
if (id == null) {
return `self.${GLOBAL_CONTEXT_REFERENCES}=self.${GLOBAL_CONTEXT_REFERENCES}||[];`;
}
return `(self.${GLOBAL_CONTEXT_REFERENCES}=self.${GLOBAL_CONTEXT_REFERENCES}||{})["${serializeString(id)}"]=[]`;
}
11 changes: 0 additions & 11 deletions packages/seroval/src/core/tree/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,6 @@ export function getRefParam(ctx: SerializerContext, index: number): string {
return identifier;
}

export function getRootID<T>(
ctx: ParserContext,
current: T,
): number {
const ref = ctx.reference.ids.get(current);
if (ref == null) {
return ctx.reference.ids.size;
}
return ref;
}

export function createIndexedValue<T>(
ctx: ParserContext,
current: T,
Expand Down
Loading

0 comments on commit 9bc2181

Please sign in to comment.