assistant-stream 0.3.25 → 0.3.27

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.
Files changed (216) hide show
  1. package/README.md +4 -1
  2. package/dist/core/AssistantStream.d.ts +0 -1
  3. package/dist/core/AssistantStream.d.ts.map +1 -1
  4. package/dist/core/AssistantStreamChunk.d.ts +26 -14
  5. package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
  6. package/dist/core/accumulators/AssistantMessageStream.d.ts +0 -1
  7. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  8. package/dist/core/accumulators/TimingTracker.d.ts +0 -1
  9. package/dist/core/accumulators/TimingTracker.d.ts.map +1 -1
  10. package/dist/core/accumulators/assistant-message-accumulator.d.ts +3 -9
  11. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
  12. package/dist/core/accumulators/assistant-message-accumulator.js +70 -28
  13. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  14. package/dist/core/converters/toGenericMessages.d.ts.map +1 -1
  15. package/dist/core/converters/toGenericMessages.js +2 -1
  16. package/dist/core/converters/toGenericMessages.js.map +1 -1
  17. package/dist/core/gorp/GorpStreamAccumulator.d.ts +15 -0
  18. package/dist/core/gorp/GorpStreamAccumulator.d.ts.map +1 -0
  19. package/dist/core/{object/ObjectStreamAccumulator.js → gorp/GorpStreamAccumulator.js} +11 -9
  20. package/dist/core/gorp/GorpStreamAccumulator.js.map +1 -0
  21. package/dist/core/gorp/GorpStreamDeltaTracker.d.ts +17 -0
  22. package/dist/core/gorp/GorpStreamDeltaTracker.d.ts.map +1 -0
  23. package/dist/core/gorp/GorpStreamDeltaTracker.js +38 -0
  24. package/dist/core/gorp/GorpStreamDeltaTracker.js.map +1 -0
  25. package/dist/core/gorp/GorpStreamResponse.d.ts +16 -0
  26. package/dist/core/gorp/GorpStreamResponse.d.ts.map +1 -0
  27. package/dist/core/gorp/GorpStreamResponse.js +51 -0
  28. package/dist/core/gorp/GorpStreamResponse.js.map +1 -0
  29. package/dist/core/gorp/changeTree.d.ts +16 -0
  30. package/dist/core/gorp/changeTree.d.ts.map +1 -0
  31. package/dist/core/gorp/changeTree.js +77 -0
  32. package/dist/core/gorp/changeTree.js.map +1 -0
  33. package/dist/core/gorp/createGorpStream.d.ts +16 -0
  34. package/dist/core/gorp/createGorpStream.d.ts.map +1 -0
  35. package/dist/core/{object/createObjectStream.js → gorp/createGorpStream.js} +13 -8
  36. package/dist/core/gorp/createGorpStream.js.map +1 -0
  37. package/dist/core/gorp/types.d.ts +20 -0
  38. package/dist/core/gorp/types.d.ts.map +1 -0
  39. package/dist/core/modules/assistant-stream.d.ts +18 -11
  40. package/dist/core/modules/assistant-stream.d.ts.map +1 -1
  41. package/dist/core/modules/assistant-stream.js +10 -6
  42. package/dist/core/modules/assistant-stream.js.map +1 -1
  43. package/dist/core/modules/text.d.ts +0 -1
  44. package/dist/core/modules/text.d.ts.map +1 -1
  45. package/dist/core/modules/tool-call.d.ts +0 -1
  46. package/dist/core/modules/tool-call.d.ts.map +1 -1
  47. package/dist/core/modules/tool-call.js +2 -2
  48. package/dist/core/modules/tool-call.js.map +1 -1
  49. package/dist/core/serialization/PlainText.d.ts +0 -1
  50. package/dist/core/serialization/PlainText.d.ts.map +1 -1
  51. package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts +0 -1
  52. package/dist/core/serialization/assistant-transport/AssistantTransport.d.ts.map +1 -1
  53. package/dist/core/serialization/assistant-transport/AssistantTransport.js +93 -53
  54. package/dist/core/serialization/assistant-transport/AssistantTransport.js.map +1 -1
  55. package/dist/core/serialization/data-stream/DataStream.d.ts +0 -1
  56. package/dist/core/serialization/data-stream/DataStream.d.ts.map +1 -1
  57. package/dist/core/serialization/data-stream/chunk-types.d.ts +3 -4
  58. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
  59. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  60. package/dist/core/serialization/data-stream/serialization.d.ts +0 -1
  61. package/dist/core/serialization/data-stream/serialization.d.ts.map +1 -1
  62. package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts +0 -1
  63. package/dist/core/serialization/ui-message-stream/UIMessageStream.d.ts.map +1 -1
  64. package/dist/core/serialization/ui-message-stream/UIMessageStream.js +76 -53
  65. package/dist/core/serialization/ui-message-stream/UIMessageStream.js.map +1 -1
  66. package/dist/core/serialization/ui-message-stream/chunk-types.d.ts +0 -1
  67. package/dist/core/serialization/ui-message-stream/chunk-types.d.ts.map +1 -1
  68. package/dist/core/tool/ToolCallReader.d.ts +1 -0
  69. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  70. package/dist/core/tool/ToolCallReader.js.map +1 -1
  71. package/dist/core/tool/ToolExecutionStream.d.ts +0 -1
  72. package/dist/core/tool/ToolExecutionStream.d.ts.map +1 -1
  73. package/dist/core/tool/ToolExecutionStream.js +9 -1
  74. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  75. package/dist/core/tool/ToolResponse.d.ts +6 -4
  76. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  77. package/dist/core/tool/schema-utils.d.ts +0 -1
  78. package/dist/core/tool/schema-utils.d.ts.map +1 -1
  79. package/dist/core/tool/tool-types.d.ts +71 -33
  80. package/dist/core/tool/tool-types.d.ts.map +1 -1
  81. package/dist/core/tool/toolResultStream.d.ts +3 -2
  82. package/dist/core/tool/toolResultStream.d.ts.map +1 -1
  83. package/dist/core/tool/toolResultStream.js +7 -2
  84. package/dist/core/tool/toolResultStream.js.map +1 -1
  85. package/dist/core/tool/type-path-utils.d.ts +2 -2
  86. package/dist/core/tool/type-path-utils.d.ts.map +1 -1
  87. package/dist/core/utils/Counter.d.ts.map +1 -1
  88. package/dist/core/utils/generateId.d.ts.map +1 -1
  89. package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts +0 -1
  90. package/dist/core/utils/stream/AssistantMetaTransformStream.d.ts.map +1 -1
  91. package/dist/core/utils/stream/AssistantTransformStream.d.ts +0 -1
  92. package/dist/core/utils/stream/AssistantTransformStream.d.ts.map +1 -1
  93. package/dist/core/utils/stream/LineDecoderStream.d.ts +1 -0
  94. package/dist/core/utils/stream/LineDecoderStream.d.ts.map +1 -1
  95. package/dist/core/utils/stream/LineDecoderStream.js +19 -6
  96. package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
  97. package/dist/core/utils/stream/PipeableTransformStream.d.ts.map +1 -1
  98. package/dist/core/utils/stream/SSE.d.ts +0 -1
  99. package/dist/core/utils/stream/SSE.d.ts.map +1 -1
  100. package/dist/core/utils/stream/SSE.js +2 -52
  101. package/dist/core/utils/stream/SSE.js.map +1 -1
  102. package/dist/core/utils/stream/SSEEventDecoder.d.ts +27 -0
  103. package/dist/core/utils/stream/SSEEventDecoder.d.ts.map +1 -0
  104. package/dist/core/utils/stream/SSEEventDecoder.js +84 -0
  105. package/dist/core/utils/stream/SSEEventDecoder.js.map +1 -0
  106. package/dist/core/utils/stream/SSEEventDecoderStream.d.ts +13 -0
  107. package/dist/core/utils/stream/SSEEventDecoderStream.d.ts.map +1 -0
  108. package/dist/core/utils/stream/SSEEventDecoderStream.js +24 -0
  109. package/dist/core/utils/stream/SSEEventDecoderStream.js.map +1 -0
  110. package/dist/core/utils/stream/UnderlyingReadable.d.ts.map +1 -1
  111. package/dist/core/utils/stream/merge.d.ts +2 -1
  112. package/dist/core/utils/stream/merge.d.ts.map +1 -1
  113. package/dist/core/utils/stream/merge.js +27 -8
  114. package/dist/core/utils/stream/merge.js.map +1 -1
  115. package/dist/core/utils/stream/path-utils.d.ts +0 -1
  116. package/dist/core/utils/stream/path-utils.d.ts.map +1 -1
  117. package/dist/core/utils/types.d.ts +16 -8
  118. package/dist/core/utils/types.d.ts.map +1 -1
  119. package/dist/core/utils/withPromiseOrValue.d.ts.map +1 -1
  120. package/dist/index.d.ts +25 -4
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +14 -3
  123. package/dist/index.js.map +1 -0
  124. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +3 -3
  125. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
  126. package/dist/resumable/ResumableStreamContext.d.ts +0 -1
  127. package/dist/resumable/ResumableStreamContext.d.ts.map +1 -1
  128. package/dist/resumable/ResumableStreamContext.js +12 -5
  129. package/dist/resumable/ResumableStreamContext.js.map +1 -1
  130. package/dist/resumable/constants.d.ts.map +1 -1
  131. package/dist/resumable/createResumableAssistantStreamResponse.d.ts +6 -4
  132. package/dist/resumable/createResumableAssistantStreamResponse.d.ts.map +1 -1
  133. package/dist/resumable/errors.d.ts.map +1 -1
  134. package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts +0 -1
  135. package/dist/resumable/stores/InMemoryResumableStreamStore.d.ts.map +1 -1
  136. package/dist/resumable/stores/InMemoryResumableStreamStore.js +2 -1
  137. package/dist/resumable/stores/InMemoryResumableStreamStore.js.map +1 -1
  138. package/dist/resumable/stores/ioredis.d.ts +0 -1
  139. package/dist/resumable/stores/ioredis.d.ts.map +1 -1
  140. package/dist/resumable/stores/redis-impl.d.ts +2 -2
  141. package/dist/resumable/stores/redis-impl.d.ts.map +1 -1
  142. package/dist/resumable/stores/redis.d.ts +0 -1
  143. package/dist/resumable/stores/redis.d.ts.map +1 -1
  144. package/dist/resumable/types.d.ts.map +1 -1
  145. package/dist/utils/AsyncIterableStream.d.ts.map +1 -1
  146. package/dist/utils/AsyncIterableStream.js +18 -3
  147. package/dist/utils/AsyncIterableStream.js.map +1 -1
  148. package/dist/utils/json/fix-json.d.ts.map +1 -1
  149. package/dist/utils/json/json-value.d.ts.map +1 -1
  150. package/dist/utils/json/parse-partial-json-object.d.ts +0 -1
  151. package/dist/utils/json/parse-partial-json-object.d.ts.map +1 -1
  152. package/dist/utils/promiseWithResolvers.d.ts.map +1 -1
  153. package/dist/utils.d.ts +2 -1
  154. package/dist/utils.js +2 -1
  155. package/package.json +5 -5
  156. package/src/core/AssistantStreamChunk.ts +5 -3
  157. package/src/core/accumulators/assistant-message-accumulator.test.ts +437 -1
  158. package/src/core/accumulators/assistant-message-accumulator.ts +89 -26
  159. package/src/core/{object/ObjectStream.test.ts → gorp/GorpStream.test.ts} +104 -23
  160. package/src/core/gorp/GorpStreamAccumulator.test.ts +28 -0
  161. package/src/core/{object/ObjectStreamAccumulator.ts → gorp/GorpStreamAccumulator.ts} +13 -15
  162. package/src/core/gorp/GorpStreamDeltaTracker.test.ts +182 -0
  163. package/src/core/gorp/GorpStreamDeltaTracker.ts +55 -0
  164. package/src/core/gorp/GorpStreamResponse.test.ts +32 -0
  165. package/src/core/gorp/GorpStreamResponse.ts +88 -0
  166. package/src/core/gorp/changeTree.test.ts +168 -0
  167. package/src/core/gorp/changeTree.ts +96 -0
  168. package/src/core/{object/createObjectStream.ts → gorp/createGorpStream.ts} +23 -17
  169. package/src/core/{object → gorp}/types.ts +5 -3
  170. package/src/core/modules/assistant-stream.test.ts +154 -2
  171. package/src/core/modules/assistant-stream.ts +16 -6
  172. package/src/core/modules/tool-call.test.ts +67 -0
  173. package/src/core/modules/tool-call.ts +3 -3
  174. package/src/core/serialization/assistant-transport/AssistantTransport.test.ts +215 -1
  175. package/src/core/serialization/assistant-transport/AssistantTransport.ts +83 -71
  176. package/src/core/serialization/data-stream/chunk-types.ts +2 -2
  177. package/src/core/serialization/ui-message-stream/UIMessageStream.test.ts +323 -4
  178. package/src/core/serialization/ui-message-stream/UIMessageStream.ts +81 -71
  179. package/src/core/tool/ToolExecutionStream.ts +10 -0
  180. package/src/core/tool/toolResultStream.test.ts +50 -0
  181. package/src/core/tool/toolResultStream.ts +7 -2
  182. package/src/core/utils/stream/LineDecoderStream.test.ts +26 -2
  183. package/src/core/utils/stream/LineDecoderStream.ts +21 -11
  184. package/src/core/utils/stream/SSE.ts +6 -70
  185. package/src/core/utils/stream/SSEEventDecoder.test.ts +146 -0
  186. package/src/core/utils/stream/SSEEventDecoder.ts +109 -0
  187. package/src/core/utils/stream/SSEEventDecoderStream.test.ts +42 -0
  188. package/src/core/utils/stream/SSEEventDecoderStream.ts +34 -0
  189. package/src/core/utils/stream/merge.ts +31 -9
  190. package/src/gorp.test.ts +42 -0
  191. package/src/index.ts +21 -6
  192. package/src/resumable/ResumableStreamContext.test.ts +116 -1
  193. package/src/resumable/ResumableStreamContext.ts +32 -5
  194. package/src/resumable/createResumableAssistantStreamResponse.test.ts +1 -25
  195. package/src/utils/AsyncIterableStream.test.ts +61 -0
  196. package/src/utils/AsyncIterableStream.ts +18 -3
  197. package/src/utils.ts +4 -0
  198. package/dist/core/object/ObjectStreamAccumulator.d.ts +0 -15
  199. package/dist/core/object/ObjectStreamAccumulator.d.ts.map +0 -1
  200. package/dist/core/object/ObjectStreamAccumulator.js.map +0 -1
  201. package/dist/core/object/ObjectStreamResponse.d.ts +0 -17
  202. package/dist/core/object/ObjectStreamResponse.d.ts.map +0 -1
  203. package/dist/core/object/ObjectStreamResponse.js +0 -57
  204. package/dist/core/object/ObjectStreamResponse.js.map +0 -1
  205. package/dist/core/object/createObjectStream.d.ts +0 -19
  206. package/dist/core/object/createObjectStream.d.ts.map +0 -1
  207. package/dist/core/object/createObjectStream.js.map +0 -1
  208. package/dist/core/object/types.d.ts +0 -18
  209. package/dist/core/object/types.d.ts.map +0 -1
  210. package/dist/utils/json/is-json.d.ts +0 -9
  211. package/dist/utils/json/is-json.d.ts.map +0 -1
  212. package/dist/utils/json/is-json.js +0 -17
  213. package/dist/utils/json/is-json.js.map +0 -1
  214. package/src/core/object/ObjectStreamResponse.ts +0 -109
  215. package/src/utils/json/is-json.ts +0 -43
  216. /package/dist/core/{object → gorp}/types.js +0 -0
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import { createResumableStreamContext } from "./ResumableStreamContext";
3
3
  import { ResumableStreamError } from "./errors";
4
4
  import { createInMemoryResumableStreamStore } from "./stores/InMemoryResumableStreamStore";
@@ -8,6 +8,10 @@ const dec = new TextDecoder();
8
8
 
9
9
  const bytes = (s: string): Uint8Array => enc.encode(s);
10
10
 
11
+ afterEach(() => {
12
+ vi.restoreAllMocks();
13
+ });
14
+
11
15
  async function collect(stream: ReadableStream<Uint8Array>): Promise<string> {
12
16
  const reader = stream.getReader();
13
17
  let out = "";
@@ -271,4 +275,115 @@ describe("createResumableStreamContext", () => {
271
275
  expect(errors[0]!.id).toBe("a");
272
276
  expect((errors[0]!.error as Error).message).toBe("boom");
273
277
  });
278
+
279
+ it("continues acquisition when onAcquire throws", async () => {
280
+ const consoleError = vi
281
+ .spyOn(console, "error")
282
+ .mockImplementation(() => {});
283
+ const hookError = new Error("acquire observer failed");
284
+ const store = createInMemoryResumableStreamStore();
285
+ const ctx = createResumableStreamContext({
286
+ store,
287
+ onAcquire: () => {
288
+ throw hookError;
289
+ },
290
+ });
291
+
292
+ const stream = await ctx.run("a", () => makeStringStream(["hello"]));
293
+
294
+ expect(await collect(stream)).toBe("hello");
295
+ expect(await ctx.status("a")).toBe("done");
296
+ expect(consoleError).toHaveBeenCalledWith(
297
+ "resumable stream onAcquire hook failed:",
298
+ hookError,
299
+ );
300
+ });
301
+
302
+ it("continues streaming when onAppend throws", async () => {
303
+ vi.spyOn(console, "error").mockImplementation(() => {});
304
+ const store = createInMemoryResumableStreamStore();
305
+ const ctx = createResumableStreamContext({
306
+ store,
307
+ onAppend: () => {
308
+ throw new Error("append observer failed");
309
+ },
310
+ });
311
+
312
+ const stream = await ctx.run("a", () =>
313
+ makeStringStream(["hello ", "world"]),
314
+ );
315
+
316
+ expect(await collect(stream)).toBe("hello world");
317
+ expect(await ctx.status("a")).toBe("done");
318
+ });
319
+
320
+ it("continues streaming when async onAppend rejects", async () => {
321
+ const consoleError = vi
322
+ .spyOn(console, "error")
323
+ .mockImplementation(() => {});
324
+ const hookError = new Error("async append observer failed");
325
+ const store = createInMemoryResumableStreamStore();
326
+ const ctx = createResumableStreamContext({
327
+ store,
328
+ onAppend: async () => {
329
+ throw hookError;
330
+ },
331
+ });
332
+
333
+ const stream = await ctx.run("a", () => makeStringStream(["hello"]));
334
+
335
+ expect(await collect(stream)).toBe("hello");
336
+ expect(await ctx.status("a")).toBe("done");
337
+ await vi.waitFor(() => {
338
+ expect(consoleError).toHaveBeenCalledWith(
339
+ "resumable stream onAppend hook failed:",
340
+ hookError,
341
+ );
342
+ });
343
+ });
344
+
345
+ it("keeps successful completion when onFinalize throws", async () => {
346
+ vi.spyOn(console, "error").mockImplementation(() => {});
347
+ const onError = vi.fn();
348
+ const onFinalize = vi.fn(() => {
349
+ throw new Error("finalize observer failed");
350
+ });
351
+ const store = createInMemoryResumableStreamStore();
352
+ const ctx = createResumableStreamContext({ store, onError, onFinalize });
353
+
354
+ const stream = await ctx.run("a", () => makeStringStream(["hello"]));
355
+
356
+ expect(await collect(stream)).toBe("hello");
357
+ expect(await ctx.status("a")).toBe("done");
358
+ expect(onFinalize).toHaveBeenCalledOnce();
359
+ expect(onError).not.toHaveBeenCalled();
360
+ });
361
+
362
+ it("finalizes producer errors when onError throws", async () => {
363
+ vi.spyOn(console, "error").mockImplementation(() => {});
364
+ const tasks: Promise<unknown>[] = [];
365
+ const store = createInMemoryResumableStreamStore();
366
+ const ctx = createResumableStreamContext({
367
+ store,
368
+ waitUntil: (task) => tasks.push(task),
369
+ onError: () => {
370
+ throw new Error("error observer failed");
371
+ },
372
+ });
373
+ const producerError = new Error("producer failed");
374
+
375
+ const stream = await ctx.run(
376
+ "a",
377
+ () =>
378
+ new ReadableStream<Uint8Array>({
379
+ start(controller) {
380
+ controller.error(producerError);
381
+ },
382
+ }),
383
+ );
384
+ await Promise.allSettled(tasks);
385
+
386
+ expect(await ctx.status("a")).toBe("error");
387
+ await expect(collect(stream)).rejects.toThrow("producer failed");
388
+ });
274
389
  });
@@ -1,4 +1,5 @@
1
1
  import { ResumableStreamError } from "./errors";
2
+ import { withPromiseOrValue } from "../core/utils/withPromiseOrValue";
2
3
  import type {
3
4
  ResumableStreamRole,
4
5
  ResumableStreamStatus,
@@ -42,6 +43,21 @@ export interface ResumableStreamContext {
42
43
  delete(streamId: string): Promise<void>;
43
44
  }
44
45
 
46
+ const invokeObservabilityHook = <TArgs extends unknown[]>(
47
+ name: string,
48
+ hook: ((...args: TArgs) => void) | undefined,
49
+ ...args: TArgs
50
+ ): void => {
51
+ if (!hook) return;
52
+ void withPromiseOrValue(
53
+ () => hook(...args),
54
+ () => {},
55
+ (error) => {
56
+ console.error(`resumable stream ${name} hook failed:`, error);
57
+ },
58
+ );
59
+ };
60
+
45
61
  export function createResumableStreamContext(
46
62
  options: ResumableStreamContextOptions,
47
63
  ): ResumableStreamContext {
@@ -53,7 +69,7 @@ export function createResumableStreamContext(
53
69
  return {
54
70
  async run(streamId, makeStream) {
55
71
  const role = await store.acquire(streamId, acquireOptions);
56
- onAcquire?.(streamId, role);
72
+ invokeObservabilityHook("onAcquire", onAcquire, streamId, role);
57
73
  if (role === "producer") {
58
74
  startProducerTask(store, streamId, makeStream, {
59
75
  waitUntil,
@@ -119,13 +135,18 @@ function startProducerTask(
119
135
  const { done, value } = await reader.read();
120
136
  if (done) break;
121
137
  await store.append(streamId, value);
122
- onAppend?.(streamId, value.byteLength);
138
+ invokeObservabilityHook(
139
+ "onAppend",
140
+ onAppend,
141
+ streamId,
142
+ value.byteLength,
143
+ );
123
144
  }
124
145
  await store.finalize(streamId, "done");
125
- onFinalize?.(streamId, "done");
146
+ invokeObservabilityHook("onFinalize", onFinalize, streamId, "done");
126
147
  } catch (err) {
127
148
  cancelled = true;
128
- onError?.(streamId, err);
149
+ invokeObservabilityHook("onError", onError, streamId, err);
129
150
  const message = err instanceof Error ? err.message : String(err);
130
151
  try {
131
152
  await reader?.cancel(err);
@@ -134,7 +155,13 @@ function startProducerTask(
134
155
  }
135
156
  try {
136
157
  await store.finalize(streamId, "error", message);
137
- onFinalize?.(streamId, "error", message);
158
+ invokeObservabilityHook(
159
+ "onFinalize",
160
+ onFinalize,
161
+ streamId,
162
+ "error",
163
+ message,
164
+ );
138
165
  } catch (finalizeErr) {
139
166
  console.error("resumable stream finalize failed:", finalizeErr);
140
167
  }
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  RESUMABLE_STREAM_ID_HEADER,
4
4
  createResumableAssistantStreamResponse,
@@ -140,25 +140,6 @@ describe("createResumableAssistantStreamResponse", () => {
140
140
  });
141
141
 
142
142
  describe("producer crash", () => {
143
- let suppressed: unknown[];
144
- let original: NodeJS.UnhandledRejectionListener[];
145
- const swallow: NodeJS.UnhandledRejectionListener = (reason) => {
146
- suppressed.push(reason);
147
- };
148
-
149
- beforeEach(() => {
150
- suppressed = [];
151
- original = process.listeners("unhandledRejection");
152
- for (const l of original) process.off("unhandledRejection", l);
153
- process.on("unhandledRejection", swallow);
154
- });
155
-
156
- afterEach(async () => {
157
- await new Promise((r) => setTimeout(r, 20));
158
- process.off("unhandledRejection", swallow);
159
- for (const l of original) process.on("unhandledRejection", l);
160
- });
161
-
162
143
  it("synchronous callback throw is encoded into the body and finalizes the stream", async () => {
163
144
  const ctx = createResumableStreamContext({
164
145
  store: createInMemoryResumableStreamStore(),
@@ -184,11 +165,6 @@ describe("createResumableAssistantStreamResponse", () => {
184
165
  expect(replay).not.toBeNull();
185
166
  const replayBytes = await new Response(replay).arrayBuffer();
186
167
  expect(new Uint8Array(replayBytes)).toEqual(new Uint8Array(firstBytes));
187
-
188
- while (suppressed.length === 0) {
189
- await new Promise((r) => setTimeout(r, 5));
190
- }
191
- expect(suppressed.map((e) => (e as Error).message)).toContain("boom");
192
168
  });
193
169
  });
194
170
 
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { asAsyncIterableStream } from "./AsyncIterableStream";
3
+
4
+ const forcePolyfilledIterator = <T>(
5
+ stream: ReadableStream<T>,
6
+ ): AsyncIterable<T> & ReadableStream<T> => {
7
+ Object.defineProperty(stream, Symbol.asyncIterator, {
8
+ configurable: true,
9
+ value: undefined,
10
+ writable: true,
11
+ });
12
+ return asAsyncIterableStream(stream);
13
+ };
14
+
15
+ describe("asAsyncIterableStream", () => {
16
+ it("cancels the source when iteration stops early", async () => {
17
+ const cancel = vi.fn();
18
+ const stream = forcePolyfilledIterator(
19
+ new ReadableStream<string>({
20
+ start(controller) {
21
+ controller.enqueue("first");
22
+ controller.enqueue("second");
23
+ },
24
+ cancel,
25
+ }),
26
+ );
27
+
28
+ const values: string[] = [];
29
+ for await (const value of stream) {
30
+ values.push(value);
31
+ break;
32
+ }
33
+
34
+ expect(values).toEqual(["first"]);
35
+ expect(cancel).toHaveBeenCalledOnce();
36
+ expect(stream.locked).toBe(false);
37
+ });
38
+
39
+ it("does not cancel a fully consumed source", async () => {
40
+ const cancel = vi.fn();
41
+ const stream = forcePolyfilledIterator(
42
+ new ReadableStream<string>({
43
+ start(controller) {
44
+ controller.enqueue("first");
45
+ controller.enqueue("second");
46
+ controller.close();
47
+ },
48
+ cancel,
49
+ }),
50
+ );
51
+
52
+ const values: string[] = [];
53
+ for await (const value of stream) {
54
+ values.push(value);
55
+ }
56
+
57
+ expect(values).toEqual(["first", "second"]);
58
+ expect(cancel).not.toHaveBeenCalled();
59
+ expect(stream.locked).toBe(false);
60
+ });
61
+ });
@@ -4,14 +4,29 @@ async function* streamGeneratorPolyfill<T>(
4
4
  this: ReadableStream<T>,
5
5
  ): AsyncGenerator<T, undefined, unknown> {
6
6
  const reader = this.getReader();
7
+ let shouldCancel = true;
7
8
  try {
8
9
  while (true) {
9
- const { done, value } = await reader.read();
10
- if (done) break;
10
+ let result: ReadableStreamReadResult<T>;
11
+ try {
12
+ result = await reader.read();
13
+ } catch (error) {
14
+ shouldCancel = false;
15
+ throw error;
16
+ }
17
+ if (result.done) {
18
+ shouldCancel = false;
19
+ break;
20
+ }
21
+ const { value } = result;
11
22
  yield value;
12
23
  }
13
24
  } finally {
14
- reader.releaseLock();
25
+ try {
26
+ if (shouldCancel) await reader.cancel();
27
+ } finally {
28
+ reader.releaseLock();
29
+ }
15
30
  }
16
31
  }
17
32
 
package/src/utils.ts CHANGED
@@ -15,3 +15,7 @@ export type {
15
15
 
16
16
  export { AssistantTransformStream } from "./core/utils/stream/AssistantTransformStream";
17
17
  export { AssistantMetaTransformStream } from "./core/utils/stream/AssistantMetaTransformStream";
18
+ export {
19
+ SSEEventDecoder,
20
+ type SSEEvent,
21
+ } from "./core/utils/stream/SSEEventDecoder";
@@ -1,15 +0,0 @@
1
- import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
2
- import { ObjectStreamOperation } from "./types.js";
3
-
4
- //#region src/core/object/ObjectStreamAccumulator.d.ts
5
- declare class ObjectStreamAccumulator {
6
- private _state;
7
- constructor(initialValue?: ReadonlyJSONValue);
8
- get state(): ReadonlyJSONValue;
9
- append(ops: readonly ObjectStreamOperation[]): void;
10
- private static apply;
11
- private static updatePath;
12
- }
13
- //#endregion
14
- export { ObjectStreamAccumulator };
15
- //# sourceMappingURL=ObjectStreamAccumulator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamAccumulator.d.ts","names":[],"sources":["../../../src/core/object/ObjectStreamAccumulator.ts"],"mappings":";;;;cAGa,uBAAA;EAAA,QACH,MAAA;cAEI,YAAA,GAAc,iBAAA;EAAA,IAItB,KAAA,IAAK,iBAAA;EAIT,MAAA,CAAO,GAAA,WAAc,qBAAA;EAAA,eAON,KAAA;EAAA,eAuBA,UAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamAccumulator.js","names":["_exhaustiveCheck"],"sources":["../../../src/core/object/ObjectStreamAccumulator.ts"],"sourcesContent":["import type { ReadonlyJSONValue, ReadonlyJSONObject } from \"../../utils\";\nimport type { ObjectStreamOperation } from \"./types\";\n\nexport class ObjectStreamAccumulator {\n private _state: ReadonlyJSONValue;\n\n constructor(initialValue: ReadonlyJSONValue = null) {\n this._state = initialValue;\n }\n\n get state() {\n return this._state;\n }\n\n append(ops: readonly ObjectStreamOperation[]) {\n this._state = ops.reduce(\n (state, op) => ObjectStreamAccumulator.apply(state, op),\n this._state,\n );\n }\n\n private static apply(state: ReadonlyJSONValue, op: ObjectStreamOperation) {\n const type = op.type;\n switch (type) {\n case \"set\":\n return ObjectStreamAccumulator.updatePath(\n state,\n op.path,\n () => op.value,\n );\n case \"append-text\":\n return ObjectStreamAccumulator.updatePath(state, op.path, (current) => {\n if (typeof current !== \"string\")\n throw new Error(`Expected string at path [${op.path.join(\", \")}]`);\n return current + op.value;\n });\n\n default: {\n const _exhaustiveCheck: never = type;\n throw new Error(`Invalid operation type: ${_exhaustiveCheck}`);\n }\n }\n }\n\n private static updatePath(\n state: ReadonlyJSONValue | undefined,\n path: readonly string[],\n updater: (current: ReadonlyJSONValue | undefined) => ReadonlyJSONValue,\n ): ReadonlyJSONValue {\n if (path.length === 0) return updater(state);\n\n // Initialize state as empty object if it's null and we're trying to set a property\n state ??= {};\n\n if (typeof state !== \"object\") {\n throw new Error(`Invalid path: [${path.join(\", \")}]`);\n }\n\n const [key, ...rest] = path as [string, ...(readonly string[])];\n if (Array.isArray(state)) {\n const idx = Number(key);\n if (Number.isNaN(idx))\n throw new Error(`Expected array index at [${path.join(\", \")}]`);\n if (idx > state.length || idx < 0)\n throw new Error(`Insert array index out of bounds`);\n\n const nextState = [...state];\n nextState[idx] = ObjectStreamAccumulator.updatePath(\n nextState[idx],\n rest,\n updater,\n );\n\n return nextState;\n }\n\n const nextState = { ...(state as ReadonlyJSONObject) };\n nextState[key] = ObjectStreamAccumulator.updatePath(\n nextState[key],\n rest,\n updater,\n );\n\n return nextState;\n }\n}\n"],"mappings":";AAGA,IAAa,0BAAb,MAAa,wBAAwB;CACnC;CAEA,YAAY,eAAkC,MAAM;EAClD,KAAK,SAAS;CAChB;CAEA,IAAI,QAAQ;EACV,OAAO,KAAK;CACd;CAEA,OAAO,KAAuC;EAC5C,KAAK,SAAS,IAAI,QACf,OAAO,OAAO,wBAAwB,MAAM,OAAO,EAAE,GACtD,KAAK,MACP;CACF;CAEA,OAAe,MAAM,OAA0B,IAA2B;EACxE,MAAM,OAAO,GAAG;EAChB,QAAQ,MAAR;GACE,KAAK,OACH,OAAO,wBAAwB,WAC7B,OACA,GAAG,YACG,GAAG,KACX;GACF,KAAK,eACH,OAAO,wBAAwB,WAAW,OAAO,GAAG,OAAO,YAAY;IACrE,IAAI,OAAO,YAAY,UACrB,MAAM,IAAI,MAAM,4BAA4B,GAAG,KAAK,KAAK,IAAI,EAAE,EAAE;IACnE,OAAO,UAAU,GAAG;GACtB,CAAC;GAEH,SAEE,MAAM,IAAI,MAAM,2BAA2BA,MAAkB;EAEjE;CACF;CAEA,OAAe,WACb,OACA,MACA,SACmB;EACnB,IAAI,KAAK,WAAW,GAAG,OAAO,QAAQ,KAAK;EAG3C,UAAU,CAAC;EAEX,IAAI,OAAO,UAAU,UACnB,MAAM,IAAI,MAAM,kBAAkB,KAAK,KAAK,IAAI,EAAE,EAAE;EAGtD,MAAM,CAAC,KAAK,GAAG,QAAQ;EACvB,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxB,MAAM,MAAM,OAAO,GAAG;GACtB,IAAI,OAAO,MAAM,GAAG,GAClB,MAAM,IAAI,MAAM,4BAA4B,KAAK,KAAK,IAAI,EAAE,EAAE;GAChE,IAAI,MAAM,MAAM,UAAU,MAAM,GAC9B,MAAM,IAAI,MAAM,kCAAkC;GAEpD,MAAM,YAAY,CAAC,GAAG,KAAK;GAC3B,UAAU,OAAO,wBAAwB,WACvC,UAAU,MACV,MACA,OACF;GAEA,OAAO;EACT;EAEA,MAAM,YAAY,EAAE,GAAI,MAA6B;EACrD,UAAU,OAAO,wBAAwB,WACvC,UAAU,MACV,MACA,OACF;EAEA,OAAO;CACT;AACF"}
@@ -1,17 +0,0 @@
1
- import { ObjectStreamChunk } from "./types.js";
2
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
3
-
4
- //#region src/core/object/ObjectStreamResponse.d.ts
5
- declare class ObjectStreamEncoder extends PipeableTransformStream<ObjectStreamChunk, Uint8Array> {
6
- constructor();
7
- }
8
- declare class ObjectStreamDecoder extends PipeableTransformStream<Uint8Array<ArrayBuffer>, ObjectStreamChunk> {
9
- constructor();
10
- }
11
- declare class ObjectStreamResponse extends Response {
12
- constructor(body: ReadableStream<ObjectStreamChunk>);
13
- }
14
- declare const fromObjectStreamResponse: (response: Response) => ReadableStream<ObjectStreamChunk>;
15
- //#endregion
16
- export { ObjectStreamDecoder, ObjectStreamEncoder, ObjectStreamResponse, fromObjectStreamResponse };
17
- //# sourceMappingURL=ObjectStreamResponse.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamResponse.d.ts","names":[],"sources":["../../../src/core/object/ObjectStreamResponse.ts"],"mappings":";;;;cAKa,mBAAA,SAA4B,uBAAA,CACvC,iBAAA,EACA,UAAA;;;cAgDW,mBAAA,SAA4B,uBAAA,CACvC,UAAA,CAAW,WAAA,GACX,iBAAA;;;cAyBW,oBAAA,SAA6B,QAAA;cAC5B,IAAA,EAAM,cAAA,CAAe,iBAAA;AAAA;AAAA,cAYtB,wBAAA,GACX,QAAA,EAAU,QAAA,KACT,cAAA,CAAe,iBAAA"}
@@ -1,57 +0,0 @@
1
- import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
2
- import { ObjectStreamAccumulator } from "./ObjectStreamAccumulator.js";
3
- import { SSEDecoder, SSEEncoder } from "../utils/stream/SSE.js";
4
- //#region src/core/object/ObjectStreamResponse.ts
5
- var ObjectStreamEncoder = class extends PipeableTransformStream {
6
- constructor() {
7
- super((readable) => readable.pipeThrough((() => {
8
- class ObjectStreamTransformer {
9
- #isFirstChunk = true;
10
- start() {}
11
- transform(chunk, controller) {
12
- if (this.#isFirstChunk && chunk.snapshot && Object.keys(chunk.snapshot).length > 0) controller.enqueue([{
13
- type: "set",
14
- path: [],
15
- value: chunk.snapshot
16
- }, ...chunk.operations]);
17
- else controller.enqueue(chunk.operations);
18
- this.#isFirstChunk = false;
19
- }
20
- }
21
- return new TransformStream(new ObjectStreamTransformer());
22
- })()).pipeThrough(new SSEEncoder()));
23
- }
24
- };
25
- var ObjectStreamDecoder = class extends PipeableTransformStream {
26
- constructor() {
27
- const accumulator = new ObjectStreamAccumulator();
28
- super((readable) => readable.pipeThrough(new SSEDecoder()).pipeThrough(new TransformStream({ transform(operations, controller) {
29
- accumulator.append(operations);
30
- controller.enqueue({
31
- snapshot: accumulator.state,
32
- operations
33
- });
34
- } })));
35
- }
36
- };
37
- var ObjectStreamResponse = class extends Response {
38
- constructor(body) {
39
- super(body.pipeThrough(new ObjectStreamEncoder()), { headers: new Headers({
40
- "Content-Type": "text/event-stream",
41
- "Cache-Control": "no-cache",
42
- Connection: "keep-alive",
43
- "Assistant-Stream-Format": "object-stream/v0"
44
- }) });
45
- }
46
- };
47
- const fromObjectStreamResponse = (response) => {
48
- if (!response.ok) throw new Error(`Response failed, status ${response.status}`);
49
- if (!response.body) throw new Error("Response body is null");
50
- if (response.headers.get("Content-Type") !== "text/event-stream") throw new Error("Response is not an event stream");
51
- if (response.headers.get("Assistant-Stream-Format") !== "object-stream/v0") throw new Error("Unsupported Assistant-Stream-Format header");
52
- return response.body.pipeThrough(new ObjectStreamDecoder());
53
- };
54
- //#endregion
55
- export { ObjectStreamDecoder, ObjectStreamEncoder, ObjectStreamResponse, fromObjectStreamResponse };
56
-
57
- //# sourceMappingURL=ObjectStreamResponse.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ObjectStreamResponse.js","names":["#isFirstChunk"],"sources":["../../../src/core/object/ObjectStreamResponse.ts"],"sourcesContent":["import { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\nimport { ObjectStreamAccumulator } from \"./ObjectStreamAccumulator\";\nimport { SSEDecoder, SSEEncoder } from \"../utils/stream/SSE\";\nimport type { ObjectStreamChunk, ObjectStreamOperation } from \"./types\";\n\nexport class ObjectStreamEncoder extends PipeableTransformStream<\n ObjectStreamChunk,\n Uint8Array\n> {\n constructor() {\n super((readable) =>\n readable\n .pipeThrough(\n (() => {\n class ObjectStreamTransformer implements Transformer<\n ObjectStreamChunk,\n readonly ObjectStreamOperation[]\n > {\n #isFirstChunk = true;\n\n start() {\n // Nothing needed here since we initialize in the field declaration\n }\n\n transform(\n chunk: ObjectStreamChunk,\n controller: TransformStreamDefaultController<\n readonly ObjectStreamOperation[]\n >,\n ) {\n if (\n this.#isFirstChunk &&\n chunk.snapshot &&\n Object.keys(chunk.snapshot).length > 0\n ) {\n // For the first chunk, if there's an initial state that's not empty,\n // prepend a set operation for the initial state\n controller.enqueue([\n { type: \"set\", path: [], value: chunk.snapshot },\n ...chunk.operations,\n ]);\n } else {\n controller.enqueue(chunk.operations);\n }\n this.#isFirstChunk = false;\n }\n }\n return new TransformStream(new ObjectStreamTransformer());\n })(),\n )\n .pipeThrough(new SSEEncoder()),\n );\n }\n}\n\nexport class ObjectStreamDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n ObjectStreamChunk\n> {\n constructor() {\n const accumulator = new ObjectStreamAccumulator();\n super((readable) =>\n readable\n .pipeThrough(new SSEDecoder<readonly ObjectStreamOperation[]>())\n .pipeThrough(\n new TransformStream<\n readonly ObjectStreamOperation[],\n ObjectStreamChunk\n >({\n transform(operations, controller) {\n accumulator.append(operations);\n controller.enqueue({\n snapshot: accumulator.state,\n operations,\n });\n },\n }),\n ),\n );\n }\n}\n\nexport class ObjectStreamResponse extends Response {\n constructor(body: ReadableStream<ObjectStreamChunk>) {\n super(body.pipeThrough(new ObjectStreamEncoder()), {\n headers: new Headers({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Assistant-Stream-Format\": \"object-stream/v0\",\n }),\n });\n }\n}\n\nexport const fromObjectStreamResponse = (\n response: Response,\n): ReadableStream<ObjectStreamChunk> => {\n if (!response.ok)\n throw new Error(`Response failed, status ${response.status}`);\n if (!response.body) throw new Error(\"Response body is null\");\n if (response.headers.get(\"Content-Type\") !== \"text/event-stream\") {\n throw new Error(\"Response is not an event stream\");\n }\n if (response.headers.get(\"Assistant-Stream-Format\") !== \"object-stream/v0\") {\n throw new Error(\"Unsupported Assistant-Stream-Format header\");\n }\n return response.body.pipeThrough(new ObjectStreamDecoder());\n};\n"],"mappings":";;;;AAKA,IAAa,sBAAb,cAAyC,wBAGvC;CACA,cAAc;EACZ,OAAO,aACL,SACG,mBACQ;GACL,MAAM,wBAGJ;IACA,gBAAgB;IAEhB,QAAQ,CAER;IAEA,UACE,OACA,YAGA;KACA,IACE,KAAKA,iBACL,MAAM,YACN,OAAO,KAAK,MAAM,QAAQ,CAAC,CAAC,SAAS,GAIrC,WAAW,QAAQ,CACjB;MAAE,MAAM;MAAO,MAAM,CAAC;MAAG,OAAO,MAAM;KAAS,GAC/C,GAAG,MAAM,UACX,CAAC;UAED,WAAW,QAAQ,MAAM,UAAU;KAErC,KAAKA,gBAAgB;IACvB;GACF;GACA,OAAO,IAAI,gBAAgB,IAAI,wBAAwB,CAAC;EAC1D,EAAA,CAAG,CACL,CAAC,CACA,YAAY,IAAI,WAAW,CAAC,CACjC;CACF;AACF;AAEA,IAAa,sBAAb,cAAyC,wBAGvC;CACA,cAAc;EACZ,MAAM,cAAc,IAAI,wBAAwB;EAChD,OAAO,aACL,SACG,YAAY,IAAI,WAA6C,CAAC,CAAC,CAC/D,YACC,IAAI,gBAGF,EACA,UAAU,YAAY,YAAY;GAChC,YAAY,OAAO,UAAU;GAC7B,WAAW,QAAQ;IACjB,UAAU,YAAY;IACtB;GACF,CAAC;EACH,EACF,CAAC,CACH,CACJ;CACF;AACF;AAEA,IAAa,uBAAb,cAA0C,SAAS;CACjD,YAAY,MAAyC;EACnD,MAAM,KAAK,YAAY,IAAI,oBAAoB,CAAC,GAAG,EACjD,SAAS,IAAI,QAAQ;GACnB,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACZ,2BAA2B;EAC7B,CAAC,EACH,CAAC;CACH;AACF;AAEA,MAAa,4BACX,aACsC;CACtC,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,2BAA2B,SAAS,QAAQ;CAC9D,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,uBAAuB;CAC3D,IAAI,SAAS,QAAQ,IAAI,cAAc,MAAM,qBAC3C,MAAM,IAAI,MAAM,iCAAiC;CAEnD,IAAI,SAAS,QAAQ,IAAI,yBAAyB,MAAM,oBACtD,MAAM,IAAI,MAAM,4CAA4C;CAE9D,OAAO,SAAS,KAAK,YAAY,IAAI,oBAAoB,CAAC;AAC5D"}
@@ -1,19 +0,0 @@
1
- import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
2
- import { ObjectStreamChunk, ObjectStreamOperation } from "./types.js";
3
-
4
- //#region src/core/object/createObjectStream.d.ts
5
- type ObjectStreamController = {
6
- readonly abortSignal: AbortSignal;
7
- enqueue(operations: readonly ObjectStreamOperation[]): void;
8
- };
9
- type CreateObjectStreamOptions = {
10
- execute: (controller: ObjectStreamController) => void | PromiseLike<void>;
11
- defaultValue?: ReadonlyJSONValue;
12
- };
13
- declare const createObjectStream: ({
14
- execute,
15
- defaultValue
16
- }: CreateObjectStreamOptions) => ReadableStream<ObjectStreamChunk>;
17
- //#endregion
18
- export { createObjectStream };
19
- //# sourceMappingURL=createObjectStream.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createObjectStream.d.ts","names":[],"sources":["../../../src/core/object/createObjectStream.ts"],"mappings":";;;;KAKK,sBAAA;EAAA,SACM,WAAA,EAAa,WAAA;EAEtB,OAAA,CAAQ,UAAA,WAAqB,qBAAqB;AAAA;AAAA,KAwD/C,yBAAA;EACH,OAAA,GAAU,UAAA,EAAY,sBAAA,YAAkC,WAAA;EACxD,YAAA,GAAe,iBAAA;AAAA;AAAA,cAGJ,kBAAA;EAAsB,OAAA;EAAA;AAAA,GAGhC,yBAAA,KAAyB,cAAA,CAAA,iBAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"createObjectStream.js","names":[],"sources":["../../../src/core/object/createObjectStream.ts"],"sourcesContent":["import type { ReadonlyJSONValue } from \"../../utils\";\nimport { withPromiseOrValue } from \"../utils/withPromiseOrValue\";\nimport { ObjectStreamAccumulator } from \"./ObjectStreamAccumulator\";\nimport type { ObjectStreamOperation, ObjectStreamChunk } from \"./types\";\n\ntype ObjectStreamController = {\n readonly abortSignal: AbortSignal;\n\n enqueue(operations: readonly ObjectStreamOperation[]): void;\n};\n\nclass ObjectStreamControllerImpl implements ObjectStreamController {\n private _controller: ReadableStreamDefaultController<ObjectStreamChunk>;\n private _abortController = new AbortController();\n private _accumulator: ObjectStreamAccumulator;\n\n get abortSignal() {\n return this._abortController.signal;\n }\n\n constructor(\n controller: ReadableStreamDefaultController<ObjectStreamChunk>,\n defaultValue: ReadonlyJSONValue,\n ) {\n this._controller = controller;\n this._accumulator = new ObjectStreamAccumulator(defaultValue);\n }\n\n enqueue(operations: readonly ObjectStreamOperation[]) {\n this._accumulator.append(operations);\n\n this._controller.enqueue({\n snapshot: this._accumulator.state,\n operations,\n });\n }\n\n __internalError(error: unknown) {\n this._controller.error(error);\n }\n\n __internalClose() {\n this._controller.close();\n }\n\n __internalCancel(reason?: unknown) {\n this._abortController.abort(reason);\n }\n}\n\nconst getStreamControllerPair = (defaultValue: ReadonlyJSONValue) => {\n let controller!: ObjectStreamControllerImpl;\n const stream = new ReadableStream<ObjectStreamChunk>({\n start(c) {\n controller = new ObjectStreamControllerImpl(c, defaultValue);\n },\n cancel(reason: unknown) {\n controller.__internalCancel(reason);\n },\n });\n\n return [stream, controller] as const;\n};\n\ntype CreateObjectStreamOptions = {\n execute: (controller: ObjectStreamController) => void | PromiseLike<void>;\n defaultValue?: ReadonlyJSONValue;\n};\n\nexport const createObjectStream = ({\n execute,\n defaultValue = {},\n}: CreateObjectStreamOptions) => {\n const [stream, controller] = getStreamControllerPair(defaultValue);\n\n withPromiseOrValue(\n () => execute(controller),\n () => {\n controller.__internalClose();\n },\n (e: unknown) => {\n controller.__internalError(e);\n },\n );\n\n return stream;\n};\n"],"mappings":";;;AAWA,IAAM,6BAAN,MAAmE;CACjE;CACA,mBAA2B,IAAI,gBAAgB;CAC/C;CAEA,IAAI,cAAc;EAChB,OAAO,KAAK,iBAAiB;CAC/B;CAEA,YACE,YACA,cACA;EACA,KAAK,cAAc;EACnB,KAAK,eAAe,IAAI,wBAAwB,YAAY;CAC9D;CAEA,QAAQ,YAA8C;EACpD,KAAK,aAAa,OAAO,UAAU;EAEnC,KAAK,YAAY,QAAQ;GACvB,UAAU,KAAK,aAAa;GAC5B;EACF,CAAC;CACH;CAEA,gBAAgB,OAAgB;EAC9B,KAAK,YAAY,MAAM,KAAK;CAC9B;CAEA,kBAAkB;EAChB,KAAK,YAAY,MAAM;CACzB;CAEA,iBAAiB,QAAkB;EACjC,KAAK,iBAAiB,MAAM,MAAM;CACpC;AACF;AAEA,MAAM,2BAA2B,iBAAoC;CACnE,IAAI;CAUJ,OAAO,CAAC,IATW,eAAkC;EACnD,MAAM,GAAG;GACP,aAAa,IAAI,2BAA2B,GAAG,YAAY;EAC7D;EACA,OAAO,QAAiB;GACtB,WAAW,iBAAiB,MAAM;EACpC;CACF,CAEa,GAAG,UAAU;AAC5B;AAOA,MAAa,sBAAsB,EACjC,SACA,eAAe,CAAC,QACe;CAC/B,MAAM,CAAC,QAAQ,cAAc,wBAAwB,YAAY;CAEjE,yBACQ,QAAQ,UAAU,SAClB;EACJ,WAAW,gBAAgB;CAC7B,IACC,MAAe;EACd,WAAW,gBAAgB,CAAC;CAC9B,CACF;CAEA,OAAO;AACT"}
@@ -1,18 +0,0 @@
1
- import { ReadonlyJSONValue } from "../../utils/json/json-value.js";
2
- //#region src/core/object/types.d.ts
3
- type ObjectStreamOperation = {
4
- readonly type: "set";
5
- readonly path: readonly string[];
6
- readonly value: ReadonlyJSONValue;
7
- } | {
8
- readonly type: "append-text";
9
- readonly path: readonly string[];
10
- readonly value: string;
11
- };
12
- type ObjectStreamChunk = {
13
- readonly snapshot: ReadonlyJSONValue;
14
- readonly operations: readonly ObjectStreamOperation[];
15
- };
16
- //#endregion
17
- export { ObjectStreamChunk, ObjectStreamOperation };
18
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/object/types.ts"],"mappings":";;KAEY,qBAAA;EAAA,SAEG,IAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA,EAAO,iBAAiB;AAAA;EAAA,SAGxB,IAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGH,iBAAA;EAAA,SACD,QAAA,EAAU,iBAAA;EAAA,SACV,UAAA,WAAqB,qBAAqB;AAAA"}
@@ -1,9 +0,0 @@
1
- import { ReadonlyJSONArray, ReadonlyJSONObject, ReadonlyJSONValue } from "./json-value.js";
2
-
3
- //#region src/utils/json/is-json.d.ts
4
- declare function isJSONValue(value: unknown): value is ReadonlyJSONValue;
5
- declare function isJSONArray(value: unknown): value is ReadonlyJSONArray;
6
- declare function isJSONObject(value: unknown): value is ReadonlyJSONObject;
7
- //#endregion
8
- export { isJSONArray, isJSONObject, isJSONValue };
9
- //# sourceMappingURL=is-json.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"is-json.d.ts","names":[],"sources":["../../../src/utils/json/is-json.ts"],"mappings":";;;iBAMgB,WAAA,CAAY,KAAA,YAAiB,KAAA,IAAS,iBAAiB;AAAA,iBAuBvD,WAAA,CAAY,KAAA,YAAiB,KAAA,IAAS,iBAAiB;AAAA,iBAIvD,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,kBAAkB"}
@@ -1,17 +0,0 @@
1
- //#region src/utils/json/is-json.ts
2
- function isJSONValue(value) {
3
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
4
- if (Array.isArray(value)) return value.every(isJSONValue);
5
- if (typeof value === "object") return Object.entries(value).every(([key, val]) => typeof key === "string" && isJSONValue(val));
6
- return false;
7
- }
8
- function isJSONArray(value) {
9
- return Array.isArray(value) && value.every(isJSONValue);
10
- }
11
- function isJSONObject(value) {
12
- return value != null && typeof value === "object" && !Array.isArray(value) && Object.entries(value).every(([key, val]) => typeof key === "string" && isJSONValue(val));
13
- }
14
- //#endregion
15
- export { isJSONArray, isJSONObject, isJSONValue };
16
-
17
- //# sourceMappingURL=is-json.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"is-json.js","names":[],"sources":["../../../src/utils/json/is-json.ts"],"sourcesContent":["import type {\n ReadonlyJSONArray,\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"./json-value\";\n\nexport function isJSONValue(value: unknown): value is ReadonlyJSONValue {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return true;\n }\n\n if (Array.isArray(value)) {\n return value.every(isJSONValue);\n }\n\n if (typeof value === \"object\") {\n return Object.entries(value).every(\n ([key, val]) => typeof key === \"string\" && isJSONValue(val),\n );\n }\n\n return false;\n}\n\nexport function isJSONArray(value: unknown): value is ReadonlyJSONArray {\n return Array.isArray(value) && value.every(isJSONValue);\n}\n\nexport function isJSONObject(value: unknown): value is ReadonlyJSONObject {\n return (\n value != null &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n Object.entries(value).every(\n ([key, val]) => typeof key === \"string\" && isJSONValue(val),\n )\n );\n}\n"],"mappings":";AAMA,SAAgB,YAAY,OAA4C;CACtE,IACE,UAAU,QACV,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,WAEjB,OAAO;CAGT,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,MAAM,MAAM,WAAW;CAGhC,IAAI,OAAO,UAAU,UACnB,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,OAC1B,CAAC,KAAK,SAAS,OAAO,QAAQ,YAAY,YAAY,GAAG,CAC5D;CAGF,OAAO;AACT;AAEA,SAAgB,YAAY,OAA4C;CACtE,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,WAAW;AACxD;AAEA,SAAgB,aAAa,OAA6C;CACxE,OACE,SAAS,QACT,OAAO,UAAU,YACjB,CAAC,MAAM,QAAQ,KAAK,KACpB,OAAO,QAAQ,KAAK,CAAC,CAAC,OACnB,CAAC,KAAK,SAAS,OAAO,QAAQ,YAAY,YAAY,GAAG,CAC5D;AAEJ"}