turbo-stream 1.0.4 → 1.1.0
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/dist/flatten.js +65 -44
- package/dist/turbo-stream.d.ts +3 -2
- package/dist/turbo-stream.js +4 -2
- package/dist/unflatten.js +9 -1
- package/dist/utils.d.ts +6 -0
- package/package.json +1 -1
package/dist/flatten.js
CHANGED
|
@@ -20,7 +20,7 @@ export function flatten(input) {
|
|
|
20
20
|
return index;
|
|
21
21
|
}
|
|
22
22
|
function stringify(input, index) {
|
|
23
|
-
const { deferred } = this;
|
|
23
|
+
const { deferred, plugins } = this;
|
|
24
24
|
const str = this.stringified;
|
|
25
25
|
const partsForObj = (obj) => Object.keys(obj)
|
|
26
26
|
.map((k) => `${JSON.stringify(k)}:${flatten.call(this, obj[k])}`)
|
|
@@ -45,51 +45,72 @@ function stringify(input, index) {
|
|
|
45
45
|
str[index] = "null";
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
else if (input instanceof Set) {
|
|
65
|
-
str[index] = `["${TYPE_SET}",${[...input]
|
|
66
|
-
.map((val) => flatten.call(this, val))
|
|
67
|
-
.join(",")}]`;
|
|
68
|
-
}
|
|
69
|
-
else if (input instanceof Map) {
|
|
70
|
-
str[index] = `["${TYPE_MAP}",${[...input]
|
|
71
|
-
.flatMap(([k, v]) => [flatten.call(this, k), flatten.call(this, v)])
|
|
72
|
-
.join(",")}]`;
|
|
73
|
-
}
|
|
74
|
-
else if (input instanceof Promise) {
|
|
75
|
-
str[index] = `["${TYPE_PROMISE}",${index}]`;
|
|
76
|
-
deferred[index] = input;
|
|
77
|
-
}
|
|
78
|
-
else if (input instanceof Error) {
|
|
79
|
-
str[index] = `["${TYPE_ERROR}",${JSON.stringify(input.message)}`;
|
|
80
|
-
if (input.name !== "Error") {
|
|
81
|
-
str[index] += `,${JSON.stringify(input.name)}`;
|
|
48
|
+
const isArray = Array.isArray(input);
|
|
49
|
+
let pluginHandled = false;
|
|
50
|
+
if (!isArray && plugins) {
|
|
51
|
+
for (const plugin of plugins) {
|
|
52
|
+
const pluginResult = plugin(input);
|
|
53
|
+
if (Array.isArray(pluginResult)) {
|
|
54
|
+
pluginHandled = true;
|
|
55
|
+
const [pluginIdentifier, ...rest] = pluginResult;
|
|
56
|
+
str[index] = `[${JSON.stringify(pluginIdentifier)}`;
|
|
57
|
+
if (rest.length > 0) {
|
|
58
|
+
str[index] +=
|
|
59
|
+
"," + rest.map((v) => flatten.call(this, v)).join(",");
|
|
60
|
+
}
|
|
61
|
+
str[index] += "]";
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
82
64
|
}
|
|
83
|
-
str[index] += "]";
|
|
84
|
-
}
|
|
85
|
-
else if (Object.getPrototypeOf(input) === null) {
|
|
86
|
-
str[index] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input)}}]`;
|
|
87
65
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
66
|
+
if (!pluginHandled) {
|
|
67
|
+
let result = isArray ? "[" : "{";
|
|
68
|
+
if (isArray) {
|
|
69
|
+
for (let i = 0; i < input.length; i++)
|
|
70
|
+
result +=
|
|
71
|
+
(i ? "," : "") +
|
|
72
|
+
(i in input ? flatten.call(this, input[i]) : HOLE);
|
|
73
|
+
str[index] = result + "]";
|
|
74
|
+
}
|
|
75
|
+
else if (input instanceof Date) {
|
|
76
|
+
str[index] = `["${TYPE_DATE}",${input.getTime()}]`;
|
|
77
|
+
}
|
|
78
|
+
else if (input instanceof URL) {
|
|
79
|
+
str[index] = `["${TYPE_URL}",${JSON.stringify(input.href)}]`;
|
|
80
|
+
}
|
|
81
|
+
else if (input instanceof RegExp) {
|
|
82
|
+
str[index] = `["${TYPE_REGEXP}",${JSON.stringify(input.source)},${JSON.stringify(input.flags)}]`;
|
|
83
|
+
}
|
|
84
|
+
else if (input instanceof Set) {
|
|
85
|
+
str[index] = `["${TYPE_SET}",${[...input]
|
|
86
|
+
.map((val) => flatten.call(this, val))
|
|
87
|
+
.join(",")}]`;
|
|
88
|
+
}
|
|
89
|
+
else if (input instanceof Map) {
|
|
90
|
+
str[index] = `["${TYPE_MAP}",${[...input]
|
|
91
|
+
.flatMap(([k, v]) => [flatten.call(this, k), flatten.call(this, v)])
|
|
92
|
+
.join(",")}]`;
|
|
93
|
+
}
|
|
94
|
+
else if (input instanceof Promise) {
|
|
95
|
+
str[index] = `["${TYPE_PROMISE}",${index}]`;
|
|
96
|
+
deferred[index] = input;
|
|
97
|
+
}
|
|
98
|
+
else if (input instanceof Error) {
|
|
99
|
+
str[index] = `["${TYPE_ERROR}",${JSON.stringify(input.message)}`;
|
|
100
|
+
if (input.name !== "Error") {
|
|
101
|
+
str[index] += `,${JSON.stringify(input.name)}`;
|
|
102
|
+
}
|
|
103
|
+
str[index] += "]";
|
|
104
|
+
}
|
|
105
|
+
else if (Object.getPrototypeOf(input) === null) {
|
|
106
|
+
str[index] = `["${TYPE_NULL_OBJECT}",{${partsForObj(input)}}]`;
|
|
107
|
+
}
|
|
108
|
+
else if (isPlainObject(input)) {
|
|
109
|
+
str[index] = `{${partsForObj(input)}}`;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
throw new Error("Cannot encode object with prototype");
|
|
113
|
+
}
|
|
93
114
|
}
|
|
94
115
|
break;
|
|
95
116
|
default:
|
package/dist/turbo-stream.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { type DecodePlugin, type EncodePlugin } from "./utils.js";
|
|
2
|
+
export declare function decode(readable: ReadableStream<Uint8Array>, plugins?: DecodePlugin[]): Promise<{
|
|
2
3
|
done: Promise<undefined>;
|
|
3
4
|
value: unknown;
|
|
4
5
|
}>;
|
|
5
|
-
export declare function encode(input: unknown): ReadableStream<Uint8Array>;
|
|
6
|
+
export declare function encode(input: unknown, plugins?: EncodePlugin[]): ReadableStream<Uint8Array>;
|
package/dist/turbo-stream.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flatten } from "./flatten.js";
|
|
2
2
|
import { unflatten } from "./unflatten.js";
|
|
3
3
|
import { Deferred, TYPE_ERROR, TYPE_PROMISE, createLineSplittingTransform, } from "./utils.js";
|
|
4
|
-
export async function decode(readable) {
|
|
4
|
+
export async function decode(readable, plugins) {
|
|
5
5
|
const done = new Deferred();
|
|
6
6
|
const reader = readable
|
|
7
7
|
.pipeThrough(createLineSplittingTransform())
|
|
@@ -10,6 +10,7 @@ export async function decode(readable) {
|
|
|
10
10
|
values: [],
|
|
11
11
|
hydrated: [],
|
|
12
12
|
deferred: {},
|
|
13
|
+
plugins,
|
|
13
14
|
};
|
|
14
15
|
const decoded = await decodeInitial.call(decoder, reader);
|
|
15
16
|
let donePromise = done.promise;
|
|
@@ -100,12 +101,13 @@ async function decodeDeferred(reader) {
|
|
|
100
101
|
read = await reader.read();
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
|
-
export function encode(input) {
|
|
104
|
+
export function encode(input, plugins) {
|
|
104
105
|
const encoder = {
|
|
105
106
|
deferred: {},
|
|
106
107
|
index: 0,
|
|
107
108
|
indicies: new Map(),
|
|
108
109
|
stringified: [],
|
|
110
|
+
plugins,
|
|
109
111
|
};
|
|
110
112
|
const textEncoder = new TextEncoder();
|
|
111
113
|
let lastSentIndex = 0;
|
package/dist/unflatten.js
CHANGED
|
@@ -16,7 +16,7 @@ export function unflatten(parsed) {
|
|
|
16
16
|
return hydrate.call(this, startIndex);
|
|
17
17
|
}
|
|
18
18
|
function hydrate(index) {
|
|
19
|
-
const { hydrated, values, deferred } = this;
|
|
19
|
+
const { hydrated, values, deferred, plugins } = this;
|
|
20
20
|
switch (index) {
|
|
21
21
|
case UNDEFINED:
|
|
22
22
|
return;
|
|
@@ -36,6 +36,14 @@ function hydrate(index) {
|
|
|
36
36
|
return (hydrated[index] = value);
|
|
37
37
|
if (Array.isArray(value)) {
|
|
38
38
|
if (typeof value[0] === "string") {
|
|
39
|
+
if (Array.isArray(plugins)) {
|
|
40
|
+
const args = value.slice(1).map((i) => hydrate.call(this, i));
|
|
41
|
+
for (const plugin of plugins) {
|
|
42
|
+
const result = plugin(value[0], ...args);
|
|
43
|
+
if (result)
|
|
44
|
+
return (hydrated[index] = result.value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
39
47
|
const [type, b, c] = value;
|
|
40
48
|
switch (type) {
|
|
41
49
|
case TYPE_DATE:
|
package/dist/utils.d.ts
CHANGED
|
@@ -14,16 +14,22 @@ export declare const TYPE_REGEXP = "R";
|
|
|
14
14
|
export declare const TYPE_SET = "S";
|
|
15
15
|
export declare const TYPE_SYMBOL = "Y";
|
|
16
16
|
export declare const TYPE_URL = "U";
|
|
17
|
+
export type DecodePlugin = (type: string, ...data: unknown[]) => {
|
|
18
|
+
value: unknown;
|
|
19
|
+
} | false | null | undefined;
|
|
20
|
+
export type EncodePlugin = (value: unknown) => [string, ...unknown[]] | false | null | undefined;
|
|
17
21
|
export interface ThisDecode {
|
|
18
22
|
values: unknown[];
|
|
19
23
|
hydrated: unknown[];
|
|
20
24
|
deferred: Record<number, Deferred<unknown>>;
|
|
25
|
+
plugins?: DecodePlugin[];
|
|
21
26
|
}
|
|
22
27
|
export interface ThisEncode {
|
|
23
28
|
index: number;
|
|
24
29
|
indicies: Map<unknown, number>;
|
|
25
30
|
stringified: string[];
|
|
26
31
|
deferred: Record<number, Promise<unknown>>;
|
|
32
|
+
plugins?: EncodePlugin[];
|
|
27
33
|
}
|
|
28
34
|
export declare class Deferred<T = unknown> {
|
|
29
35
|
promise: Promise<T>;
|
package/package.json
CHANGED