turbo-stream 2.0.1 → 2.2.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.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import { type ThisEncode } from "./utils.js";
2
- export declare function flatten(this: ThisEncode, input: unknown): number;
2
+ export declare function flatten(this: ThisEncode, input: unknown): number | [number];
package/dist/flatten.js CHANGED
@@ -6,7 +6,7 @@ function flatten(input) {
6
6
  const { indices } = this;
7
7
  const existing = indices.get(input);
8
8
  if (existing)
9
- return existing;
9
+ return [existing];
10
10
  if (input === undefined)
11
11
  return utils_js_1.UNDEFINED;
12
12
  if (input === null)
@@ -90,14 +90,27 @@ function stringify(input, index) {
90
90
  str[index] = `["${utils_js_1.TYPE_REGEXP}",${JSON.stringify(input.source)},${JSON.stringify(input.flags)}]`;
91
91
  }
92
92
  else if (input instanceof Set) {
93
- str[index] = `["${utils_js_1.TYPE_SET}",${[...input]
94
- .map((val) => flatten.call(this, val))
95
- .join(",")}]`;
93
+ if (input.size > 0) {
94
+ str[index] = `["${utils_js_1.TYPE_SET}",${[...input]
95
+ .map((val) => flatten.call(this, val))
96
+ .join(",")}]`;
97
+ }
98
+ else {
99
+ str[index] = `["${utils_js_1.TYPE_SET}"]`;
100
+ }
96
101
  }
97
102
  else if (input instanceof Map) {
98
- str[index] = `["${utils_js_1.TYPE_MAP}",${[...input]
99
- .flatMap(([k, v]) => [flatten.call(this, k), flatten.call(this, v)])
100
- .join(",")}]`;
103
+ if (input.size > 0) {
104
+ str[index] = `["${utils_js_1.TYPE_MAP}",${[...input]
105
+ .flatMap(([k, v]) => [
106
+ flatten.call(this, k),
107
+ flatten.call(this, v),
108
+ ])
109
+ .join(",")}]`;
110
+ }
111
+ else {
112
+ str[index] = `["${utils_js_1.TYPE_MAP}"]`;
113
+ }
101
114
  }
102
115
  else if (input instanceof Promise) {
103
116
  str[index] = `["${utils_js_1.TYPE_PROMISE}",${index}]`;
@@ -122,8 +135,30 @@ function stringify(input, index) {
122
135
  }
123
136
  break;
124
137
  }
125
- default:
126
- throw new Error("Cannot encode function or unexpected type");
138
+ default: {
139
+ const isArray = Array.isArray(input);
140
+ let pluginHandled = false;
141
+ if (!isArray && plugins) {
142
+ for (const plugin of plugins) {
143
+ const pluginResult = plugin(input);
144
+ if (Array.isArray(pluginResult)) {
145
+ pluginHandled = true;
146
+ const [pluginIdentifier, ...rest] = pluginResult;
147
+ str[index] = `[${JSON.stringify(pluginIdentifier)}`;
148
+ if (rest.length > 0) {
149
+ str[index] += `,${rest
150
+ .map((v) => flatten.call(this, v))
151
+ .join(",")}`;
152
+ }
153
+ str[index] += "]";
154
+ break;
155
+ }
156
+ }
157
+ }
158
+ if (!pluginHandled) {
159
+ throw new Error("Cannot encode function or unexpected type");
160
+ }
161
+ }
127
162
  }
128
163
  }
129
164
  const objectProtoNames = Object.getOwnPropertyNames(Object.prototype)
@@ -121,6 +121,9 @@ function encode(input, options) {
121
121
  const readable = new ReadableStream({
122
122
  async start(controller) {
123
123
  const id = flatten_js_1.flatten.call(encoder, input);
124
+ if (Array.isArray(id)) {
125
+ throw new Error("This should never happen");
126
+ }
124
127
  if (id < 0) {
125
128
  controller.enqueue(textEncoder.encode(`${id}\n`));
126
129
  }
@@ -136,7 +139,10 @@ function encode(input, options) {
136
139
  seenPromises.add((encoder.deferred[Number(deferredId)] = raceSignal(deferred, encoder.signal)
137
140
  .then((resolved) => {
138
141
  const id = flatten_js_1.flatten.call(encoder, resolved);
139
- if (id < 0) {
142
+ if (Array.isArray(id)) {
143
+ controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_PROMISE}${deferredId}:[["${utils_js_1.TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n`));
144
+ }
145
+ else if (id < 0) {
140
146
  controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_PROMISE}${deferredId}:${id}\n`));
141
147
  }
142
148
  else {
@@ -153,7 +159,10 @@ function encode(input, options) {
153
159
  reason = new Error("An unknown error occurred");
154
160
  }
155
161
  const id = flatten_js_1.flatten.call(encoder, reason);
156
- if (id < 0) {
162
+ if (Array.isArray(id)) {
163
+ controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_ERROR}${deferredId}:[["${utils_js_1.TYPE_PREVIOUS_RESOLVED}",${id[0]}]]\n`));
164
+ }
165
+ else if (id < 0) {
157
166
  controller.enqueue(textEncoder.encode(`${utils_js_1.TYPE_ERROR}${deferredId}:${id}\n`));
158
167
  }
159
168
  else {
@@ -16,6 +16,7 @@ var TYPE_REGEXP = "R";
16
16
  var TYPE_SET = "S";
17
17
  var TYPE_SYMBOL = "Y";
18
18
  var TYPE_URL = "U";
19
+ var TYPE_PREVIOUS_RESOLVED = "Z";
19
20
  var Deferred = class {
20
21
  promise;
21
22
  resolve;
@@ -52,7 +53,7 @@ function flatten(input) {
52
53
  const { indices } = this;
53
54
  const existing = indices.get(input);
54
55
  if (existing)
55
- return existing;
56
+ return [existing];
56
57
  if (input === void 0)
57
58
  return UNDEFINED;
58
59
  if (input === null)
@@ -129,9 +130,20 @@ function stringify(input, index) {
129
130
  input.source
130
131
  )},${JSON.stringify(input.flags)}]`;
131
132
  } else if (input instanceof Set) {
132
- str[index] = `["${TYPE_SET}",${[...input].map((val) => flatten.call(this, val)).join(",")}]`;
133
+ if (input.size > 0) {
134
+ str[index] = `["${TYPE_SET}",${[...input].map((val) => flatten.call(this, val)).join(",")}]`;
135
+ } else {
136
+ str[index] = `["${TYPE_SET}"]`;
137
+ }
133
138
  } else if (input instanceof Map) {
134
- str[index] = `["${TYPE_MAP}",${[...input].flatMap(([k, v]) => [flatten.call(this, k), flatten.call(this, v)]).join(",")}]`;
139
+ if (input.size > 0) {
140
+ str[index] = `["${TYPE_MAP}",${[...input].flatMap(([k, v]) => [
141
+ flatten.call(this, k),
142
+ flatten.call(this, v)
143
+ ]).join(",")}]`;
144
+ } else {
145
+ str[index] = `["${TYPE_MAP}"]`;
146
+ }
135
147
  } else if (input instanceof Promise) {
136
148
  str[index] = `["${TYPE_PROMISE}",${index}]`;
137
149
  deferred[index] = input;
@@ -151,8 +163,28 @@ function stringify(input, index) {
151
163
  }
152
164
  break;
153
165
  }
154
- default:
155
- throw new Error("Cannot encode function or unexpected type");
166
+ default: {
167
+ const isArray = Array.isArray(input);
168
+ let pluginHandled = false;
169
+ if (!isArray && plugins) {
170
+ for (const plugin of plugins) {
171
+ const pluginResult = plugin(input);
172
+ if (Array.isArray(pluginResult)) {
173
+ pluginHandled = true;
174
+ const [pluginIdentifier, ...rest] = pluginResult;
175
+ str[index] = `[${JSON.stringify(pluginIdentifier)}`;
176
+ if (rest.length > 0) {
177
+ str[index] += `,${rest.map((v) => flatten.call(this, v)).join(",")}`;
178
+ }
179
+ str[index] += "]";
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ if (!pluginHandled) {
185
+ throw new Error("Cannot encode function or unexpected type");
186
+ }
187
+ }
156
188
  }
157
189
  }
158
190
  var objectProtoNames = Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
@@ -244,6 +276,8 @@ function hydrate(index) {
244
276
  let error = errorType && globalObj && globalObj[errorType] ? new globalObj[errorType](message) : new Error(message);
245
277
  hydrated[index] = error;
246
278
  return error;
279
+ case TYPE_PREVIOUS_RESOLVED:
280
+ return hydrate.call(this, b);
247
281
  default:
248
282
  if (Array.isArray(plugins)) {
249
283
  const args = value.slice(1).map((i) => hydrate.call(this, i));
@@ -386,6 +420,9 @@ function encode(input, options) {
386
420
  const readable = new ReadableStream({
387
421
  async start(controller) {
388
422
  const id = flatten.call(encoder, input);
423
+ if (Array.isArray(id)) {
424
+ throw new Error("This should never happen");
425
+ }
389
426
  if (id < 0) {
390
427
  controller.enqueue(textEncoder.encode(`${id}
391
428
  `));
@@ -408,7 +445,14 @@ function encode(input, options) {
408
445
  ).then(
409
446
  (resolved) => {
410
447
  const id2 = flatten.call(encoder, resolved);
411
- if (id2 < 0) {
448
+ if (Array.isArray(id2)) {
449
+ controller.enqueue(
450
+ textEncoder.encode(
451
+ `${TYPE_PROMISE}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
452
+ `
453
+ )
454
+ );
455
+ } else if (id2 < 0) {
412
456
  controller.enqueue(
413
457
  textEncoder.encode(`${TYPE_PROMISE}${deferredId}:${id2}
414
458
  `)
@@ -429,7 +473,14 @@ function encode(input, options) {
429
473
  reason = new Error("An unknown error occurred");
430
474
  }
431
475
  const id2 = flatten.call(encoder, reason);
432
- if (id2 < 0) {
476
+ if (Array.isArray(id2)) {
477
+ controller.enqueue(
478
+ textEncoder.encode(
479
+ `${TYPE_ERROR}${deferredId}:[["${TYPE_PREVIOUS_RESOLVED}",${id2[0]}]]
480
+ `
481
+ )
482
+ );
483
+ } else if (id2 < 0) {
433
484
  controller.enqueue(
434
485
  textEncoder.encode(`${TYPE_ERROR}${deferredId}:${id2}
435
486
  `)
package/dist/unflatten.js CHANGED
@@ -89,6 +89,8 @@ function hydrate(index) {
89
89
  : new Error(message);
90
90
  hydrated[index] = error;
91
91
  return error;
92
+ case utils_js_1.TYPE_PREVIOUS_RESOLVED:
93
+ return hydrate.call(this, b);
92
94
  default:
93
95
  // Run plugins at the end so we have a chance to resolve primitives
94
96
  // without running into a loop
package/dist/utils.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare const TYPE_REGEXP = "R";
15
15
  export declare const TYPE_SET = "S";
16
16
  export declare const TYPE_SYMBOL = "Y";
17
17
  export declare const TYPE_URL = "U";
18
+ export declare const TYPE_PREVIOUS_RESOLVED = "Z";
18
19
  export type DecodePlugin = (type: string, ...data: unknown[]) => {
19
20
  value: unknown;
20
21
  } | false | null | undefined;
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
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.NULL = exports.NEGATIVE_ZERO = exports.NEGATIVE_INFINITY = exports.NAN = exports.HOLE = void 0;
3
+ 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;
4
4
  exports.HOLE = -1;
5
5
  exports.NAN = -2;
6
6
  exports.NEGATIVE_INFINITY = -3;
@@ -18,6 +18,7 @@ exports.TYPE_REGEXP = "R";
18
18
  exports.TYPE_SET = "S";
19
19
  exports.TYPE_SYMBOL = "Y";
20
20
  exports.TYPE_URL = "U";
21
+ exports.TYPE_PREVIOUS_RESOLVED = "Z";
21
22
  class Deferred {
22
23
  promise;
23
24
  resolve;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbo-stream",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "A streaming data transport format that aims to support built-in features such as Promises, Dates, RegExps, Maps, Sets and more.",
5
5
  "files": [
6
6
  "dist",