assistant-stream 0.2.6 → 0.2.7

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 (83) hide show
  1. package/dist/core/AssistantStreamChunk.d.ts +5 -1
  2. package/dist/core/AssistantStreamChunk.d.ts.map +1 -1
  3. package/dist/core/accumulators/AssistantMessageStream.d.ts.map +1 -1
  4. package/dist/core/accumulators/AssistantMessageStream.js +1 -0
  5. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  6. package/dist/core/accumulators/assistant-message-accumulator.d.ts.map +1 -1
  7. package/dist/core/accumulators/assistant-message-accumulator.js +19 -1
  8. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  9. package/dist/core/index.d.ts +3 -0
  10. package/dist/core/index.d.ts.map +1 -1
  11. package/dist/core/index.js +9 -1
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/core/modules/tool-call.d.ts.map +1 -1
  14. package/dist/core/modules/tool-call.js +1 -1
  15. package/dist/core/modules/tool-call.js.map +1 -1
  16. package/dist/core/object/ObjectStream.test.d.ts +2 -0
  17. package/dist/core/object/ObjectStream.test.d.ts.map +1 -0
  18. package/dist/core/object/ObjectStreamAccumulator.d.ts +11 -0
  19. package/dist/core/object/ObjectStreamAccumulator.d.ts.map +1 -0
  20. package/dist/core/object/ObjectStreamAccumulator.js +64 -0
  21. package/dist/core/object/ObjectStreamAccumulator.js.map +1 -0
  22. package/dist/core/object/ObjectStreamResponse.d.ts +13 -0
  23. package/dist/core/object/ObjectStreamResponse.d.ts.map +1 -0
  24. package/dist/core/object/ObjectStreamResponse.js +66 -0
  25. package/dist/core/object/ObjectStreamResponse.js.map +1 -0
  26. package/dist/core/object/createObjectStream.d.ts +13 -0
  27. package/dist/core/object/createObjectStream.d.ts.map +1 -0
  28. package/dist/core/object/createObjectStream.js +63 -0
  29. package/dist/core/object/createObjectStream.js.map +1 -0
  30. package/dist/core/object/types.d.ts +15 -0
  31. package/dist/core/object/types.d.ts.map +1 -0
  32. package/dist/core/object/types.js +1 -0
  33. package/dist/core/object/types.js.map +1 -0
  34. package/dist/core/serialization/PlainText.d.ts.map +1 -1
  35. package/dist/core/serialization/PlainText.js.map +1 -1
  36. package/dist/core/serialization/data-stream/DataStream.d.ts.map +1 -1
  37. package/dist/core/serialization/data-stream/DataStream.js +14 -0
  38. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  39. package/dist/core/serialization/data-stream/chunk-types.d.ts +4 -1
  40. package/dist/core/serialization/data-stream/chunk-types.d.ts.map +1 -1
  41. package/dist/core/serialization/data-stream/chunk-types.js +1 -0
  42. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  43. package/dist/core/tool/ToolCallReader.d.ts.map +1 -1
  44. package/dist/core/tool/ToolCallReader.js.map +1 -1
  45. package/dist/core/tool/ToolResponse.d.ts +1 -1
  46. package/dist/core/tool/ToolResponse.d.ts.map +1 -1
  47. package/dist/core/tool/ToolResponse.js +3 -1
  48. package/dist/core/tool/ToolResponse.js.map +1 -1
  49. package/dist/core/tool/toolResultStream.d.ts.map +1 -1
  50. package/dist/core/tool/toolResultStream.js +1 -1
  51. package/dist/core/tool/toolResultStream.js.map +1 -1
  52. package/dist/core/tool/type-path-utils.d.ts.map +1 -1
  53. package/dist/core/utils/stream/SSE.d.ts +10 -0
  54. package/dist/core/utils/stream/SSE.d.ts.map +1 -0
  55. package/dist/core/utils/stream/SSE.js +102 -0
  56. package/dist/core/utils/stream/SSE.js.map +1 -0
  57. package/dist/core/utils/types.d.ts +14 -3
  58. package/dist/core/utils/types.d.ts.map +1 -1
  59. package/dist/utils/index.d.ts +1 -1
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/dist/utils/index.js +3 -1
  62. package/dist/utils/index.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/AssistantStreamChunk.ts +6 -1
  65. package/src/core/accumulators/AssistantMessageStream.ts +1 -0
  66. package/src/core/accumulators/assistant-message-accumulator.ts +25 -1
  67. package/src/core/index.ts +7 -0
  68. package/src/core/modules/tool-call.ts +3 -1
  69. package/src/core/object/ObjectStream.test.ts +376 -0
  70. package/src/core/object/ObjectStreamAccumulator.ts +80 -0
  71. package/src/core/object/ObjectStreamResponse.ts +81 -0
  72. package/src/core/object/createObjectStream.ts +87 -0
  73. package/src/core/object/types.ts +18 -0
  74. package/src/core/serialization/PlainText.ts +2 -1
  75. package/src/core/serialization/data-stream/DataStream.ts +16 -0
  76. package/src/core/serialization/data-stream/chunk-types.ts +6 -0
  77. package/src/core/tool/ToolCallReader.ts +0 -3
  78. package/src/core/tool/ToolResponse.ts +4 -2
  79. package/src/core/tool/toolResultStream.ts +3 -2
  80. package/src/core/tool/type-path-utils.ts +0 -2
  81. package/src/core/utils/stream/SSE.ts +116 -0
  82. package/src/core/utils/types.ts +18 -3
  83. package/src/utils/index.ts +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/utils/types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAErC,KAAK,UAAU,GACX;IACE,IAAI,EAAE,SAAS,CAAC;CACjB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;CAC7D,CAAC;AAMN,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,KAAK,cAAc,GACf;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;CACzB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,kBAAkB,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;CAC7D,CAAC;AAEN,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC1C,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,KAAK,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAC5B,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,UAAU,GACV,QAAQ,CAAC;AAEb,KAAK,yBAAyB,GAAG;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,KAAK,4BAA4B,GAC7B;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IACE,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EACR,MAAM,GACN,QAAQ,GACR,gBAAgB,GAChB,YAAY,GACZ,OAAO,GACP,OAAO,GACP,SAAS,CAAC;IACd,KAAK,CAAC,EAAE,yBAAyB,CAAC;IAClC,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEN,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,SAAS,CAAC;CACjB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,YAAY,CAAC;CACtB,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EACF,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,gBAAgB,GAChB,OAAO,GACP,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAEN,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B;;OAEG;IACH,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,QAAQ,EAAE;QACR,aAAa,EAAE,iBAAiB,EAAE,CAAC;QACnC,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;QAC1C,KAAK,EAAE,4BAA4B,EAAE,CAAC;QACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;CACH,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/utils/types.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- export { asAsyncIterableStream, type AsyncIterableStream } from "./AsyncIterableStream";
1
+ export { asAsyncIterableStream, type AsyncIterableStream, } from "./AsyncIterableStream";
2
2
  export { type ReadonlyJSONObject, type ReadonlyJSONValue } from "./json";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,mBAAmB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC"}
@@ -1,5 +1,7 @@
1
1
  // src/utils/index.ts
2
- import { asAsyncIterableStream } from "./AsyncIterableStream.js";
2
+ import {
3
+ asAsyncIterableStream
4
+ } from "./AsyncIterableStream.js";
3
5
  export {
4
6
  asAsyncIterableStream
5
7
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["export { asAsyncIterableStream, type AsyncIterableStream } from \"./AsyncIterableStream\";\nexport { type ReadonlyJSONObject, type ReadonlyJSONValue } from \"./json\";\n"],"mappings":";AAAA,SAAS,6BAAuD;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["export {\n asAsyncIterableStream,\n type AsyncIterableStream,\n} from \"./AsyncIterableStream\";\nexport { type ReadonlyJSONObject, type ReadonlyJSONValue } from \"./json\";\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAEK;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-stream",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,4 +1,5 @@
1
1
  import { ReadonlyJSONValue } from "../utils/json/json-value";
2
+ import { ObjectStreamOperation } from "./object/types";
2
3
 
3
4
  export type PartInit =
4
5
  | {
@@ -82,7 +83,7 @@ export type AssistantStreamChunk = { readonly path: readonly number[] } & (
82
83
  }
83
84
  | {
84
85
  readonly type: "result";
85
- readonly artifact?: ReadonlyJSONValue | undefined;
86
+ readonly artifact?: ReadonlyJSONValue;
86
87
  readonly result: ReadonlyJSONValue;
87
88
  readonly isError: boolean;
88
89
  }
@@ -90,4 +91,8 @@ export type AssistantStreamChunk = { readonly path: readonly number[] } & (
90
91
  readonly type: "error";
91
92
  readonly error: string;
92
93
  }
94
+ | {
95
+ readonly type: "update-state";
96
+ readonly operations: ObjectStreamOperation[];
97
+ }
93
98
  );
@@ -26,6 +26,7 @@ export class AssistantMessageStream {
26
26
  parts: [],
27
27
  content: [],
28
28
  metadata: {
29
+ unstable_state: null,
29
30
  unstable_data: [],
30
31
  unstable_annotations: [],
31
32
  steps: [],
@@ -11,6 +11,7 @@ import {
11
11
  ReasoningPart,
12
12
  FilePart,
13
13
  } from "../utils/types";
14
+ import { ObjectStreamAccumulator } from "../object/ObjectStreamAccumulator";
14
15
 
15
16
  const createInitialMessage = (): AssistantMessage => ({
16
17
  role: "assistant",
@@ -20,6 +21,7 @@ const createInitialMessage = (): AssistantMessage => ({
20
21
  return this.parts;
21
22
  },
22
23
  metadata: {
24
+ unstable_state: null,
23
25
  unstable_data: [],
24
26
  unstable_annotations: [],
25
27
  steps: [],
@@ -132,6 +134,9 @@ const handleToolCallArgsTextFinish = (
132
134
  if (part.type !== "tool-call") {
133
135
  throw new Error("Last is not a tool call");
134
136
  }
137
+ if (part.state !== "partial-call")
138
+ throw new Error("Last is not a partial call");
139
+
135
140
  return {
136
141
  ...part,
137
142
  state: "call",
@@ -180,7 +185,7 @@ const handleResult = (
180
185
  return {
181
186
  ...part,
182
187
  state: "result",
183
- artifact: chunk.artifact,
188
+ ...(chunk.artifact !== undefined ? { artifact: chunk.artifact } : {}),
184
189
  result: chunk.result,
185
190
  isError: chunk.isError ?? false,
186
191
  status: { type: "complete", reason: "stop" },
@@ -316,6 +321,22 @@ const handleErrorChunk = (
316
321
  };
317
322
  };
318
323
 
324
+ const handleUpdateState = (
325
+ message: AssistantMessage,
326
+ chunk: AssistantStreamChunk & { type: "update-state" },
327
+ ): AssistantMessage => {
328
+ const acc = new ObjectStreamAccumulator(message.metadata.unstable_state);
329
+ acc.append(chunk.operations);
330
+
331
+ return {
332
+ ...message,
333
+ metadata: {
334
+ ...message.metadata,
335
+ unstable_state: acc.state,
336
+ },
337
+ };
338
+ };
339
+
319
340
  export class AssistantMessageAccumulator extends TransformStream<
320
341
  AssistantStreamChunk,
321
342
  AssistantMessage
@@ -366,6 +387,9 @@ export class AssistantMessageAccumulator extends TransformStream<
366
387
  case "error":
367
388
  message = handleErrorChunk(message, chunk);
368
389
  break;
390
+ case "update-state":
391
+ message = handleUpdateState(message, chunk);
392
+ break;
369
393
  default: {
370
394
  const unhandledType: never = type;
371
395
  throw new Error(`Unsupported chunk type: ${unhandledType}`);
package/src/core/index.ts CHANGED
@@ -15,3 +15,10 @@ export { AssistantMessageStream } from "./accumulators/AssistantMessageStream";
15
15
  export type { AssistantMessage } from "./utils/types";
16
16
 
17
17
  export * from "./tool";
18
+
19
+ export { createObjectStream } from "./object/createObjectStream";
20
+ export {
21
+ ObjectStreamResponse,
22
+ fromObjectStreamResponse,
23
+ } from "./object/ObjectStreamResponse";
24
+ export type { ObjectStreamChunk } from "./object/types";
@@ -57,7 +57,9 @@ class ToolCallStreamControllerImpl implements ToolCallStreamController {
57
57
  this._controller.enqueue({
58
58
  type: "result",
59
59
  path: [],
60
- artifact: response.artifact,
60
+ ...(response.artifact !== undefined
61
+ ? { artifact: response.artifact }
62
+ : {}),
61
63
  result: response.result,
62
64
  isError: response.isError ?? false,
63
65
  });
@@ -0,0 +1,376 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createObjectStream } from "./createObjectStream";
3
+ import {
4
+ ObjectStreamEncoder,
5
+ ObjectStreamDecoder,
6
+ } from "./ObjectStreamResponse";
7
+ import { ReadonlyJSONValue } from "../../utils";
8
+ import { ObjectStreamChunk } from "./types";
9
+
10
+ // Helper function to collect all chunks from a stream
11
+ async function collectChunks<T>(stream: ReadableStream<T>): Promise<T[]> {
12
+ const reader = stream.getReader();
13
+ const chunks: T[] = [];
14
+
15
+ try {
16
+ while (true) {
17
+ const { done, value } = await reader.read();
18
+ if (done) break;
19
+ chunks.push(value);
20
+ }
21
+ } finally {
22
+ reader.releaseLock();
23
+ }
24
+
25
+ return chunks;
26
+ }
27
+
28
+ // Helper function to encode and decode a stream
29
+ async function encodeAndDecode(
30
+ stream: ReadableStream<ObjectStreamChunk>,
31
+ ): Promise<ReadableStream<ObjectStreamChunk>> {
32
+ // Encode the stream to Uint8Array (simulating network transmission)
33
+ const encodedStream = stream.pipeThrough(new ObjectStreamEncoder());
34
+
35
+ // Collect all encoded chunks
36
+ const encodedChunks = await collectChunks(encodedStream);
37
+
38
+ // Create a new stream from the encoded chunks
39
+ const reconstructedStream = new ReadableStream<Uint8Array>({
40
+ start(controller) {
41
+ for (const chunk of encodedChunks) {
42
+ controller.enqueue(chunk);
43
+ }
44
+ controller.close();
45
+ },
46
+ });
47
+
48
+ // Decode the stream back to ObjectStreamChunk
49
+ return reconstructedStream.pipeThrough(new ObjectStreamDecoder());
50
+ }
51
+
52
+ describe("ObjectStream serialization and deserialization", () => {
53
+ it("should correctly serialize and deserialize simple objects", async () => {
54
+ // Create an object stream with simple operations
55
+ const stream = createObjectStream({
56
+ execute: (controller) => {
57
+ controller.enqueue([
58
+ { type: "set", path: ["name"], value: "John" },
59
+ { type: "set", path: ["age"], value: 30 },
60
+ ]);
61
+ },
62
+ });
63
+
64
+ // Encode and decode the stream
65
+ const decodedStream = await encodeAndDecode(stream);
66
+
67
+ // Collect all chunks from the decoded stream
68
+ const chunks = await collectChunks(decodedStream);
69
+
70
+ // Verify the final state
71
+ const finalChunk = chunks[chunks.length - 1]!;
72
+ expect(finalChunk.snapshot).toEqual({
73
+ name: "John",
74
+ age: 30,
75
+ });
76
+ });
77
+
78
+ it("should correctly handle nested objects", async () => {
79
+ const stream = createObjectStream({
80
+ execute: (controller) => {
81
+ controller.enqueue([
82
+ { type: "set", path: ["user", "profile", "name"], value: "Jane" },
83
+ {
84
+ type: "set",
85
+ path: ["user", "profile", "email"],
86
+ value: "jane@example.com",
87
+ },
88
+ { type: "set", path: ["user", "settings", "theme"], value: "dark" },
89
+ ]);
90
+ },
91
+ });
92
+
93
+ const decodedStream = await encodeAndDecode(stream);
94
+ const chunks = await collectChunks(decodedStream);
95
+ const finalChunk = chunks[chunks.length - 1]!;
96
+
97
+ expect(finalChunk.snapshot).toEqual({
98
+ user: {
99
+ profile: {
100
+ name: "Jane",
101
+ email: "jane@example.com",
102
+ },
103
+ settings: {
104
+ theme: "dark",
105
+ },
106
+ },
107
+ });
108
+ });
109
+
110
+ it("should correctly handle arrays", async () => {
111
+ const stream = createObjectStream({
112
+ execute: (controller) => {
113
+ controller.enqueue([
114
+ { type: "set", path: ["items"], value: [] },
115
+ { type: "set", path: ["items", "0"], value: "apple" },
116
+ { type: "set", path: ["items", "1"], value: "banana" },
117
+ { type: "set", path: ["items", "2"], value: "cherry" },
118
+ ]);
119
+ },
120
+ });
121
+
122
+ const decodedStream = await encodeAndDecode(stream);
123
+ const chunks = await collectChunks(decodedStream);
124
+ const finalChunk = chunks[chunks.length - 1]!;
125
+
126
+ expect(finalChunk.snapshot).toEqual({
127
+ items: ["apple", "banana", "cherry"],
128
+ });
129
+ });
130
+
131
+ it("should correctly handle mixed arrays and objects", async () => {
132
+ const stream = createObjectStream({
133
+ execute: (controller) => {
134
+ controller.enqueue([
135
+ { type: "set", path: ["users"], value: [] },
136
+ { type: "set", path: ["users", "0"], value: {} },
137
+ { type: "set", path: ["users", "0", "id"], value: 1 },
138
+ { type: "set", path: ["users", "0", "name"], value: "Alice" },
139
+ { type: "set", path: ["users", "1"], value: {} },
140
+ { type: "set", path: ["users", "1", "id"], value: 2 },
141
+ { type: "set", path: ["users", "1", "name"], value: "Bob" },
142
+ ]);
143
+ },
144
+ });
145
+
146
+ const decodedStream = await encodeAndDecode(stream);
147
+ const chunks = await collectChunks(decodedStream);
148
+ const finalChunk = chunks[chunks.length - 1]!;
149
+
150
+ expect(finalChunk.snapshot).toEqual({
151
+ users: [
152
+ { id: 1, name: "Alice" },
153
+ { id: 2, name: "Bob" },
154
+ ],
155
+ });
156
+ });
157
+
158
+ it("should correctly handle append-text operations", async () => {
159
+ const stream = createObjectStream({
160
+ execute: (controller) => {
161
+ controller.enqueue([
162
+ { type: "set", path: ["message"], value: "Hello" },
163
+ { type: "append-text", path: ["message"], value: " " },
164
+ { type: "append-text", path: ["message"], value: "World" },
165
+ { type: "append-text", path: ["message"], value: "!" },
166
+ ]);
167
+ },
168
+ });
169
+
170
+ const decodedStream = await encodeAndDecode(stream);
171
+ const chunks = await collectChunks(decodedStream);
172
+ const finalChunk = chunks[chunks.length - 1]!;
173
+
174
+ expect(finalChunk.snapshot).toEqual({
175
+ message: "Hello World!",
176
+ });
177
+ });
178
+
179
+ it("should correctly handle special characters and Unicode", async () => {
180
+ const stream = createObjectStream({
181
+ execute: (controller) => {
182
+ controller.enqueue([
183
+ {
184
+ type: "set",
185
+ path: ["special"],
186
+ value: "Special chars: !@#$%^&*()",
187
+ },
188
+ { type: "set", path: ["unicode"], value: "Unicode: 😀🌍🚀" },
189
+ { type: "set", path: ["quotes"], value: "Quotes: \"'`" },
190
+ {
191
+ type: "set",
192
+ path: ["newlines"],
193
+ value: "Line 1\nLine 2\r\nLine 3",
194
+ },
195
+ ]);
196
+ },
197
+ });
198
+
199
+ const decodedStream = await encodeAndDecode(stream);
200
+ const chunks = await collectChunks(decodedStream);
201
+ const finalChunk = chunks[chunks.length - 1]!;
202
+
203
+ expect(finalChunk.snapshot).toEqual({
204
+ special: "Special chars: !@#$%^&*()",
205
+ unicode: "Unicode: 😀🌍🚀",
206
+ quotes: "Quotes: \"'`",
207
+ newlines: "Line 1\nLine 2\r\nLine 3",
208
+ });
209
+ });
210
+
211
+ it("should correctly handle null and undefined values", async () => {
212
+ const stream = createObjectStream({
213
+ execute: (controller) => {
214
+ controller.enqueue([
215
+ { type: "set", path: ["nullValue"], value: null },
216
+ { type: "set", path: ["emptyObject"], value: {} },
217
+ { type: "set", path: ["emptyArray"], value: [] },
218
+ ]);
219
+ },
220
+ });
221
+
222
+ const decodedStream = await encodeAndDecode(stream);
223
+ const chunks = await collectChunks(decodedStream);
224
+ const finalChunk = chunks[chunks.length - 1]!;
225
+
226
+ expect(finalChunk.snapshot).toEqual({
227
+ nullValue: null,
228
+ emptyObject: {},
229
+ emptyArray: [],
230
+ });
231
+ });
232
+
233
+ it("should correctly handle large nested structures", async () => {
234
+ // Create a deep nested structure
235
+ const stream = createObjectStream({
236
+ execute: (controller) => {
237
+ controller.enqueue([
238
+ { type: "set", path: ["level1"], value: {} },
239
+ { type: "set", path: ["level1", "level2"], value: {} },
240
+ { type: "set", path: ["level1", "level2", "level3"], value: {} },
241
+ {
242
+ type: "set",
243
+ path: ["level1", "level2", "level3", "level4"],
244
+ value: {},
245
+ },
246
+ {
247
+ type: "set",
248
+ path: ["level1", "level2", "level3", "level4", "level5"],
249
+ value: "deep value",
250
+ },
251
+ ]);
252
+ },
253
+ });
254
+
255
+ const decodedStream = await encodeAndDecode(stream);
256
+ const chunks = await collectChunks(decodedStream);
257
+ const finalChunk = chunks[chunks.length - 1]!;
258
+
259
+ expect(finalChunk.snapshot).toEqual({
260
+ level1: {
261
+ level2: {
262
+ level3: {
263
+ level4: {
264
+ level5: "deep value",
265
+ },
266
+ },
267
+ },
268
+ },
269
+ });
270
+ });
271
+
272
+ it("should correctly handle operations in multiple enqueue calls", async () => {
273
+ const stream = createObjectStream({
274
+ execute: (controller) => {
275
+ // First batch of operations
276
+ controller.enqueue([
277
+ { type: "set", path: ["user"], value: { name: "Initial" } },
278
+ ]);
279
+
280
+ // Second batch of operations
281
+ controller.enqueue([
282
+ { type: "set", path: ["user", "name"], value: "Updated" },
283
+ { type: "set", path: ["user", "email"], value: "user@example.com" },
284
+ ]);
285
+
286
+ // Third batch of operations
287
+ controller.enqueue([
288
+ { type: "set", path: ["status"], value: "complete" },
289
+ ]);
290
+ },
291
+ });
292
+
293
+ const decodedStream = await encodeAndDecode(stream);
294
+ const chunks = await collectChunks(decodedStream);
295
+ const finalChunk = chunks[chunks.length - 1]!;
296
+
297
+ expect(finalChunk.snapshot).toEqual({
298
+ user: {
299
+ name: "Updated",
300
+ email: "user@example.com",
301
+ },
302
+ status: "complete",
303
+ });
304
+
305
+ // Verify that we got the correct number of chunks
306
+ expect(chunks.length).toBe(3);
307
+
308
+ // Verify intermediate states
309
+ expect(chunks[0]!.snapshot).toEqual({
310
+ user: { name: "Initial" },
311
+ });
312
+
313
+ expect(chunks[1]!.snapshot).toEqual({
314
+ user: {
315
+ name: "Updated",
316
+ email: "user@example.com",
317
+ },
318
+ });
319
+ });
320
+
321
+ it("should correctly handle overwriting existing values", async () => {
322
+ const stream = createObjectStream({
323
+ execute: (controller) => {
324
+ controller.enqueue([
325
+ { type: "set", path: ["value"], value: "initial" },
326
+ { type: "set", path: ["nested"], value: { prop: "initial" } },
327
+ ]);
328
+
329
+ controller.enqueue([
330
+ { type: "set", path: ["value"], value: "updated" },
331
+ { type: "set", path: ["nested"], value: "completely replaced" },
332
+ ]);
333
+ },
334
+ });
335
+
336
+ const decodedStream = await encodeAndDecode(stream);
337
+ const chunks = await collectChunks(decodedStream);
338
+ const finalChunk = chunks[chunks.length - 1]!;
339
+
340
+ expect(finalChunk.snapshot).toEqual({
341
+ value: "updated",
342
+ nested: "completely replaced",
343
+ });
344
+ });
345
+
346
+ it("should correctly handle custom initial values", async () => {
347
+ const initialValue: ReadonlyJSONValue = {
348
+ existing: "value",
349
+ nested: {
350
+ prop: 123,
351
+ },
352
+ };
353
+
354
+ const stream = createObjectStream({
355
+ defaultValue: initialValue,
356
+ execute: (controller) => {
357
+ controller.enqueue([
358
+ { type: "set", path: ["new"], value: "added" },
359
+ { type: "set", path: ["nested", "prop"], value: 456 },
360
+ ]);
361
+ },
362
+ });
363
+
364
+ const decodedStream = await encodeAndDecode(stream);
365
+ const chunks = await collectChunks(decodedStream);
366
+ const finalChunk = chunks[chunks.length - 1]!;
367
+
368
+ expect(finalChunk.snapshot).toEqual({
369
+ existing: "value",
370
+ nested: {
371
+ prop: 456,
372
+ },
373
+ new: "added",
374
+ });
375
+ });
376
+ });
@@ -0,0 +1,80 @@
1
+ import { ReadonlyJSONValue, ReadonlyJSONObject } from "../../utils";
2
+ import { ObjectStreamOperation } from "./types";
3
+
4
+ export class ObjectStreamAccumulator {
5
+ private _state: ReadonlyJSONValue;
6
+
7
+ constructor(initialValue: ReadonlyJSONValue = null) {
8
+ this._state = initialValue;
9
+ }
10
+
11
+ get state() {
12
+ return this._state;
13
+ }
14
+
15
+ append(ops: readonly ObjectStreamOperation[]) {
16
+ this._state = ops.reduce(
17
+ (state, op) => ObjectStreamAccumulator.apply(state, op),
18
+ this._state,
19
+ );
20
+ }
21
+
22
+ private static apply(state: ReadonlyJSONValue, op: ObjectStreamOperation) {
23
+ const type = op.type;
24
+ switch (type) {
25
+ case "set":
26
+ return ObjectStreamAccumulator.updatePath(
27
+ state,
28
+ op.path,
29
+ () => op.value,
30
+ );
31
+ case "append-text":
32
+ return ObjectStreamAccumulator.updatePath(state, op.path, (current) => {
33
+ if (typeof current !== "string")
34
+ throw new Error(`Expected string at path [${op.path.join(", ")}]`);
35
+ return current + op.value;
36
+ });
37
+
38
+ default: {
39
+ const _exhaustiveCheck: never = type;
40
+ throw new Error(`Invalid operation type: ${_exhaustiveCheck}`);
41
+ }
42
+ }
43
+ }
44
+
45
+ private static updatePath(
46
+ state: ReadonlyJSONValue | undefined,
47
+ path: readonly string[],
48
+ updater: (current: ReadonlyJSONValue | undefined) => ReadonlyJSONValue,
49
+ ): ReadonlyJSONValue {
50
+ if (path.length === 0) return updater(state);
51
+
52
+ // Initialize state as empty object if it's null and we're trying to set a property
53
+ if (state === null) {
54
+ state = {};
55
+ }
56
+
57
+ if (typeof state !== "object") {
58
+ throw new Error(`Invalid path: [${path.join(", ")}]`);
59
+ }
60
+
61
+ const [key, ...rest] = path as [string, ...(readonly string[])];
62
+ if (Array.isArray(state)) {
63
+ const idx = Number(key);
64
+ if (isNaN(idx))
65
+ throw new Error(`Expected array index at [${path.join(", ")}]`);
66
+ if (idx > state.length || idx < 0)
67
+ throw new Error(`Insert array index out of bounds`);
68
+
69
+ const nextState = [...state];
70
+ nextState[idx] = this.updatePath(nextState[idx], rest, updater);
71
+
72
+ return nextState;
73
+ }
74
+
75
+ const nextState = { ...(state as ReadonlyJSONObject) };
76
+ nextState[key] = this.updatePath(nextState[key], rest, updater);
77
+
78
+ return nextState;
79
+ }
80
+ }