turbo-stream
Version:
A streaming data transport format that aims to support built-in features such as Promises, Dates, RegExps, Maps, Sets and more.
1,262 lines (1,259 loc) • 39.6 kB
JavaScript
// src/shared.ts
var STR_ARRAY_BUFFER = "A";
var STR_ASYNC_ITERABLE = "*";
var STR_BIG_INT_64_ARRAY = "J";
var STR_BIG_UINT_64_ARRAY = "j";
var STR_BIGINT = "b";
var STR_BLOB = "K";
var STR_DATA_VIEW = "V";
var STR_DATE = "D";
var STR_ERROR = "E";
var STR_FAILURE = "!";
var STR_FALSE = "false";
var STR_FILE = "k";
var STR_FLOAT_32_ARRAY = "H";
var STR_FLOAT_64_ARRAY = "h";
var STR_FORM_DATA = "F";
var STR_INFINITY = "I";
var STR_INT_16_ARRAY = "L";
var STR_INT_32_ARRAY = "G";
var STR_INT_8_ARRAY = "O";
var STR_MAP = "M";
var STR_NaN = "NaN";
var STR_NEGATIVE_INFINITY = "i";
var STR_NEGATIVE_ZERO = "z";
var STR_NULL = "null";
var STR_PLUGIN = "P";
var STR_PROMISE = "$";
var STR_READABLE_STREAM = "R";
var STR_REDACTED = "<redacted>";
var STR_REFERENCE_SYMBOL = "@";
var STR_REGEXP = "r";
var STR_SET = "S";
var STR_SUCCESS = ":";
var STR_SYMBOL = "s";
var STR_TRUE = "true";
var STR_UINT_16_ARRAY = "l";
var STR_UINT_32_ARRAY = "g";
var STR_UINT_8_ARRAY = "o";
var STR_UINT_8_ARRAY_CLAMPED = "C";
var STR_UNDEFINED = "u";
var STR_URL = "U";
var SUPPORTS_FILE = true;
try {
new File([], "");
} catch {
SUPPORTS_FILE = false;
}
var WaitGroup = class {
p = 0;
#q = [];
#waitQueue(resolve) {
if (this.p === 0) {
resolve();
} else {
this.#q.push(resolve);
}
}
add() {
this.p++;
}
done() {
if (--this.p === 0) {
let r;
while ((r = this.#q.shift()) !== void 0) {
r();
}
}
}
wait() {
return new Promise(this.#waitQueue.bind(this));
}
};
var Deferred = class {
promise;
resolve;
reject;
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
};
var DeferredAsyncIterable = class {
iterable;
#deferred = new Deferred();
#next = this.#deferred;
constructor() {
this.iterable = async function* () {
let next = this.#deferred;
while (true) {
const res = await next.promise;
if (res.done) {
return;
}
yield res.value;
next = res.next;
}
}.bind(this)();
}
resolve() {
this.#next.resolve({ done: true });
}
reject(error) {
this.#next.promise.catch(() => {
});
this.#next.reject(error);
}
yield(value) {
const deferred = new Deferred();
this.#next.resolve({
done: false,
value,
next: deferred
});
this.#next = deferred;
}
};
var DeferredReadableStream = class extends DeferredAsyncIterable {
readable = new ReadableStream({
start: async (controller) => {
try {
for await (const value of this.iterable) {
controller.enqueue(value);
}
controller.close();
} catch (error) {
controller.error(error);
}
}
});
};
var TurboBlob = class _TurboBlob extends Blob {
promise;
#size;
#type;
#slice = {};
constructor(from, start, end, contentType) {
super();
if (typeof from !== "undefined") {
this.promise = from.promise;
let nextStart = from.#slice.start ?? 0;
if (typeof start !== "undefined") {
nextStart += start;
}
this.#slice.start = nextStart;
let nextEnd = from.#slice.end;
if (typeof end !== "undefined") {
nextEnd = (from.#slice.start ?? 0) + end;
}
this.#slice.end = nextEnd;
this.#type = contentType ?? (from == null ? void 0 : from.type);
this.#size = (nextEnd ?? from.size) - nextStart;
}
}
get size() {
if (typeof this.#size === "undefined") {
throw new Error("Size is not set");
}
return this.#size;
}
set size(value) {
this.#size = value;
}
get type() {
if (typeof this.#type === "undefined") {
throw new Error("Type is not set");
}
return this.#type;
}
set type(value) {
this.#type = value;
}
async arrayBuffer() {
if (!this.promise) {
throw new Error("Promise is not set");
}
const buffer = await this.promise;
if (this.#slice) {
return buffer.slice(
this.#slice.start,
this.#slice.end
);
}
return buffer;
}
bytes() {
return this.arrayBuffer().then((buffer) => new Uint8Array(buffer));
}
slice(start, end, contentType) {
return new _TurboBlob(this, start, end, contentType);
}
stream() {
return new ReadableStream({
start: async (controller) => {
try {
controller.enqueue(await this.bytes());
controller.close();
} catch (err) {
controller.error(err);
}
}
});
}
text() {
return this.bytes().then((bytes) => {
return new TextDecoder().decode(bytes);
});
}
};
var FileBaseClass = SUPPORTS_FILE ? File : Blob;
var TurboFile = class _TurboFile extends FileBaseClass {
promise;
#size;
#type;
#name;
#lastModified;
#slice = {};
constructor(from, start, end, contentType) {
if (SUPPORTS_FILE) {
super([], "");
} else {
super([]);
}
if (typeof from !== "undefined") {
this.promise = from.promise;
let nextStart = from.#slice.start ?? 0;
if (typeof start !== "undefined") {
nextStart += start;
}
this.#slice.start = nextStart;
let nextEnd = from.#slice.end;
if (typeof end !== "undefined") {
nextEnd = (from.#slice.start ?? 0) + end;
}
this.#slice.end = nextEnd;
this.#type = contentType ?? (from == null ? void 0 : from.type);
this.#name = from.name;
this.#lastModified = from.lastModified;
}
}
get name() {
if (typeof this.#name === "undefined") {
throw new Error("Name is not set");
}
return this.#name;
}
set name(value) {
this.#name = value;
}
get lastModified() {
if (typeof this.#lastModified === "undefined") {
throw new Error("Last modified is not set");
}
return this.#lastModified;
}
set lastModified(value) {
this.#lastModified = value;
}
get size() {
if (typeof this.#size === "undefined") {
throw new Error("Size is not set");
}
return this.#size;
}
set size(value) {
this.#size = value;
}
get type() {
if (typeof this.#type === "undefined") {
throw new Error("Type is not set");
}
return this.#type;
}
set type(value) {
this.#type = value;
}
async arrayBuffer() {
if (!this.promise) {
throw new Error("Promise is not set");
}
const buffer = await this.promise;
if (this.#slice) {
return buffer.slice(
this.#slice.start,
this.#slice.end
);
}
return buffer;
}
bytes() {
return this.arrayBuffer().then((buffer) => new Uint8Array(buffer));
}
slice(start, end, contentType) {
return new _TurboFile(this, start, end, contentType);
}
stream() {
return new ReadableStream({
start: async (controller) => {
try {
controller.enqueue(await this.bytes());
controller.close();
} catch (err) {
controller.error(err);
}
}
});
}
text() {
return this.bytes().then((bytes) => {
return new TextDecoder().decode(bytes);
});
}
};
// src/decode.ts
var MODE_UNKNOWN = 0;
var MODE_NUMBER = 1;
var MODE_STRING = 2;
var MODE_ASYNC = 3;
var SUB_MODE_UNKNOWN = 0;
var SUB_MODE_BIGINT = 1;
var SUB_MODE_DATE = 2;
var SUB_MODE_URL = 3;
var SUB_MODE_SYMBOL = 4;
var SUB_MODE_REFERENCE = 5;
var SUB_MODE_OBJECT_KEY = 6;
var SUB_MODE_PROMISE_ID = 7;
var SUB_MODE_ASYNC_ITERABLE_ID = 8;
var SUB_MODE_READABLE_STREAM_ID = 9;
var SUB_MODE_ASYNC_STATUS = 10;
var SUB_MODE_ARRAY_BUFFER = 11;
var SUB_MODE_INT_8_ARRAY = 12;
var SUB_MODE_UINT_8_ARRAY = 13;
var SUB_MODE_UINT_8_ARRAY_CLAMPED = 14;
var SUB_MODE_INT_16_ARRAY = 15;
var SUB_MODE_UINT_16_ARRAY = 16;
var SUB_MODE_INT_32_ARRAY = 17;
var SUB_MODE_UINT_32_ARRAY = 18;
var SUB_MODE_FLOAT_32_ARRAY = 19;
var SUB_MODE_FLOAT_64_ARRAY = 20;
var SUB_MODE_BIG_INT_64_ARRAY = 21;
var SUB_MODE_BIG_UINT_64_ARRAY = 22;
var SUB_MODE_DATA_VIEW = 23;
var ARRAY_TYPE_SET = 0;
var ARRAY_TYPE_MAP = 1;
var ARRAY_TYPE_REGEXP = 2;
var ARRAY_TYPE_FORM_DATA = 3;
var ARRAY_TYPE_PLUGIN = 4;
var RELEASE_TYPE_OBJECT = 1;
var RELEASE_TYPE_ARRAY = 2;
async function decode(stream, { plugins = [] } = {}) {
let root = new Deferred();
let references = /* @__PURE__ */ new Map();
let deferredValues = /* @__PURE__ */ new Map();
let stack = [];
let mode = MODE_UNKNOWN;
let subMode = SUB_MODE_UNKNOWN;
let buffer = "";
let shouldSkip = 0;
let lastChar;
let numSlashes = 0;
let hasSlashes = false;
let releaseValue = (value, type) => {
if (type === RELEASE_TYPE_OBJECT) {
if (typeof value !== "object" || value === null) {
throw new Error("Expected object");
}
} else if (type === RELEASE_TYPE_ARRAY) {
if (!Array.isArray(value)) {
throw new Error("Expected array");
}
}
if (Array.isArray(value) && typeof value.__type === "number") {
switch (value.__type) {
case ARRAY_TYPE_MAP:
for (let [key, val] of value) {
value.__ref.set(key, val);
}
value = value.__ref;
break;
case ARRAY_TYPE_SET:
for (let val of value) {
value.__ref.add(val);
}
value = value.__ref;
break;
case ARRAY_TYPE_REGEXP:
value = new RegExp(value[0], value[1]);
references.set(value.__id, value);
break;
case ARRAY_TYPE_FORM_DATA: {
let formData = new FormData();
for (let [key, val] of value) {
formData.append(key, val);
}
value = formData;
references.set(value.__id, value);
break;
}
case ARRAY_TYPE_PLUGIN: {
let pluginHandled = false;
let pluginsLength = plugins.length;
for (let i = 0; i < pluginsLength; i++) {
let result = plugins[i](...value);
if (typeof result === "object" && result !== null) {
value = result.value;
pluginHandled = true;
break;
}
}
if (!pluginHandled) {
value = void 0;
}
break;
}
}
}
if (stack.length === 0) {
if (root === null) {
throw new Error("Unexpected root value");
}
if (root !== null) {
root.resolve(value);
root = null;
return;
}
}
let parent = stack[stack.length - 1];
if (Array.isArray(parent)) {
parent.push(value);
} else if (typeof parent === "string") {
stack.pop();
stack[stack.length - 1][parent] = value;
} else if (typeof parent === "boolean") {
stack.pop();
let deferred = deferredValues.get(stack.pop());
if (!deferred) {
throw new Error("Invalid stack state");
}
if (deferred instanceof Deferred) {
if (parent) {
deferred.resolve(value);
} else {
deferred.reject(value);
}
} else {
if (parent) {
deferred.yield(value);
} else {
deferred.reject(value);
}
}
} else {
throw new Error("Invalid stack state");
}
};
let step = (chunk) => {
let length = chunk.length;
let charCode;
let start = shouldSkip;
shouldSkip = 0;
let i = start;
for (; i < length; i++) {
charCode = chunk.charCodeAt(i);
if (mode === MODE_UNKNOWN) {
if (charCode === 44) {
mode = MODE_UNKNOWN;
subMode = Array.isArray(stack[stack.length - 1]) ? SUB_MODE_UNKNOWN : SUB_MODE_OBJECT_KEY;
} else if (charCode === 10) {
if (subMode === SUB_MODE_ASYNC_STATUS) {
let id = stack.pop();
if (typeof id !== "number") {
throw new Error("Invalid stack state");
}
let deferred = deferredValues.get(id);
deferred.resolve();
deferredValues.delete(id);
}
mode = MODE_ASYNC;
subMode = MODE_UNKNOWN;
buffer = "";
} else if (charCode === 123) {
let newObj = {};
stack.push(newObj);
references.set(references.size, newObj);
subMode = SUB_MODE_OBJECT_KEY;
} else if (charCode === 125) {
releaseValue(stack.pop(), 1);
} else if (charCode === 91) {
let newArr = [];
stack.push(newArr);
references.set(references.size, newArr);
} else if (charCode === 83) {
let newArr = [];
newArr.__type = ARRAY_TYPE_SET;
newArr.__ref = /* @__PURE__ */ new Set();
stack.push(newArr);
references.set(references.size, newArr.__ref);
i++;
} else if (charCode === 77) {
let newArr = [];
newArr.__type = ARRAY_TYPE_MAP;
newArr.__ref = /* @__PURE__ */ new Map();
stack.push(newArr);
references.set(references.size, newArr.__ref);
i++;
} else if (charCode === 114) {
let newArr = [];
newArr.__type = ARRAY_TYPE_REGEXP;
newArr.__id = references.size;
stack.push(newArr);
references.set(newArr.__id, newArr);
i++;
} else if (charCode === 80) {
let newArr = [];
newArr.__type = ARRAY_TYPE_PLUGIN;
newArr.__id = references.size;
stack.push(newArr);
references.set(newArr.__id, newArr);
i++;
} else if (charCode === 93) {
releaseValue(stack.pop(), 2);
} else if (charCode === 64) {
subMode = SUB_MODE_REFERENCE;
} else if (charCode === 68) {
subMode = SUB_MODE_DATE;
} else if (charCode === 85) {
subMode = SUB_MODE_URL;
} else if (charCode === 115) {
subMode = SUB_MODE_SYMBOL;
} else if (charCode === 34) {
mode = MODE_STRING;
buffer = "";
lastChar = void 0;
numSlashes = 0;
hasSlashes = false;
} else if (charCode === 36) {
subMode = SUB_MODE_PROMISE_ID;
} else if (charCode === 42) {
subMode = SUB_MODE_ASYNC_ITERABLE_ID;
} else if (charCode === 82) {
subMode = SUB_MODE_READABLE_STREAM_ID;
} else if (charCode === 58) {
if (subMode !== SUB_MODE_ASYNC_STATUS) {
throw new SyntaxError("Unexpected character: ':'");
}
stack.push(true);
} else if (charCode === 33) {
if (subMode !== SUB_MODE_ASYNC_STATUS) {
throw new SyntaxError("Unexpected character: '!'");
}
stack.push(false);
} else if (charCode === 117) {
releaseValue(void 0, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 110) {
i += 3;
releaseValue(null, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 116) {
i += 3;
releaseValue(true, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 102) {
i += 4;
releaseValue(false, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 78) {
i += 2;
releaseValue(Number.NaN, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 73) {
releaseValue(Number.POSITIVE_INFINITY, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 105) {
releaseValue(Number.NEGATIVE_INFINITY, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 122) {
releaseValue(-0, 0);
subMode = SUB_MODE_UNKNOWN;
} else if (charCode === 98) {
subMode = SUB_MODE_BIGINT;
} else if (charCode === 45 || // -
charCode === 46 || // .
charCode >= 48 && charCode <= 57) {
mode = MODE_NUMBER;
buffer = chunk[i];
} else if (charCode === 69) {
let newObj = new Error();
stack.push(newObj);
references.set(references.size, newObj);
subMode = SUB_MODE_OBJECT_KEY;
i++;
} else if (charCode === 70) {
let newArr = [];
newArr.__type = ARRAY_TYPE_FORM_DATA;
newArr.__id = references.size;
stack.push(newArr);
references.set(newArr.__id, newArr);
i++;
} else if (charCode === 75) {
let newObj = new TurboBlob();
stack.push(newObj);
references.set(references.size, newObj);
subMode = SUB_MODE_OBJECT_KEY;
i++;
} else if (charCode === 107) {
let newObj = new TurboFile();
stack.push(newObj);
references.set(references.size, newObj);
subMode = SUB_MODE_OBJECT_KEY;
i++;
} else if (charCode === 65) {
subMode = SUB_MODE_ARRAY_BUFFER;
} else if (charCode === 79) {
subMode = SUB_MODE_INT_8_ARRAY;
} else if (charCode === 111) {
subMode = SUB_MODE_UINT_8_ARRAY;
} else if (charCode === 67) {
subMode = SUB_MODE_UINT_8_ARRAY_CLAMPED;
} else if (charCode === 76) {
subMode = SUB_MODE_INT_16_ARRAY;
} else if (charCode === 108) {
subMode = SUB_MODE_UINT_16_ARRAY;
} else if (charCode === 71) {
subMode = SUB_MODE_INT_32_ARRAY;
} else if (charCode === 103) {
subMode = SUB_MODE_UINT_32_ARRAY;
} else if (charCode === 72) {
subMode = SUB_MODE_FLOAT_32_ARRAY;
} else if (charCode === 104) {
subMode = SUB_MODE_FLOAT_64_ARRAY;
} else if (charCode === 74) {
subMode = SUB_MODE_BIG_INT_64_ARRAY;
} else if (charCode === 106) {
subMode = SUB_MODE_BIG_UINT_64_ARRAY;
} else if (charCode === 86) {
subMode = SUB_MODE_DATA_VIEW;
} else {
throw new SyntaxError(`Unexpected character: '${chunk[i]}'`);
}
} else if (mode === MODE_NUMBER || mode === MODE_ASYNC) {
if (charCode === 45 || // -
charCode === 46 || // .
charCode >= 48 && charCode <= 57) {
buffer += chunk[i];
} else {
if (mode === MODE_ASYNC) {
stack.push(Number(buffer));
mode = MODE_UNKNOWN;
subMode = SUB_MODE_ASYNC_STATUS;
i--;
continue;
}
if (subMode === SUB_MODE_PROMISE_ID) {
let id = Number(buffer);
let existing = deferredValues.get(id);
if (existing) {
releaseValue(existing.promise, 0);
} else {
let deferred = new Deferred();
deferredValues.set(id, deferred);
releaseValue(deferred.promise, 0);
}
} else if (subMode === SUB_MODE_ASYNC_ITERABLE_ID) {
let id = Number(buffer);
let existing = deferredValues.get(
id
);
if (existing) {
releaseValue(existing.iterable, 0);
} else {
let deferred = new DeferredAsyncIterable();
deferredValues.set(id, deferred);
releaseValue(deferred.iterable, 0);
}
} else if (subMode === SUB_MODE_READABLE_STREAM_ID) {
let id = Number(buffer);
let existing = deferredValues.get(
id
);
if (existing) {
releaseValue(existing.readable, 0);
} else {
let deferred = new DeferredReadableStream();
deferredValues.set(id, deferred);
releaseValue(deferred.readable, 0);
}
} else {
releaseValue(
subMode === SUB_MODE_BIGINT ? BigInt(buffer) : subMode === SUB_MODE_REFERENCE ? references.get(Number(buffer)) : Number(buffer),
0
);
}
buffer = "";
mode = MODE_UNKNOWN;
subMode = SUB_MODE_UNKNOWN;
i--;
}
} else if (mode === MODE_STRING) {
let stringEnd = false;
for (; i < length; i++) {
charCode = chunk.charCodeAt(i);
if (charCode !== 34 || lastChar === 92 && numSlashes % 2 === 1) {
buffer += chunk[i];
lastChar = charCode;
if (lastChar === 92) {
numSlashes++;
hasSlashes = true;
} else {
numSlashes = 0;
}
} else {
stringEnd = true;
break;
}
}
if (stringEnd) {
let value = hasSlashes ? JSON.parse(`"${buffer}"`) : buffer;
if (subMode === SUB_MODE_OBJECT_KEY) {
stack.push(value);
i++;
} else {
if (subMode === SUB_MODE_DATE) {
value = new Date(value);
references.set(references.size, value);
} else if (subMode === SUB_MODE_SYMBOL) {
value = Symbol.for(value);
} else if (subMode === SUB_MODE_URL) {
value = new URL(value);
references.set(references.size, value);
} else if (subMode === SUB_MODE_ARRAY_BUFFER) {
value = decodeTypedArray(value).buffer;
references.set(references.size, value);
} else if (subMode === SUB_MODE_INT_8_ARRAY) {
value = new Int8Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_UINT_8_ARRAY) {
value = decodeTypedArray(value);
references.set(references.size, value);
} else if (subMode === SUB_MODE_UINT_8_ARRAY_CLAMPED) {
value = new Uint8ClampedArray(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_INT_16_ARRAY) {
value = new Int16Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_UINT_16_ARRAY) {
value = new Uint16Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_INT_32_ARRAY) {
value = new Int32Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_UINT_32_ARRAY) {
value = new Uint32Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_FLOAT_32_ARRAY) {
value = new Float32Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_FLOAT_64_ARRAY) {
value = new Float64Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_BIG_INT_64_ARRAY) {
value = new BigInt64Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_BIG_UINT_64_ARRAY) {
value = new BigUint64Array(decodeTypedArray(value).buffer);
references.set(references.size, value);
} else if (subMode === SUB_MODE_DATA_VIEW) {
value = decodeTypedArray(value);
value = new DataView(
value.buffer,
value.byteOffset,
value.byteLength
);
references.set(references.size, value);
}
releaseValue(value, 0);
}
mode = MODE_UNKNOWN;
subMode = SUB_MODE_UNKNOWN;
} else {
i--;
}
}
}
if (i > length) {
shouldSkip = i - length;
}
};
let reader = stream.getReader();
(async () => {
let read;
while (!(read = await reader.read()).done) {
step(read.value);
}
})().catch((error) => {
if (root) {
root.reject(error);
root = null;
}
for (let deferred of deferredValues.values()) {
deferred.reject(error);
}
}).finally(() => {
reader.releaseLock();
if (root) {
root.reject(new Error("Stream ended before root value was parsed"));
root = null;
}
for (let deferred of deferredValues.values()) {
deferred.reject(new Error("Stream ended before promise was resolved"));
}
});
return root.promise;
}
function decodeTypedArray(base64) {
const decodedStr = atob(base64);
const uint8Array = new Uint8Array(decodedStr.length);
for (let i = 0; i < decodedStr.length; i++) {
uint8Array[i] = decodedStr.charCodeAt(i);
}
return uint8Array;
}
// src/encode.ts
var { NEGATIVE_INFINITY, POSITIVE_INFINITY, isNaN: nan } = Number;
var ASYNC_FRAME_TYPE_PROMISE = 1;
var ASYNC_FRAME_TYPE_ITERABLE = 2;
function encode(value, { plugins = [], redactErrors = true, signal } = {}) {
const aborted = () => (signal == null ? void 0 : signal.aborted) ?? false;
const waitForAbort = new Promise((_, reject) => {
signal == null ? void 0 : signal.addEventListener("abort", (reason) => {
reject(new DOMException("Aborted", "AbortError"));
});
});
return new ReadableStream({
async start(controller) {
let refCache = /* @__PURE__ */ new WeakMap();
let asyncCache = /* @__PURE__ */ new WeakMap();
let counters = { refId: 0, promiseId: 0 };
let wg = new WaitGroup();
let chunks = [];
let encode2 = (value2) => {
encodeSync(
value2,
chunks,
refCache,
asyncCache,
promises,
counters,
plugins,
redactErrors
);
controller.enqueue(chunks.join("") + "\n");
chunks.length = 0;
};
let handlePromiseResolved = (id, value2) => {
wg.done();
if (aborted())
return;
controller.enqueue(`${id}${STR_SUCCESS}`);
encode2(value2);
};
let handlePromiseRejected = (id, error) => {
wg.done();
if (aborted())
return;
controller.enqueue(`${id}${STR_FAILURE}`);
encode2(error);
};
let promises = {
push: (...promiseFrames) => {
for (let [type, id, promise] of promiseFrames) {
wg.add();
if (type === ASYNC_FRAME_TYPE_PROMISE) {
Promise.race([promise, waitForAbort]).then(
handlePromiseResolved.bind(null, id),
handlePromiseRejected.bind(null, id)
);
} else {
(async () => {
let iterator = promise[Symbol.asyncIterator]();
let result;
do {
result = await iterator.next();
if (aborted())
return;
if (!result.done) {
controller.enqueue(`${id}${STR_SUCCESS}`);
encode2(result.value);
}
} while (!result.done);
})().then(
() => {
if (aborted())
return;
controller.enqueue(`${id}
`);
},
(error) => {
if (aborted())
return;
controller.enqueue(`${id}${STR_FAILURE}`);
encode2(error);
}
).finally(() => {
wg.done();
});
}
}
}
};
try {
encode2(value);
do {
await Promise.race([wg.wait(), waitForAbort]);
} while (wg.p > 0);
controller.close();
} catch (error) {
controller.error(error);
}
}
});
}
var ENCODE_FRAME_TYPE_NEEDS_ENCODING = 1;
var ENCODE_FRAME_TYPE_ALREADY_ENCODED = 2;
var EncodeFrame = class {
type;
prefix;
value;
constructor(type, prefix, value) {
this.type = type;
this.prefix = prefix;
this.value = value;
}
};
function encodeSync(value, chunks, refs, promises, asyncFrames, counters, plugins, redactErrors) {
let encodeStack = [
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
"",
value
)
];
let frame;
encodeLoop:
while ((frame = encodeStack.pop()) !== void 0) {
if (frame.type === ENCODE_FRAME_TYPE_ALREADY_ENCODED) {
chunks.push(frame.prefix);
continue;
}
let { prefix, value: value2 } = frame;
chunks.push(prefix);
if (value2 === void 0) {
chunks.push(STR_UNDEFINED);
continue;
}
if (value2 === null) {
chunks.push(STR_NULL);
continue;
}
if (value2 === true) {
chunks.push(STR_TRUE);
continue;
}
if (value2 === false) {
chunks.push(STR_FALSE);
continue;
}
const typeOfValue = typeof value2;
if (typeOfValue === "object") {
if (value2 instanceof Promise || typeof value2.then === "function") {
let existingId = promises.get(value2);
if (existingId !== void 0) {
chunks.push(STR_PROMISE, existingId.toString());
continue;
}
let promiseId = counters.promiseId++;
promises.set(value2, promiseId);
chunks.push(STR_PROMISE, promiseId.toString());
asyncFrames.push([
ASYNC_FRAME_TYPE_PROMISE,
promiseId,
value2
]);
continue;
}
if (value2 instanceof ReadableStream) {
let existingId = promises.get(value2);
if (existingId !== void 0) {
chunks.push(STR_READABLE_STREAM, existingId.toString());
continue;
}
let iterableId = counters.promiseId++;
promises.set(value2, iterableId);
chunks.push(STR_READABLE_STREAM, iterableId.toString());
asyncFrames.push([
ASYNC_FRAME_TYPE_ITERABLE,
iterableId,
{
[Symbol.asyncIterator]: async function* () {
let reader = value2.getReader();
try {
while (true) {
let { done, value: value3 } = await reader.read();
if (done) {
return;
}
yield value3;
}
} finally {
reader.releaseLock();
}
}
}
]);
continue;
}
if (typeof value2[Symbol.asyncIterator] === "function") {
let existingId = promises.get(value2);
if (existingId !== void 0) {
chunks.push(STR_ASYNC_ITERABLE, existingId.toString());
continue;
}
let iterableId = counters.promiseId++;
promises.set(value2, iterableId);
chunks.push(STR_ASYNC_ITERABLE, iterableId.toString());
asyncFrames.push([
ASYNC_FRAME_TYPE_ITERABLE,
iterableId,
value2
]);
continue;
}
{
let existingId = refs.get(value2);
if (existingId !== void 0) {
chunks.push(STR_REFERENCE_SYMBOL, existingId.toString());
continue;
}
refs.set(value2, counters.refId++);
}
if (value2 instanceof Date) {
chunks.push(STR_DATE, '"', value2.toJSON(), '"');
} else if (value2 instanceof RegExp) {
chunks.push(STR_REGEXP, JSON.stringify([value2.source, value2.flags]));
} else if (value2 instanceof URL) {
chunks.push(STR_URL, JSON.stringify(value2));
} else if (value2 instanceof ArrayBuffer) {
chunks.push(
STR_ARRAY_BUFFER,
stringifyTypedArray(new Uint8Array(value2))
);
} else if (value2 instanceof Int8Array) {
chunks.push(STR_INT_8_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Uint8Array) {
chunks.push(STR_UINT_8_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Uint8ClampedArray) {
chunks.push(STR_UINT_8_ARRAY_CLAMPED, stringifyTypedArray(value2));
} else if (value2 instanceof Int16Array) {
chunks.push(STR_INT_16_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Uint16Array) {
chunks.push(STR_UINT_16_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Int32Array) {
chunks.push(STR_INT_32_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Uint32Array) {
chunks.push(STR_UINT_32_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Float32Array) {
chunks.push(STR_FLOAT_32_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof Float64Array) {
chunks.push(STR_FLOAT_64_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof BigInt64Array) {
chunks.push(STR_BIG_INT_64_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof BigUint64Array) {
chunks.push(STR_BIG_UINT_64_ARRAY, stringifyTypedArray(value2));
} else if (value2 instanceof DataView) {
chunks.push(STR_DATA_VIEW, stringifyTypedArray(value2));
} else if (value2 instanceof FormData) {
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
STR_FORM_DATA,
Array.from(value2.entries())
)
);
} else if (SUPPORTS_FILE && value2 instanceof File) {
encodeStack.push(
new EncodeFrame(ENCODE_FRAME_TYPE_NEEDS_ENCODING, STR_FILE, {
promise: value2.arrayBuffer(),
size: value2.size,
type: value2.type,
name: value2.name,
lastModified: value2.lastModified
})
);
} else if (value2 instanceof Blob) {
encodeStack.push(
new EncodeFrame(ENCODE_FRAME_TYPE_NEEDS_ENCODING, STR_BLOB, {
promise: value2.arrayBuffer(),
size: value2.size,
type: value2.type
})
);
} else if (value2 instanceof Error) {
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
STR_ERROR,
prepareErrorForEncoding(value2, redactErrors)
)
);
} else if (typeof value2.toJSON === "function") {
const newValue = value2.toJSON();
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
"",
newValue
)
);
if (typeof newValue === "object") {
counters.refId--;
} else {
refs.delete(value2);
}
} else {
{
let isIterable = typeof value2[Symbol.iterator] === "function";
if (isIterable) {
let isArray = Array.isArray(value2);
let toEncode = isArray ? value2 : Array.from(value2);
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_ALREADY_ENCODED,
"]",
void 0
)
);
for (let i = toEncode.length - 1; i >= 0; i--) {
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
i === 0 ? "" : ",",
toEncode[i]
)
);
}
chunks.push(
isArray ? "[" : value2 instanceof Set ? `${STR_SET}[` : value2 instanceof Map ? `${STR_MAP}[` : "["
);
continue;
}
}
{
let pluginsLength = plugins.length;
for (let i = 0; i < pluginsLength; i++) {
let result = plugins[i](value2);
if (Array.isArray(result)) {
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
STR_PLUGIN,
result
)
);
counters.refId--;
refs.delete(value2);
continue encodeLoop;
}
}
}
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_ALREADY_ENCODED,
"}",
void 0
)
);
{
let keys = Object.keys(value2);
let end = keys.length;
let encodeFrames = new Array(end);
end -= 1;
for (let i = keys.length - 1; i >= 0; i--) {
let key = keys[i];
let prefix2 = i > 0 ? "," : "";
encodeFrames[end - i] = new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
`${prefix2}${JSON.stringify(key)}:`,
value2[key]
);
}
encodeStack.push(...encodeFrames);
}
chunks.push("{");
}
} else if (typeOfValue === "string") {
chunks.push(JSON.stringify(value2));
} else if (typeOfValue === "number") {
if (nan(value2)) {
chunks.push(STR_NaN);
} else if (value2 === POSITIVE_INFINITY) {
chunks.push(STR_INFINITY);
} else if (value2 === NEGATIVE_INFINITY) {
chunks.push(STR_NEGATIVE_INFINITY);
} else if (Object.is(value2, -0)) {
chunks.push(STR_NEGATIVE_ZERO);
} else {
chunks.push(value2.toString());
}
} else if (typeOfValue === "bigint") {
chunks.push(STR_BIGINT, value2.toString());
} else if (typeOfValue === "symbol") {
let symbolKey = Symbol.keyFor(value2);
if (typeof symbolKey === "string") {
chunks.push(STR_SYMBOL, JSON.stringify(symbolKey));
} else {
chunks.push(STR_UNDEFINED);
}
} else {
let pluginsLength = plugins.length;
for (let i = 0; i < pluginsLength; i++) {
let result = plugins[i](value2);
if (Array.isArray(result)) {
encodeStack.push(
new EncodeFrame(
ENCODE_FRAME_TYPE_NEEDS_ENCODING,
STR_PLUGIN,
result
)
);
continue encodeLoop;
}
}
chunks.push(STR_UNDEFINED);
}
}
}
function prepareErrorForEncoding(error, redactErrors) {
const shouldRedact = redactErrors === true || typeof redactErrors === "string" || typeof redactErrors === "undefined";
const redacted = typeof redactErrors === "string" ? redactErrors : STR_REDACTED;
return {
name: shouldRedact ? "Error" : error.name,
message: shouldRedact ? redacted : error.message,
stack: shouldRedact ? void 0 : error.stack,
cause: error.cause
};
}
function stringifyTypedArray(content) {
const view = new Uint8Array(
content.buffer,
content.byteOffset,
content.byteLength
);
return `"${btoa(String.fromCharCode.apply(String, view))}"`;
}
export {
decode,
encode
};