turbo-stream 1.2.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/dist/flatten.js +13 -8
- package/dist/turbo-stream.d.ts +7 -2
- package/dist/turbo-stream.js +20 -3
- package/dist/turbo-stream.mjs +46 -18
- package/dist/unflatten.js +2 -0
- package/dist/utils.d.ts +6 -4
- package/dist/utils.js +9 -8
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2018-19 [these people](https://github.com/rich-harris/devalue/graphs/contributors)
|
|
2
|
+
|
|
3
|
+
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:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
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.
|
package/dist/flatten.js
CHANGED
|
@@ -9,11 +9,13 @@ function flatten(input) {
|
|
|
9
9
|
return existing;
|
|
10
10
|
if (input === undefined)
|
|
11
11
|
return utils_js_1.UNDEFINED;
|
|
12
|
+
if (input === null)
|
|
13
|
+
return utils_js_1.NULL;
|
|
12
14
|
if (Number.isNaN(input))
|
|
13
15
|
return utils_js_1.NAN;
|
|
14
|
-
if (input ===
|
|
16
|
+
if (input === Number.POSITIVE_INFINITY)
|
|
15
17
|
return utils_js_1.POSITIVE_INFINITY;
|
|
16
|
-
if (input ===
|
|
18
|
+
if (input === Number.NEGATIVE_INFINITY)
|
|
17
19
|
return utils_js_1.NEGATIVE_INFINITY;
|
|
18
20
|
if (input === 0 && 1 / input < 0)
|
|
19
21
|
return utils_js_1.NEGATIVE_ZERO;
|
|
@@ -38,15 +40,16 @@ function stringify(input, index) {
|
|
|
38
40
|
case "bigint":
|
|
39
41
|
str[index] = `["${utils_js_1.TYPE_BIGINT}","${input}"]`;
|
|
40
42
|
break;
|
|
41
|
-
case "symbol":
|
|
43
|
+
case "symbol": {
|
|
42
44
|
const keyFor = Symbol.keyFor(input);
|
|
43
45
|
if (!keyFor)
|
|
44
46
|
throw new Error("Cannot encode symbol unless created with Symbol.for()");
|
|
45
47
|
str[index] = `["${utils_js_1.TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
|
|
46
48
|
break;
|
|
47
|
-
|
|
49
|
+
}
|
|
50
|
+
case "object": {
|
|
48
51
|
if (!input) {
|
|
49
|
-
str[index] =
|
|
52
|
+
str[index] = `${utils_js_1.NULL}`;
|
|
50
53
|
break;
|
|
51
54
|
}
|
|
52
55
|
const isArray = Array.isArray(input);
|
|
@@ -59,8 +62,9 @@ function stringify(input, index) {
|
|
|
59
62
|
const [pluginIdentifier, ...rest] = pluginResult;
|
|
60
63
|
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
61
64
|
if (rest.length > 0) {
|
|
62
|
-
str[index] +=
|
|
63
|
-
|
|
65
|
+
str[index] += `,${rest
|
|
66
|
+
.map((v) => flatten.call(this, v))
|
|
67
|
+
.join(",")}`;
|
|
64
68
|
}
|
|
65
69
|
str[index] += "]";
|
|
66
70
|
break;
|
|
@@ -74,7 +78,7 @@ function stringify(input, index) {
|
|
|
74
78
|
result +=
|
|
75
79
|
(i ? "," : "") +
|
|
76
80
|
(i in input ? flatten.call(this, input[i]) : utils_js_1.HOLE);
|
|
77
|
-
str[index] = result
|
|
81
|
+
str[index] = `${result}]`;
|
|
78
82
|
}
|
|
79
83
|
else if (input instanceof Date) {
|
|
80
84
|
str[index] = `["${utils_js_1.TYPE_DATE}",${input.getTime()}]`;
|
|
@@ -117,6 +121,7 @@ function stringify(input, index) {
|
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
break;
|
|
124
|
+
}
|
|
120
125
|
default:
|
|
121
126
|
throw new Error("Cannot encode function or unexpected type");
|
|
122
127
|
}
|
package/dist/turbo-stream.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { type DecodePlugin, type EncodePlugin } from "./utils.js";
|
|
2
2
|
export type { DecodePlugin, EncodePlugin };
|
|
3
|
-
export declare function decode(readable: ReadableStream<Uint8Array>,
|
|
3
|
+
export declare function decode(readable: ReadableStream<Uint8Array>, options?: {
|
|
4
|
+
plugins?: DecodePlugin[];
|
|
5
|
+
}): Promise<{
|
|
4
6
|
done: Promise<undefined>;
|
|
5
7
|
value: unknown;
|
|
6
8
|
}>;
|
|
7
|
-
export declare function encode(input: unknown,
|
|
9
|
+
export declare function encode(input: unknown, options?: {
|
|
10
|
+
plugins?: EncodePlugin[];
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
}): ReadableStream<Uint8Array>;
|
package/dist/turbo-stream.js
CHANGED
|
@@ -4,7 +4,8 @@ exports.encode = exports.decode = void 0;
|
|
|
4
4
|
const flatten_js_1 = require("./flatten.js");
|
|
5
5
|
const unflatten_js_1 = require("./unflatten.js");
|
|
6
6
|
const utils_js_1 = require("./utils.js");
|
|
7
|
-
async function decode(readable,
|
|
7
|
+
async function decode(readable, options) {
|
|
8
|
+
const { plugins } = options ?? {};
|
|
8
9
|
const done = new utils_js_1.Deferred();
|
|
9
10
|
const reader = readable
|
|
10
11
|
.pipeThrough((0, utils_js_1.createLineSplittingTransform)())
|
|
@@ -105,13 +106,15 @@ async function decodeDeferred(reader) {
|
|
|
105
106
|
read = await reader.read();
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
|
-
function encode(input,
|
|
109
|
+
function encode(input, options) {
|
|
110
|
+
const { plugins, signal } = options ?? {};
|
|
109
111
|
const encoder = {
|
|
110
112
|
deferred: {},
|
|
111
113
|
index: 0,
|
|
112
114
|
indices: new Map(),
|
|
113
115
|
stringified: [],
|
|
114
116
|
plugins,
|
|
117
|
+
signal,
|
|
115
118
|
};
|
|
116
119
|
const textEncoder = new TextEncoder();
|
|
117
120
|
let lastSentIndex = 0;
|
|
@@ -130,7 +133,7 @@ function encode(input, plugins) {
|
|
|
130
133
|
for (const [deferredId, deferred] of Object.entries(encoder.deferred)) {
|
|
131
134
|
if (seenPromises.has(deferred))
|
|
132
135
|
continue;
|
|
133
|
-
seenPromises.add((encoder.deferred[Number(deferredId)] = deferred
|
|
136
|
+
seenPromises.add((encoder.deferred[Number(deferredId)] = raceSignal(deferred, encoder.signal)
|
|
134
137
|
.then((resolved) => {
|
|
135
138
|
const id = flatten_js_1.flatten.call(encoder, resolved);
|
|
136
139
|
if (id < 0) {
|
|
@@ -174,3 +177,17 @@ function encode(input, plugins) {
|
|
|
174
177
|
return readable;
|
|
175
178
|
}
|
|
176
179
|
exports.encode = encode;
|
|
180
|
+
function raceSignal(promise, signal) {
|
|
181
|
+
if (!signal)
|
|
182
|
+
return promise;
|
|
183
|
+
if (signal.aborted)
|
|
184
|
+
return Promise.reject(signal.reason || new Error("Signal was aborted."));
|
|
185
|
+
const abort = new Promise((resolve, reject) => {
|
|
186
|
+
signal.addEventListener("abort", (event) => {
|
|
187
|
+
reject(signal.reason || new Error("Signal was aborted."));
|
|
188
|
+
});
|
|
189
|
+
promise.then(resolve).catch(reject);
|
|
190
|
+
});
|
|
191
|
+
abort.catch(() => { });
|
|
192
|
+
return Promise.race([abort, promise]);
|
|
193
|
+
}
|
package/dist/turbo-stream.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/utils.ts
|
|
2
2
|
var HOLE = -1;
|
|
3
3
|
var NAN = -2;
|
|
4
|
-
var NEGATIVE_INFINITY = -
|
|
5
|
-
var NEGATIVE_ZERO = -
|
|
6
|
-
var
|
|
7
|
-
var
|
|
4
|
+
var NEGATIVE_INFINITY = -3;
|
|
5
|
+
var NEGATIVE_ZERO = -4;
|
|
6
|
+
var NULL = -5;
|
|
7
|
+
var POSITIVE_INFINITY = -6;
|
|
8
|
+
var UNDEFINED = -7;
|
|
8
9
|
var TYPE_BIGINT = "B";
|
|
9
10
|
var TYPE_DATE = "D";
|
|
10
11
|
var TYPE_ERROR = "E";
|
|
@@ -27,12 +28,12 @@ var Deferred = class {
|
|
|
27
28
|
}
|
|
28
29
|
};
|
|
29
30
|
function createLineSplittingTransform() {
|
|
30
|
-
|
|
31
|
+
const decoder = new TextDecoder();
|
|
31
32
|
let leftover = "";
|
|
32
33
|
return new TransformStream({
|
|
33
34
|
transform(chunk, controller) {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
const str = decoder.decode(chunk, { stream: true });
|
|
36
|
+
const parts = (leftover + str).split("\n");
|
|
36
37
|
leftover = parts.pop() || "";
|
|
37
38
|
for (const part of parts) {
|
|
38
39
|
controller.enqueue(part);
|
|
@@ -54,11 +55,13 @@ function flatten(input) {
|
|
|
54
55
|
return existing;
|
|
55
56
|
if (input === void 0)
|
|
56
57
|
return UNDEFINED;
|
|
58
|
+
if (input === null)
|
|
59
|
+
return NULL;
|
|
57
60
|
if (Number.isNaN(input))
|
|
58
61
|
return NAN;
|
|
59
|
-
if (input ===
|
|
62
|
+
if (input === Number.POSITIVE_INFINITY)
|
|
60
63
|
return POSITIVE_INFINITY;
|
|
61
|
-
if (input ===
|
|
64
|
+
if (input === Number.NEGATIVE_INFINITY)
|
|
62
65
|
return NEGATIVE_INFINITY;
|
|
63
66
|
if (input === 0 && 1 / input < 0)
|
|
64
67
|
return NEGATIVE_ZERO;
|
|
@@ -80,7 +83,7 @@ function stringify(input, index) {
|
|
|
80
83
|
case "bigint":
|
|
81
84
|
str[index] = `["${TYPE_BIGINT}","${input}"]`;
|
|
82
85
|
break;
|
|
83
|
-
case "symbol":
|
|
86
|
+
case "symbol": {
|
|
84
87
|
const keyFor = Symbol.keyFor(input);
|
|
85
88
|
if (!keyFor)
|
|
86
89
|
throw new Error(
|
|
@@ -88,9 +91,10 @@ function stringify(input, index) {
|
|
|
88
91
|
);
|
|
89
92
|
str[index] = `["${TYPE_SYMBOL}",${JSON.stringify(keyFor)}]`;
|
|
90
93
|
break;
|
|
91
|
-
|
|
94
|
+
}
|
|
95
|
+
case "object": {
|
|
92
96
|
if (!input) {
|
|
93
|
-
str[index] =
|
|
97
|
+
str[index] = `${NULL}`;
|
|
94
98
|
break;
|
|
95
99
|
}
|
|
96
100
|
const isArray = Array.isArray(input);
|
|
@@ -103,7 +107,7 @@ function stringify(input, index) {
|
|
|
103
107
|
const [pluginIdentifier, ...rest] = pluginResult;
|
|
104
108
|
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
105
109
|
if (rest.length > 0) {
|
|
106
|
-
str[index] +=
|
|
110
|
+
str[index] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
|
|
107
111
|
}
|
|
108
112
|
str[index] += "]";
|
|
109
113
|
break;
|
|
@@ -115,7 +119,7 @@ function stringify(input, index) {
|
|
|
115
119
|
if (isArray) {
|
|
116
120
|
for (let i = 0; i < input.length; i++)
|
|
117
121
|
result += (i ? "," : "") + (i in input ? flatten.call(this, input[i]) : HOLE);
|
|
118
|
-
str[index] = result
|
|
122
|
+
str[index] = `${result}]`;
|
|
119
123
|
} else if (input instanceof Date) {
|
|
120
124
|
str[index] = `["${TYPE_DATE}",${input.getTime()}]`;
|
|
121
125
|
} else if (input instanceof URL) {
|
|
@@ -146,6 +150,7 @@ function stringify(input, index) {
|
|
|
146
150
|
}
|
|
147
151
|
}
|
|
148
152
|
break;
|
|
153
|
+
}
|
|
149
154
|
default:
|
|
150
155
|
throw new Error("Cannot encode function or unexpected type");
|
|
151
156
|
}
|
|
@@ -174,6 +179,8 @@ function hydrate(index) {
|
|
|
174
179
|
switch (index) {
|
|
175
180
|
case UNDEFINED:
|
|
176
181
|
return;
|
|
182
|
+
case NULL:
|
|
183
|
+
return null;
|
|
177
184
|
case NAN:
|
|
178
185
|
return NaN;
|
|
179
186
|
case POSITIVE_INFINITY:
|
|
@@ -272,7 +279,8 @@ function hydrate(index) {
|
|
|
272
279
|
}
|
|
273
280
|
|
|
274
281
|
// src/turbo-stream.ts
|
|
275
|
-
async function decode(readable,
|
|
282
|
+
async function decode(readable, options) {
|
|
283
|
+
const { plugins } = options ?? {};
|
|
276
284
|
const done = new Deferred();
|
|
277
285
|
const reader = readable.pipeThrough(createLineSplittingTransform()).getReader();
|
|
278
286
|
const decoder = {
|
|
@@ -363,13 +371,15 @@ async function decodeDeferred(reader) {
|
|
|
363
371
|
read = await reader.read();
|
|
364
372
|
}
|
|
365
373
|
}
|
|
366
|
-
function encode(input,
|
|
374
|
+
function encode(input, options) {
|
|
375
|
+
const { plugins, signal } = options ?? {};
|
|
367
376
|
const encoder = {
|
|
368
377
|
deferred: {},
|
|
369
378
|
index: 0,
|
|
370
379
|
indices: /* @__PURE__ */ new Map(),
|
|
371
380
|
stringified: [],
|
|
372
|
-
plugins
|
|
381
|
+
plugins,
|
|
382
|
+
signal
|
|
373
383
|
};
|
|
374
384
|
const textEncoder = new TextEncoder();
|
|
375
385
|
let lastSentIndex = 0;
|
|
@@ -392,7 +402,10 @@ function encode(input, plugins) {
|
|
|
392
402
|
if (seenPromises.has(deferred))
|
|
393
403
|
continue;
|
|
394
404
|
seenPromises.add(
|
|
395
|
-
encoder.deferred[Number(deferredId)] =
|
|
405
|
+
encoder.deferred[Number(deferredId)] = raceSignal(
|
|
406
|
+
deferred,
|
|
407
|
+
encoder.signal
|
|
408
|
+
).then(
|
|
396
409
|
(resolved) => {
|
|
397
410
|
const id2 = flatten.call(encoder, resolved);
|
|
398
411
|
if (id2 < 0) {
|
|
@@ -445,6 +458,21 @@ function encode(input, plugins) {
|
|
|
445
458
|
});
|
|
446
459
|
return readable;
|
|
447
460
|
}
|
|
461
|
+
function raceSignal(promise, signal) {
|
|
462
|
+
if (!signal)
|
|
463
|
+
return promise;
|
|
464
|
+
if (signal.aborted)
|
|
465
|
+
return Promise.reject(signal.reason || new Error("Signal was aborted."));
|
|
466
|
+
const abort = new Promise((resolve, reject) => {
|
|
467
|
+
signal.addEventListener("abort", (event) => {
|
|
468
|
+
reject(signal.reason || new Error("Signal was aborted."));
|
|
469
|
+
});
|
|
470
|
+
promise.then(resolve).catch(reject);
|
|
471
|
+
});
|
|
472
|
+
abort.catch(() => {
|
|
473
|
+
});
|
|
474
|
+
return Promise.race([abort, promise]);
|
|
475
|
+
}
|
|
448
476
|
export {
|
|
449
477
|
decode,
|
|
450
478
|
encode
|
package/dist/unflatten.js
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export declare const HOLE = -1;
|
|
2
2
|
export declare const NAN = -2;
|
|
3
|
-
export declare const NEGATIVE_INFINITY = -
|
|
4
|
-
export declare const NEGATIVE_ZERO = -
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
3
|
+
export declare const NEGATIVE_INFINITY = -3;
|
|
4
|
+
export declare const NEGATIVE_ZERO = -4;
|
|
5
|
+
export declare const NULL = -5;
|
|
6
|
+
export declare const POSITIVE_INFINITY = -6;
|
|
7
|
+
export declare const UNDEFINED = -7;
|
|
7
8
|
export declare const TYPE_BIGINT = "B";
|
|
8
9
|
export declare const TYPE_DATE = "D";
|
|
9
10
|
export declare const TYPE_ERROR = "E";
|
|
@@ -30,6 +31,7 @@ export interface ThisEncode {
|
|
|
30
31
|
stringified: string[];
|
|
31
32
|
deferred: Record<number, Promise<unknown>>;
|
|
32
33
|
plugins?: EncodePlugin[];
|
|
34
|
+
signal?: AbortSignal;
|
|
33
35
|
}
|
|
34
36
|
export declare class Deferred<T = unknown> {
|
|
35
37
|
promise: Promise<T>;
|
package/dist/utils.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createLineSplittingTransform = exports.Deferred = 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.NEGATIVE_ZERO = exports.NEGATIVE_INFINITY = exports.NAN = exports.HOLE = void 0;
|
|
3
|
+
exports.createLineSplittingTransform = exports.Deferred = 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;
|
|
4
4
|
exports.HOLE = -1;
|
|
5
5
|
exports.NAN = -2;
|
|
6
|
-
exports.NEGATIVE_INFINITY = -
|
|
7
|
-
exports.NEGATIVE_ZERO = -
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
6
|
+
exports.NEGATIVE_INFINITY = -3;
|
|
7
|
+
exports.NEGATIVE_ZERO = -4;
|
|
8
|
+
exports.NULL = -5;
|
|
9
|
+
exports.POSITIVE_INFINITY = -6;
|
|
10
|
+
exports.UNDEFINED = -7;
|
|
10
11
|
exports.TYPE_BIGINT = "B";
|
|
11
12
|
exports.TYPE_DATE = "D";
|
|
12
13
|
exports.TYPE_ERROR = "E";
|
|
@@ -30,12 +31,12 @@ class Deferred {
|
|
|
30
31
|
}
|
|
31
32
|
exports.Deferred = Deferred;
|
|
32
33
|
function createLineSplittingTransform() {
|
|
33
|
-
|
|
34
|
+
const decoder = new TextDecoder();
|
|
34
35
|
let leftover = "";
|
|
35
36
|
return new TransformStream({
|
|
36
37
|
transform(chunk, controller) {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
const str = decoder.decode(chunk, { stream: true });
|
|
39
|
+
const parts = (leftover + str).split("\n");
|
|
39
40
|
// The last part might be a partial line, so keep it for the next chunk.
|
|
40
41
|
leftover = parts.pop() || "";
|
|
41
42
|
for (const part of parts) {
|
package/package.json
CHANGED