seroval-plugins
Version:
Stringify JS values
144 lines (133 loc) • 3.22 kB
text/typescript
import type { SerovalNode, Stream } from 'seroval';
import { createPlugin, createStream } from 'seroval';
const READABLE_STREAM_FACTORY = {};
const ReadableStreamFactoryPlugin = /* @__PURE__ */ createPlugin<
object,
undefined
>({
tag: 'seroval-plugins/web/ReadableStreamFactory',
test(value) {
return value === READABLE_STREAM_FACTORY;
},
parse: {
sync() {
return undefined;
},
async async() {
return await Promise.resolve(undefined);
},
stream() {
return undefined;
},
},
serialize(_node, ctx) {
return ctx.createFunction(
['d'],
'new ReadableStream({start:' +
ctx.createEffectfulFunction(
['c'],
'd.on({next:' +
ctx.createEffectfulFunction(['v'], 'try{c.enqueue(v)}catch{}') +
',throw:' +
ctx.createEffectfulFunction(['v'], 'c.error(v)') +
',return:' +
ctx.createEffectfulFunction([], 'try{c.close()}catch{}') +
'})',
) +
'})',
);
},
deserialize() {
return READABLE_STREAM_FACTORY;
},
});
function toStream<T>(value: ReadableStream<T>): Stream<T | undefined> {
const stream = createStream<T | undefined>();
const reader = value.getReader();
async function push(): Promise<void> {
try {
const result = await reader.read();
if (result.done) {
stream.return(result.value);
} else {
stream.next(result.value);
await push();
}
} catch (error) {
stream.throw(error);
}
}
push().catch(() => {
//
});
return stream;
}
interface ReadableStreamNode {
factory: SerovalNode;
stream: SerovalNode;
}
const ReadableStreamPlugin = /* @__PURE__ */ createPlugin<
ReadableStream,
ReadableStreamNode
>({
tag: 'seroval/plugins/web/ReadableStream',
extends: [ReadableStreamFactoryPlugin],
test(value) {
if (typeof ReadableStream === 'undefined') {
return false;
}
return value instanceof ReadableStream;
},
parse: {
sync(_value, ctx) {
return {
factory: ctx.parse(READABLE_STREAM_FACTORY),
stream: ctx.parse(createStream()),
};
},
async async(value, ctx) {
return {
factory: await ctx.parse(READABLE_STREAM_FACTORY),
stream: await ctx.parse(toStream(value)),
};
},
stream(value, ctx) {
return {
factory: ctx.parse(READABLE_STREAM_FACTORY),
stream: ctx.parse(toStream(value)),
};
},
},
serialize(node, ctx) {
return (
'(' +
ctx.serialize(node.factory) +
')(' +
ctx.serialize(node.stream) +
')'
);
},
deserialize(node, ctx) {
const stream = ctx.deserialize(node.stream) as Stream<any>;
return new ReadableStream({
start(controller): void {
stream.on({
next(value) {
try {
controller.enqueue(value);
} catch {}
},
throw(value) {
controller.error(value);
},
return() {
try {
controller.close();
} catch {}
},
});
},
});
},
});
export default ReadableStreamPlugin;