Add yet-another-react-lightbox package and update .gitignore to exclude node_modules

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-12 18:50:30 +00:00
parent bd2a5570a9
commit c92f4a5edd
9304 changed files with 29 additions and 2008667 deletions

View File

@@ -1,7 +0,0 @@
Copyright (c) 2018-19 [these people](https://github.com/rich-harris/devalue/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,31 +0,0 @@
# Turbo Stream <br> [![turbo-stream's badge](https://deno.bundlejs.com/?q=turbo-stream&badge=detailed)](https://bundlejs.com/?q=turbo-stream)
A streaming data transport format that aims to support built-in features such as Promises, Dates, RegExps, Maps, Sets and more.
Decode runtime size: [![turbo-stream's badge](https://deno.bundlejs.com/badge?q=turbo-stream&badge=detailed&treeshake=%5B%7B+decode+%7D%5D)](https://bundlejs.com/?q=turbo-stream&treeshake=%5B%7B+decode+%7D%5D)
## Shout Out!
Shout out to Rich Harris and his https://github.com/rich-harris/devalue project. Devalue has heavily influenced this project and portions
of the code have been directly lifted from it. I highly recommend checking it out if you need something more cusomizable or without streaming support.
## Installation
```bash
npm install turbo-stream
```
## Usage
```js
import { decode, encode } from "turbo-stream";
const encodedStream = encode(Promise.resolve(42));
const decoded = await decode(encodedStream);
console.log(decoded.value); // a Promise
console.log(await decoded.value); // 42
await decoded.done; // wait for the stream to finish
```
Stackblitz: https://stackblitz.com/edit/stackblitz-starters-2wm7dh?file=index.js
React SSR Example: https://github.com/jacob-ebey/turbo-stream-react-example

View File

@@ -1,2 +0,0 @@
import { type ThisEncode } from "./utils.js";
export declare function flatten(this: ThisEncode, input: unknown): number | [number];

View File

@@ -1,203 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.flatten = void 0;
const utils_js_1 = require("./utils.js");
function flatten(input) {
const { indices } = this;
const existing = indices.get(input);
if (existing)
return [existing];
if (input === undefined)
return utils_js_1.UNDEFINED;
if (input === null)
return utils_js_1.NULL;
if (Number.isNaN(input))
return utils_js_1.NAN;
if (input === Number.POSITIVE_INFINITY)
return utils_js_1.POSITIVE_INFINITY;
if (input === Number.NEGATIVE_INFINITY)
return utils_js_1.NEGATIVE_INFINITY;
if (input === 0 && 1 / input < 0)
return utils_js_1.NEGATIVE_ZERO;
const index = this.index++;
indices.set(input, index);
stringify.call(this, input, index);
return index;
}
exports.flatten = flatten;
function stringify(input, index) {
const { deferred, plugins, postPlugins } = this;
const str = this.stringified;
const stack = [[input, index]];
while (stack.length > 0) {
const [input, index] = stack.pop();
const partsForObj = (obj) => Object.keys(obj)
.map((k) => `"_${flatten.call(this, k)}":${flatten.call(this, obj[k])}`)
.join(",");
let error = null;
switch (typeof input) {
case "boolean":
case "number":
case "string":
str[index] = JSON.stringify(input);
break;
case "bigint":
str[index] = `["${utils_js_1.TYPE_BIGINT}","${input}"]`;
break;
case "symbol": {
const keyFor = Symbol.keyFor(input);
if (!keyFor) {
error = new Error("Cannot encode symbol unless created with Symbol.for()");
}
else {
str[index] = `["${utils_js_1.TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
}
break;
}
case "object": {
if (!input) {
str[index] = `${utils_js_1.NULL}`;
break;
}
const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
}
str[index] += "]";
break;
}
}
}
if (!pluginHandled) {
let result = isArray ? "[" : "{";
if (isArray) {
for (let i = 0; i < input.length; i++)
result +=
(i ? "," : "") +
(i in input ? flatten.call(this, input[i]) : utils_js_1.HOLE);
str[index] = `${result}]`;
}
else if (input instanceof Date) {
str[index] = `["${utils_js_1.TYPE_DATE}",${input.getTime()}]`;
}
else if (input instanceof URL) {
str[index] = `["${utils_js_1.TYPE_URL}",${JSON.stringify(input.href)}]`;
}
else if (input instanceof RegExp) {
str[index] = `["${utils_js_1.TYPE_REGEXP}",${JSON.stringify(input.source)},${JSON.stringify(input.flags)}]`;
}
else if (input instanceof Set) {
if (input.size > 0) {
str[index] = `["${utils_js_1.TYPE_SET}",${[...input]
.map((val) => flatten.call(this, val))
.join(",")}]`;
}
else {
str[index] = `["${utils_js_1.TYPE_SET}"]`;
}
}
else if (input instanceof Map) {
if (input.size > 0) {
str[index] = `["${utils_js_1.TYPE_MAP}",${[...input]
.flatMap(([k, v]) => [
flatten.call(this, k),
flatten.call(this, v),
])
.join(",")}]`;
}
else {
str[index] = `["${utils_js_1.TYPE_MAP}"]`;
}
}
else if (input instanceof Promise) {
str[index] = `["${utils_js_1.TYPE_PROMISE}",${index}]`;
deferred[index] = input;
}
else if (input instanceof Error) {
str[index] = `["${utils_js_1.TYPE_ERROR}",${JSON.stringify(input.message)}`;
if (input.name !== "Error") {
str[index] += `,${JSON.stringify(input.name)}`;
}
str[index] += "]";
}
else if (Object.getPrototypeOf(input) === null) {
str[index] = `["${utils_js_1.TYPE_NULL_OBJECT}",{${partsForObj(input)}}]`;
}
else if (isPlainObject(input)) {
str[index] = `{${partsForObj(input)}}`;
}
else {
error = new Error("Cannot encode object with prototype");
}
}
break;
}
default: {
const isArray = Array.isArray(input);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
}
str[index] += "]";
break;
}
}
}
if (!pluginHandled) {
error = new Error("Cannot encode function or unexpected type");
}
}
}
if (error) {
let pluginHandled = false;
if (postPlugins) {
for (const plugin of postPlugins) {
const pluginResult = plugin(input);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index] += `,${rest
.map((v) => flatten.call(this, v))
.join(",")}`;
}
str[index] += "]";
break;
}
}
}
if (!pluginHandled) {
throw error;
}
}
}
}
const objectProtoNames = Object.getOwnPropertyNames(Object.prototype)
.sort()
.join("\0");
function isPlainObject(thing) {
const proto = Object.getPrototypeOf(thing);
return (proto === Object.prototype ||
proto === null ||
Object.getOwnPropertyNames(proto).sort().join("\0") === objectProtoNames);
}

View File

@@ -1,13 +0,0 @@
import { type DecodePlugin, type EncodePlugin } from "./utils.js";
export type { DecodePlugin, EncodePlugin };
export declare function decode(readable: ReadableStream<Uint8Array>, options?: {
plugins?: DecodePlugin[];
}): Promise<{
done: Promise<undefined>;
value: unknown;
}>;
export declare function encode(input: unknown, options?: {
plugins?: EncodePlugin[];
postPlugins?: EncodePlugin[];
signal?: AbortSignal;
}): ReadableStream<Uint8Array>;

View File

@@ -1,207 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encode = exports.decode = void 0;
const flatten_js_1 = require("./flatten.js");
const unflatten_js_1 = require("./unflatten.js");
const utils_js_1 = require("./utils.js");
async function decode(readable, options) {
const { plugins } = options ?? {};
const done = new utils_js_1.Deferred();
const reader = readable
.pipeThrough((0, utils_js_1.createLineSplittingTransform)())
.getReader();
const decoder = {
values: [],
hydrated: [],
deferred: {},
plugins,
};
const decoded = await decodeInitial.call(decoder, reader);
let donePromise = done.promise;
if (decoded.done) {
done.resolve();
}
else {
donePromise = decodeDeferred
.call(decoder, reader)
.then(done.resolve)
.catch((reason) => {
for (const deferred of Object.values(decoder.deferred)) {
deferred.reject(reason);
}
done.reject(reason);
});
}
return {
done: donePromise.then(() => reader.closed),
value: decoded.value,
};
}
exports.decode = decode;
async function decodeInitial(reader) {
const read = await reader.read();
if (!read.value) {
throw new SyntaxError();
}
let line;
try {
line = JSON.parse(read.value);
}
catch (reason) {
throw new SyntaxError();
}
return {
done: read.done,
value: unflatten_js_1.unflatten.call(this, line),
};
}
async function decodeDeferred(reader) {
let read = await reader.read();
while (!read.done) {
if (!read.value)
continue;
const line = read.value;
switch (line[0]) {
case utils_js_1.TYPE_PROMISE: {
const colonIndex = line.indexOf(":");
const deferredId = Number(line.slice(1, colonIndex));
const deferred = this.deferred[deferredId];
if (!deferred) {
throw new Error(`Deferred ID ${deferredId} not found in stream`);
}
const lineData = line.slice(colonIndex + 1);
let jsonLine;
try {
jsonLine = JSON.parse(lineData);
}
catch (reason) {
throw new SyntaxError();
}
const value = unflatten_js_1.unflatten.call(this, jsonLine);
deferred.resolve(value);
break;
}
case utils_js_1.TYPE_ERROR: {
const colonIndex = line.indexOf(":");
const deferredId = Number(line.slice(1, colonIndex));
const deferred = this.deferred[deferredId];
if (!deferred) {
throw new Error(`Deferred ID ${deferredId} not found in stream`);
}
const lineData = line.slice(colonIndex + 1);
let jsonLine;
try {
jsonLine = JSON.parse(lineData);
}
catch (reason) {
throw new SyntaxError();
}
const value = unflatten_js_1.unflatten.call(this, jsonLine);
deferred.reject(value);
break;
}
default:
throw new SyntaxError();
}
read = await reader.read();
}
}
function encode(input, options) {
const { plugins, postPlugins, signal } = options ?? {};
const encoder = {
deferred: {},
index: 0,
indices: new Map(),
stringified: [],
plugins,
postPlugins,
signal,
};
const textEncoder = new TextEncoder();
let lastSentIndex = 0;
const readable = new ReadableStream({
async start(controller) {
const id = flatten_js_1.flatten.call(encoder, input);
if (Array.isArray(id)) {
throw new Error("This should never happen");
}
if (id < 0) {
controller.enqueue(textEncoder.encode(`${id}\n`));
}
else {
controller.enqueue(textEncoder.encode(`[${encoder.stringified.join(",")}]\n`));
lastSentIndex = encoder.stringified.length - 1;
}
const seenPromises = new WeakSet();
while (Object.keys(encoder.deferred).length > 0) {
for (const [deferredId, deferred] of Object.entries(encoder.deferred)) {
if (seenPromises.has(deferred))
continue;
seenPromises.add((encoder.deferred[Number(deferredId)] = raceSignal(deferred, encoder.signal)
.then((resolved) => {
const id = flatten_js_1.flatten.call(encoder, resolved);
if (Array.isArray(id)) {
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_PROMISE}${deferredId}:[["${utils_js_1.TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n`));
encoder.index++;
lastSentIndex++;
}
else if (id < 0) {
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_PROMISE}${deferredId}:${id}\n`));
}
else {
const values = encoder.stringified
.slice(lastSentIndex + 1)
.join(",");
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_PROMISE}${deferredId}:[${values}]\n`));
lastSentIndex = encoder.stringified.length - 1;
}
}, (reason) => {
if (!reason ||
typeof reason !== "object" ||
!(reason instanceof Error)) {
reason = new Error("An unknown error occurred");
}
const id = flatten_js_1.flatten.call(encoder, reason);
if (Array.isArray(id)) {
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_ERROR}${deferredId}:[["${utils_js_1.TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n`));
encoder.index++;
lastSentIndex++;
}
else if (id < 0) {
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_ERROR}${deferredId}:${id}\n`));
}
else {
const values = encoder.stringified
.slice(lastSentIndex + 1)
.join(",");
controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_ERROR}${deferredId}:[${values}]\n`));
lastSentIndex = encoder.stringified.length - 1;
}
})
.finally(() => {
delete encoder.deferred[Number(deferredId)];
})));
}
await Promise.race(Object.values(encoder.deferred));
}
await Promise.all(Object.values(encoder.deferred));
controller.close();
},
});
return readable;
}
exports.encode = encode;
function raceSignal(promise, signal) {
if (!signal)
return promise;
if (signal.aborted)
return Promise.reject(signal.reason || new Error("Signal was aborted."));
const abort = new Promise((resolve, reject) => {
signal.addEventListener("abort", (event) => {
reject(signal.reason || new Error("Signal was aborted."));
});
promise.then(resolve).catch(reject);
});
abort.catch(() => { });
return Promise.race([abort, promise]);
}

View File

@@ -1,673 +0,0 @@
// src/utils.ts
var HOLE = -1;
var NAN = -2;
var NEGATIVE_INFINITY = -3;
var NEGATIVE_ZERO = -4;
var NULL = -5;
var POSITIVE_INFINITY = -6;
var UNDEFINED = -7;
var TYPE_BIGINT = "B";
var TYPE_DATE = "D";
var TYPE_ERROR = "E";
var TYPE_MAP = "M";
var TYPE_NULL_OBJECT = "N";
var TYPE_PROMISE = "P";
var TYPE_REGEXP = "R";
var TYPE_SET = "S";
var TYPE_SYMBOL = "Y";
var TYPE_URL = "U";
var TYPE_PREVIOUS_RESOLVED = "Z";
var Deferred = class {
promise;
resolve;
reject;
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
};
function createLineSplittingTransform() {
const decoder = new TextDecoder();
let leftover = "";
return new TransformStream({
transform(chunk, controller) {
const str = decoder.decode(chunk, { stream: true });
const parts = (leftover + str).split("\n");
leftover = parts.pop() || "";
for (const part of parts) {
controller.enqueue(part);
}
},
flush(controller) {
if (leftover) {
controller.enqueue(leftover);
}
}
});
}
// src/flatten.ts
function flatten(input) {
const { indices } = this;
const existing = indices.get(input);
if (existing)
return [existing];
if (input === void 0)
return UNDEFINED;
if (input === null)
return NULL;
if (Number.isNaN(input))
return NAN;
if (input === Number.POSITIVE_INFINITY)
return POSITIVE_INFINITY;
if (input === Number.NEGATIVE_INFINITY)
return NEGATIVE_INFINITY;
if (input === 0 && 1 / input < 0)
return NEGATIVE_ZERO;
const index = this.index++;
indices.set(input, index);
stringify.call(this, input, index);
return index;
}
function stringify(input, index) {
const { deferred, plugins, postPlugins } = this;
const str = this.stringified;
const stack = [[input, index]];
while (stack.length > 0) {
const [input2, index2] = stack.pop();
const partsForObj = (obj) => Object.keys(obj).map((k) => `"_${flatten.call(this, k)}":${flatten.call(this, obj[k])}`).join(",");
let error = null;
switch (typeof input2) {
case "boolean":
case "number":
case "string":
str[index2] = JSON.stringify(input2);
break;
case "bigint":
str[index2] = `["${TYPE_BIGINT}","${input2}"]`;
break;
case "symbol": {
const keyFor = Symbol.keyFor(input2);
if (!keyFor) {
error = new Error(
"Cannot encode symbol unless created with Symbol.for()"
);
} else {
str[index2] = `["${TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
}
break;
}
case "object": {
if (!input2) {
str[index2] = `${NULL}`;
break;
}
const isArray = Array.isArray(input2);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input2);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
}
str[index2] += "]";
break;
}
}
}
if (!pluginHandled) {
let result = isArray ? "[" : "{";
if (isArray) {
for (let i = 0; i < input2.length; i++)
result += (i ? "," : "") + (i in input2 ? flatten.call(this, input2[i]) : HOLE);
str[index2] = `${result}]`;
} else if (input2 instanceof Date) {
str[index2] = `["${TYPE_DATE}",${input2.getTime()}]`;
} else if (input2 instanceof URL) {
str[index2] = `["${TYPE_URL}",${JSON.stringify(input2.href)}]`;
} else if (input2 instanceof RegExp) {
str[index2] = `["${TYPE_REGEXP}",${JSON.stringify(
input2.source
)},${JSON.stringify(input2.flags)}]`;
} else if (input2 instanceof Set) {
if (input2.size > 0) {
str[index2] = `["${TYPE_SET}",${[...input2].map((val) => flatten.call(this, val)).join(",")}]`;
} else {
str[index2] = `["${TYPE_SET}"]`;
}
} else if (input2 instanceof Map) {
if (input2.size > 0) {
str[index2] = `["${TYPE_MAP}",${[...input2].flatMap(([k, v]) => [
flatten.call(this, k),
flatten.call(this, v)
]).join(",")}]`;
} else {
str[index2] = `["${TYPE_MAP}"]`;
}
} else if (input2 instanceof Promise) {
str[index2] = `["${TYPE_PROMISE}",${index2}]`;
deferred[index2] = input2;
} else if (input2 instanceof Error) {
str[index2] = `["${TYPE_ERROR}",${JSON.stringify(input2.message)}`;
if (input2.name !== "Error") {
str[index2] += `,${JSON.stringify(input2.name)}`;
}
str[index2] += "]";
} else if (Object.getPrototypeOf(input2) === null) {
str[index2] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input2)}}]`;
} else if (isPlainObject(input2)) {
str[index2] = `{${partsForObj(input2)}}`;
} else {
error = new Error("Cannot encode object with prototype");
}
}
break;
}
default: {
const isArray = Array.isArray(input2);
let pluginHandled = false;
if (!isArray && plugins) {
for (const plugin of plugins) {
const pluginResult = plugin(input2);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
}
str[index2] += "]";
break;
}
}
}
if (!pluginHandled) {
error = new Error("Cannot encode function or unexpected type");
}
}
}
if (error) {
let pluginHandled = false;
if (postPlugins) {
for (const plugin of postPlugins) {
const pluginResult = plugin(input2);
if (Array.isArray(pluginResult)) {
pluginHandled = true;
const [pluginIdentifier, ...rest] = pluginResult;
str[index2] = `[${JSON.stringify(pluginIdentifier)}`;
if (rest.length > 0) {
str[index2] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
}
str[index2] += "]";
break;
}
}
}
if (!pluginHandled) {
throw error;
}
}
}
}
var objectProtoNames = Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
function isPlainObject(thing) {
const proto = Object.getPrototypeOf(thing);
return proto === Object.prototype || proto === null || Object.getOwnPropertyNames(proto).sort().join("\0") === objectProtoNames;
}
// src/unflatten.ts
var globalObj = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : void 0;
function unflatten(parsed) {
const { hydrated, values } = this;
if (typeof parsed === "number")
return hydrate.call(this, parsed);
if (!Array.isArray(parsed) || !parsed.length)
throw new SyntaxError();
const startIndex = values.length;
for (const value of parsed) {
values.push(value);
}
hydrated.length = values.length;
return hydrate.call(this, startIndex);
}
function hydrate(index) {
const { hydrated, values, deferred, plugins } = this;
let result;
const stack = [
[
index,
(v) => {
result = v;
}
]
];
let postRun = [];
while (stack.length > 0) {
const [index2, set] = stack.pop();
switch (index2) {
case UNDEFINED:
set(void 0);
continue;
case NULL:
set(null);
continue;
case NAN:
set(NaN);
continue;
case POSITIVE_INFINITY:
set(Infinity);
continue;
case NEGATIVE_INFINITY:
set(-Infinity);
continue;
case NEGATIVE_ZERO:
set(-0);
continue;
}
if (hydrated[index2]) {
set(hydrated[index2]);
continue;
}
const value = values[index2];
if (!value || typeof value !== "object") {
hydrated[index2] = value;
set(value);
continue;
}
if (Array.isArray(value)) {
if (typeof value[0] === "string") {
const [type, b, c] = value;
switch (type) {
case TYPE_DATE:
set(hydrated[index2] = new Date(b));
continue;
case TYPE_URL:
set(hydrated[index2] = new URL(b));
continue;
case TYPE_BIGINT:
set(hydrated[index2] = BigInt(b));
continue;
case TYPE_REGEXP:
set(hydrated[index2] = new RegExp(b, c));
continue;
case TYPE_SYMBOL:
set(hydrated[index2] = Symbol.for(b));
continue;
case TYPE_SET:
const newSet = /* @__PURE__ */ new Set();
hydrated[index2] = newSet;
for (let i = 1; i < value.length; i++)
stack.push([
value[i],
(v) => {
newSet.add(v);
}
]);
set(newSet);
continue;
case TYPE_MAP:
const map = /* @__PURE__ */ new Map();
hydrated[index2] = map;
for (let i = 1; i < value.length; i += 2) {
const r = [];
stack.push([
value[i + 1],
(v) => {
r[1] = v;
}
]);
stack.push([
value[i],
(k) => {
r[0] = k;
}
]);
postRun.push(() => {
map.set(r[0], r[1]);
});
}
set(map);
continue;
case TYPE_NULL_OBJECT:
const obj = /* @__PURE__ */ Object.create(null);
hydrated[index2] = obj;
for (const key of Object.keys(b).reverse()) {
const r = [];
stack.push([
b[key],
(v) => {
r[1] = v;
}
]);
stack.push([
Number(key.slice(1)),
(k) => {
r[0] = k;
}
]);
postRun.push(() => {
obj[r[0]] = r[1];
});
}
set(obj);
continue;
case TYPE_PROMISE:
if (hydrated[b]) {
set(hydrated[index2] = hydrated[b]);
} else {
const d = new Deferred();
deferred[b] = d;
set(hydrated[index2] = d.promise);
}
continue;
case TYPE_ERROR:
const [, message, errorType] = value;
let error = errorType && globalObj && globalObj[errorType] ? new globalObj[errorType](message) : new Error(message);
hydrated[index2] = error;
set(error);
continue;
case TYPE_PREVIOUS_RESOLVED:
set(hydrated[index2] = hydrated[b]);
continue;
default:
if (Array.isArray(plugins)) {
const r = [];
const vals = value.slice(1);
for (let i = 0; i < vals.length; i++) {
const v = vals[i];
stack.push([
v,
(v2) => {
r[i] = v2;
}
]);
}
postRun.push(() => {
for (const plugin of plugins) {
const result2 = plugin(value[0], ...r);
if (result2) {
set(hydrated[index2] = result2.value);
return;
}
}
throw new SyntaxError();
});
continue;
}
throw new SyntaxError();
}
} else {
const array = [];
hydrated[index2] = array;
for (let i = 0; i < value.length; i++) {
const n = value[i];
if (n !== HOLE) {
stack.push([
n,
(v) => {
array[i] = v;
}
]);
}
}
set(array);
continue;
}
} else {
const object = {};
hydrated[index2] = object;
for (const key of Object.keys(value).reverse()) {
const r = [];
stack.push([
value[key],
(v) => {
r[1] = v;
}
]);
stack.push([
Number(key.slice(1)),
(k) => {
r[0] = k;
}
]);
postRun.push(() => {
object[r[0]] = r[1];
});
}
set(object);
continue;
}
}
while (postRun.length > 0) {
postRun.pop()();
}
return result;
}
// src/turbo-stream.ts
async function decode(readable, options) {
const { plugins } = options ?? {};
const done = new Deferred();
const reader = readable.pipeThrough(createLineSplittingTransform()).getReader();
const decoder = {
values: [],
hydrated: [],
deferred: {},
plugins
};
const decoded = await decodeInitial.call(decoder, reader);
let donePromise = done.promise;
if (decoded.done) {
done.resolve();
} else {
donePromise = decodeDeferred.call(decoder, reader).then(done.resolve).catch((reason) => {
for (const deferred of Object.values(decoder.deferred)) {
deferred.reject(reason);
}
done.reject(reason);
});
}
return {
done: donePromise.then(() => reader.closed),
value: decoded.value
};
}
async function decodeInitial(reader) {
const read = await reader.read();
if (!read.value) {
throw new SyntaxError();
}
let line;
try {
line = JSON.parse(read.value);
} catch (reason) {
throw new SyntaxError();
}
return {
done: read.done,
value: unflatten.call(this, line)
};
}
async function decodeDeferred(reader) {
let read = await reader.read();
while (!read.done) {
if (!read.value)
continue;
const line = read.value;
switch (line[0]) {
case TYPE_PROMISE: {
const colonIndex = line.indexOf(":");
const deferredId = Number(line.slice(1, colonIndex));
const deferred = this.deferred[deferredId];
if (!deferred) {
throw new Error(`Deferred ID ${deferredId} not found in stream`);
}
const lineData = line.slice(colonIndex + 1);
let jsonLine;
try {
jsonLine = JSON.parse(lineData);
} catch (reason) {
throw new SyntaxError();
}
const value = unflatten.call(this, jsonLine);
deferred.resolve(value);
break;
}
case TYPE_ERROR: {
const colonIndex = line.indexOf(":");
const deferredId = Number(line.slice(1, colonIndex));
const deferred = this.deferred[deferredId];
if (!deferred) {
throw new Error(`Deferred ID ${deferredId} not found in stream`);
}
const lineData = line.slice(colonIndex + 1);
let jsonLine;
try {
jsonLine = JSON.parse(lineData);
} catch (reason) {
throw new SyntaxError();
}
const value = unflatten.call(this, jsonLine);
deferred.reject(value);
break;
}
default:
throw new SyntaxError();
}
read = await reader.read();
}
}
function encode(input, options) {
const { plugins, postPlugins, signal } = options ?? {};
const encoder = {
deferred: {},
index: 0,
indices: /* @__PURE__ */ new Map(),
stringified: [],
plugins,
postPlugins,
signal
};
const textEncoder = new TextEncoder();
let lastSentIndex = 0;
const readable = new ReadableStream({
async start(controller) {
const id = flatten.call(encoder, input);
if (Array.isArray(id)) {
throw new Error("This should never happen");
}
if (id < 0) {
controller.enqueue(textEncoder.encode(`${id}
`));
} else {
controller.enqueue(
textEncoder.encode(`[${encoder.stringified.join(",")}]
`)
);
lastSentIndex = encoder.stringified.length - 1;
}
const seenPromises = /* @__PURE__ */ new WeakSet();
while (Object.keys(encoder.deferred).length > 0) {
for (const [deferredId, deferred] of Object.entries(encoder.deferred)) {
if (seenPromises.has(deferred))
continue;
seenPromises.add(
encoder.deferred[Number(deferredId)] = raceSignal(
deferred,
encoder.signal
).then(
(resolved) => {
const id2 = flatten.call(encoder, resolved);
if (Array.isArray(id2)) {
controller.enqueue(
textEncoder.encode(
`${TYPE_PROMISE}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
`
)
);
encoder.index++;
lastSentIndex++;
} else if (id2 < 0) {
controller.enqueue(
textEncoder.encode(`${TYPE_PROMISE}${deferredId}:${id2}
`)
);
} else {
const values = encoder.stringified.slice(lastSentIndex + 1).join(",");
controller.enqueue(
textEncoder.encode(
`${TYPE_PROMISE}${deferredId}:[${values}]
`
)
);
lastSentIndex = encoder.stringified.length - 1;
}
},
(reason) => {
if (!reason || typeof reason !== "object" || !(reason instanceof Error)) {
reason = new Error("An unknown error occurred");
}
const id2 = flatten.call(encoder, reason);
if (Array.isArray(id2)) {
controller.enqueue(
textEncoder.encode(
`${TYPE_ERROR}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
`
)
);
encoder.index++;
lastSentIndex++;
} else if (id2 < 0) {
controller.enqueue(
textEncoder.encode(`${TYPE_ERROR}${deferredId}:${id2}
`)
);
} else {
const values = encoder.stringified.slice(lastSentIndex + 1).join(",");
controller.enqueue(
textEncoder.encode(
`${TYPE_ERROR}${deferredId}:[${values}]
`
)
);
lastSentIndex = encoder.stringified.length - 1;
}
}
).finally(() => {
delete encoder.deferred[Number(deferredId)];
})
);
}
await Promise.race(Object.values(encoder.deferred));
}
await Promise.all(Object.values(encoder.deferred));
controller.close();
}
});
return readable;
}
function raceSignal(promise, signal) {
if (!signal)
return promise;
if (signal.aborted)
return Promise.reject(signal.reason || new Error("Signal was aborted."));
const abort = new Promise((resolve, reject) => {
signal.addEventListener("abort", (event) => {
reject(signal.reason || new Error("Signal was aborted."));
});
promise.then(resolve).catch(reject);
});
abort.catch(() => {
});
return Promise.race([abort, promise]);
}
export {
decode,
encode
};

View File

@@ -1,2 +0,0 @@
import { type ThisDecode } from "./utils.js";
export declare function unflatten(this: ThisDecode, parsed: unknown): unknown;

View File

@@ -1,243 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unflatten = void 0;
const utils_js_1 = require("./utils.js");
const globalObj = (typeof window !== "undefined"
? window
: typeof globalThis !== "undefined"
? globalThis
: undefined);
function unflatten(parsed) {
const { hydrated, values } = this;
if (typeof parsed === "number")
return hydrate.call(this, parsed);
if (!Array.isArray(parsed) || !parsed.length)
throw new SyntaxError();
const startIndex = values.length;
for (const value of parsed) {
values.push(value);
}
hydrated.length = values.length;
return hydrate.call(this, startIndex);
}
exports.unflatten = unflatten;
function hydrate(index) {
const { hydrated, values, deferred, plugins } = this;
let result;
const stack = [
[
index,
(v) => {
result = v;
},
],
];
let postRun = [];
while (stack.length > 0) {
const [index, set] = stack.pop();
switch (index) {
case utils_js_1.UNDEFINED:
set(undefined);
continue;
case utils_js_1.NULL:
set(null);
continue;
case utils_js_1.NAN:
set(NaN);
continue;
case utils_js_1.POSITIVE_INFINITY:
set(Infinity);
continue;
case utils_js_1.NEGATIVE_INFINITY:
set(-Infinity);
continue;
case utils_js_1.NEGATIVE_ZERO:
set(-0);
continue;
}
if (hydrated[index]) {
set(hydrated[index]);
continue;
}
const value = values[index];
if (!value || typeof value !== "object") {
hydrated[index] = value;
set(value);
continue;
}
if (Array.isArray(value)) {
if (typeof value[0] === "string") {
const [type, b, c] = value;
switch (type) {
case utils_js_1.TYPE_DATE:
set((hydrated[index] = new Date(b)));
continue;
case utils_js_1.TYPE_URL:
set((hydrated[index] = new URL(b)));
continue;
case utils_js_1.TYPE_BIGINT:
set((hydrated[index] = BigInt(b)));
continue;
case utils_js_1.TYPE_REGEXP:
set((hydrated[index] = new RegExp(b, c)));
continue;
case utils_js_1.TYPE_SYMBOL:
set((hydrated[index] = Symbol.for(b)));
continue;
case utils_js_1.TYPE_SET:
const newSet = new Set();
hydrated[index] = newSet;
for (let i = 1; i < value.length; i++)
stack.push([
value[i],
(v) => {
newSet.add(v);
},
]);
set(newSet);
continue;
case utils_js_1.TYPE_MAP:
const map = new Map();
hydrated[index] = map;
for (let i = 1; i < value.length; i += 2) {
const r = [];
stack.push([
value[i + 1],
(v) => {
r[1] = v;
},
]);
stack.push([
value[i],
(k) => {
r[0] = k;
},
]);
postRun.push(() => {
map.set(r[0], r[1]);
});
}
set(map);
continue;
case utils_js_1.TYPE_NULL_OBJECT:
const obj = Object.create(null);
hydrated[index] = obj;
for (const key of Object.keys(b).reverse()) {
const r = [];
stack.push([
b[key],
(v) => {
r[1] = v;
},
]);
stack.push([
Number(key.slice(1)),
(k) => {
r[0] = k;
},
]);
postRun.push(() => {
obj[r[0]] = r[1];
});
}
set(obj);
continue;
case utils_js_1.TYPE_PROMISE:
if (hydrated[b]) {
set((hydrated[index] = hydrated[b]));
}
else {
const d = new utils_js_1.Deferred();
deferred[b] = d;
set((hydrated[index] = d.promise));
}
continue;
case utils_js_1.TYPE_ERROR:
const [, message, errorType] = value;
let error = errorType && globalObj && globalObj[errorType]
? new globalObj[errorType](message)
: new Error(message);
hydrated[index] = error;
set(error);
continue;
case utils_js_1.TYPE_PREVIOUS_RESOLVED:
set((hydrated[index] = hydrated[b]));
continue;
default:
// Run plugins at the end so we have a chance to resolve primitives
// without running into a loop
if (Array.isArray(plugins)) {
const r = [];
const vals = value.slice(1);
for (let i = 0; i < vals.length; i++) {
const v = vals[i];
stack.push([
v,
(v) => {
r[i] = v;
},
]);
}
postRun.push(() => {
for (const plugin of plugins) {
const result = plugin(value[0], ...r);
if (result) {
set((hydrated[index] = result.value));
return;
}
}
throw new SyntaxError();
});
continue;
}
throw new SyntaxError();
}
}
else {
const array = [];
hydrated[index] = array;
for (let i = 0; i < value.length; i++) {
const n = value[i];
if (n !== utils_js_1.HOLE) {
stack.push([
n,
(v) => {
array[i] = v;
},
]);
}
}
set(array);
continue;
}
}
else {
const object = {};
hydrated[index] = object;
for (const key of Object.keys(value).reverse()) {
const r = [];
stack.push([
value[key],
(v) => {
r[1] = v;
},
]);
stack.push([
Number(key.slice(1)),
(k) => {
r[0] = k;
},
]);
postRun.push(() => {
object[r[0]] = r[1];
});
}
set(object);
continue;
}
}
while (postRun.length > 0) {
postRun.pop()();
}
return result;
}

View File

@@ -1,44 +0,0 @@
export declare const HOLE = -1;
export declare const NAN = -2;
export declare const NEGATIVE_INFINITY = -3;
export declare const NEGATIVE_ZERO = -4;
export declare const NULL = -5;
export declare const POSITIVE_INFINITY = -6;
export declare const UNDEFINED = -7;
export declare const TYPE_BIGINT = "B";
export declare const TYPE_DATE = "D";
export declare const TYPE_ERROR = "E";
export declare const TYPE_MAP = "M";
export declare const TYPE_NULL_OBJECT = "N";
export declare const TYPE_PROMISE = "P";
export declare const TYPE_REGEXP = "R";
export declare const TYPE_SET = "S";
export declare const TYPE_SYMBOL = "Y";
export declare const TYPE_URL = "U";
export declare const TYPE_PREVIOUS_RESOLVED = "Z";
export type DecodePlugin = (type: string, ...data: unknown[]) => {
value: unknown;
} | false | null | undefined;
export type EncodePlugin = (value: unknown) => [string, ...unknown[]] | false | null | undefined;
export interface ThisDecode {
values: unknown[];
hydrated: unknown[];
deferred: Record<number, Deferred<unknown>>;
plugins?: DecodePlugin[];
}
export interface ThisEncode {
index: number;
indices: Map<unknown, number>;
stringified: string[];
deferred: Record<number, Promise<unknown>>;
plugins?: EncodePlugin[];
postPlugins?: EncodePlugin[];
signal?: AbortSignal;
}
export declare class Deferred<T = unknown> {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (reason: unknown) => void;
constructor();
}
export declare function createLineSplittingTransform(): TransformStream<any, any>;

View File

@@ -1,55 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLineSplittingTransform = exports.Deferred = exports.TYPE_PREVIOUS_RESOLVED = exports.TYPE_URL = exports.TYPE_SYMBOL = exports.TYPE_SET = exports.TYPE_REGEXP = exports.TYPE_PROMISE = exports.TYPE_NULL_OBJECT = exports.TYPE_MAP = exports.TYPE_ERROR = exports.TYPE_DATE = exports.TYPE_BIGINT = exports.UNDEFINED = exports.POSITIVE_INFINITY = exports.NULL = exports.NEGATIVE_ZERO = exports.NEGATIVE_INFINITY = exports.NAN = exports.HOLE = void 0;
exports.HOLE = -1;
exports.NAN = -2;
exports.NEGATIVE_INFINITY = -3;
exports.NEGATIVE_ZERO = -4;
exports.NULL = -5;
exports.POSITIVE_INFINITY = -6;
exports.UNDEFINED = -7;
exports.TYPE_BIGINT = "B";
exports.TYPE_DATE = "D";
exports.TYPE_ERROR = "E";
exports.TYPE_MAP = "M";
exports.TYPE_NULL_OBJECT = "N";
exports.TYPE_PROMISE = "P";
exports.TYPE_REGEXP = "R";
exports.TYPE_SET = "S";
exports.TYPE_SYMBOL = "Y";
exports.TYPE_URL = "U";
exports.TYPE_PREVIOUS_RESOLVED = "Z";
class Deferred {
promise;
resolve;
reject;
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
exports.Deferred = Deferred;
function createLineSplittingTransform() {
const decoder = new TextDecoder();
let leftover = "";
return new TransformStream({
transform(chunk, controller) {
const str = decoder.decode(chunk, { stream: true });
const parts = (leftover + str).split("\n");
// The last part might be a partial line, so keep it for the next chunk.
leftover = parts.pop() || "";
for (const part of parts) {
controller.enqueue(part);
}
},
flush(controller) {
// If there's any leftover data, enqueue it before closing.
if (leftover) {
controller.enqueue(leftover);
}
},
});
}
exports.createLineSplittingTransform = createLineSplittingTransform;

View File

@@ -1,50 +0,0 @@
{
"name": "turbo-stream",
"version": "2.4.0",
"description": "A streaming data transport format that aims to support built-in features such as Promises, Dates, RegExps, Maps, Sets and more.",
"files": [
"dist",
"README.md"
],
"main": "dist/turbo-stream.js",
"types": "dist/turbo-stream.d.ts",
"exports": {
".": {
"types": "./dist/turbo-stream.d.ts",
"import": "./dist/turbo-stream.mjs",
"require": "./dist/turbo-stream.js",
"default": "./dist/turbo-stream.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "pnpm build:esm && pnpm build:cjs && cp ./dist/turbo-stream.mjs ./viewer/scripts/turbo-stream.js",
"build:cjs": "tsc --outDir dist --project tsconfig.lib.json",
"build:esm": "esbuild --bundle --platform=node --target=node16 --format=esm --outfile=dist/turbo-stream.mjs src/turbo-stream.ts",
"test": "node --no-warnings --loader tsm --enable-source-maps --test-reporter tap --test src/*.spec.ts",
"test-typecheck": "tsc --noEmit --project tsconfig.spec.json",
"prepublish": "attw $(npm pack) --ignore-rules false-cjs"
},
"keywords": [
"devalue",
"turbo",
"stream",
"enhanced",
"json"
],
"author": "Jacob Ebey <jacob.ebey@live.com>",
"license": "ISC",
"repository": {
"type": "git",
"url": "https://github.com/jacob-ebey/turbo-stream.git"
},
"sideEffects": false,
"devDependencies": {
"@arethetypeswrong/cli": "^0.12.2",
"@types/node": "^20.8.7",
"esbuild": "^0.19.5",
"expect": "^29.7.0",
"tsm": "^2.3.0",
"typescript": "^5.2.2"
}
}