assistant-stream 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/ai-sdk/package.json +2 -2
  2. package/dist/ai-sdk/index.js +7 -34
  3. package/dist/ai-sdk/index.js.map +1 -1
  4. package/dist/ai-sdk/language-model.js +5 -31
  5. package/dist/ai-sdk/language-model.js.map +1 -1
  6. package/dist/ai-sdk.js +4 -32
  7. package/dist/ai-sdk.js.map +1 -1
  8. package/dist/core/AssistantStream.js +3 -29
  9. package/dist/core/AssistantStream.js.map +1 -1
  10. package/dist/core/AssistantStreamChunk.js +0 -18
  11. package/dist/core/AssistantStreamChunk.js.map +1 -1
  12. package/dist/core/accumulators/AssistantMessageStream.js +9 -35
  13. package/dist/core/accumulators/AssistantMessageStream.js.map +1 -1
  14. package/dist/core/accumulators/assistant-message-accumulator.js +22 -48
  15. package/dist/core/accumulators/assistant-message-accumulator.js.map +1 -1
  16. package/dist/core/index.js +16 -46
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/modules/assistant-stream.js +29 -52
  19. package/dist/core/modules/assistant-stream.js.map +1 -1
  20. package/dist/core/modules/text.js +6 -33
  21. package/dist/core/modules/text.js.map +1 -1
  22. package/dist/core/modules/tool-call.js +8 -35
  23. package/dist/core/modules/tool-call.js.map +1 -1
  24. package/dist/core/serialization/PlainText.js +9 -36
  25. package/dist/core/serialization/PlainText.js.map +1 -1
  26. package/dist/core/serialization/data-stream/DataStream.js +57 -79
  27. package/dist/core/serialization/data-stream/DataStream.js.map +1 -1
  28. package/dist/core/serialization/data-stream/chunk-types.js +2 -28
  29. package/dist/core/serialization/data-stream/chunk-types.js.map +1 -1
  30. package/dist/core/serialization/data-stream/serialization.js +6 -33
  31. package/dist/core/serialization/data-stream/serialization.js.map +1 -1
  32. package/dist/core/tool/ToolCallReader.js +30 -55
  33. package/dist/core/tool/ToolCallReader.js.map +1 -1
  34. package/dist/core/tool/ToolExecutionStream.js +18 -52
  35. package/dist/core/tool/ToolExecutionStream.js.map +1 -1
  36. package/dist/core/tool/ToolResponse.js +5 -31
  37. package/dist/core/tool/ToolResponse.js.map +1 -1
  38. package/dist/core/tool/index.js +9 -35
  39. package/dist/core/tool/index.js.map +1 -1
  40. package/dist/core/tool/tool-types.js +0 -18
  41. package/dist/core/tool/tool-types.js.map +1 -1
  42. package/dist/core/tool/toolResultStream.js +8 -35
  43. package/dist/core/tool/toolResultStream.js.map +1 -1
  44. package/dist/core/tool/type-path-utils.js +0 -18
  45. package/dist/core/tool/type-path-utils.js.map +1 -1
  46. package/dist/core/utils/Counter.js +4 -30
  47. package/dist/core/utils/Counter.js.map +1 -1
  48. package/dist/core/utils/generateId.js +4 -30
  49. package/dist/core/utils/generateId.js.map +1 -1
  50. package/dist/core/utils/stream/AssistantMetaTransformStream.js +4 -30
  51. package/dist/core/utils/stream/AssistantMetaTransformStream.js.map +1 -1
  52. package/dist/core/utils/stream/AssistantTransformStream.js +8 -32
  53. package/dist/core/utils/stream/AssistantTransformStream.js.map +1 -1
  54. package/dist/core/utils/stream/LineDecoderStream.js +4 -30
  55. package/dist/core/utils/stream/LineDecoderStream.js.map +1 -1
  56. package/dist/core/utils/stream/PipeableTransformStream.js +4 -30
  57. package/dist/core/utils/stream/PipeableTransformStream.js.map +1 -1
  58. package/dist/core/utils/stream/UnderlyingReadable.js +0 -18
  59. package/dist/core/utils/stream/UnderlyingReadable.js.map +1 -1
  60. package/dist/core/utils/stream/merge.js +5 -31
  61. package/dist/core/utils/stream/merge.js.map +1 -1
  62. package/dist/core/utils/stream/path-utils.js +10 -38
  63. package/dist/core/utils/stream/path-utils.js.map +1 -1
  64. package/dist/core/utils/types.js +0 -18
  65. package/dist/core/utils/types.js.map +1 -1
  66. package/dist/core/utils/withPromiseOrValue.js +2 -28
  67. package/dist/core/utils/withPromiseOrValue.js.map +1 -1
  68. package/dist/index.js +1 -24
  69. package/dist/index.js.map +1 -1
  70. package/dist/utils/AsyncIterableStream.js +2 -28
  71. package/dist/utils/AsyncIterableStream.js.map +1 -1
  72. package/dist/utils/json/fix-json.js +2 -28
  73. package/dist/utils/json/fix-json.js.map +1 -1
  74. package/dist/utils/json/is-json.js +2 -30
  75. package/dist/utils/json/is-json.js.map +1 -1
  76. package/dist/utils/json/json-value.js +0 -18
  77. package/dist/utils/json/json-value.js.map +1 -1
  78. package/dist/utils/json/parse-partial-json-object.js +12 -50
  79. package/dist/utils/json/parse-partial-json-object.js.map +1 -1
  80. package/dist/utils/promiseWithResolvers.js +3 -29
  81. package/dist/utils/promiseWithResolvers.js.map +1 -1
  82. package/dist/utils.js +9 -32
  83. package/dist/utils.js.map +1 -1
  84. package/package.json +7 -9
  85. package/{dist/ai-sdk/index.mjs → src/ai-sdk/index.ts} +64 -33
  86. package/{dist/ai-sdk/language-model.mjs → src/ai-sdk/language-model.ts} +35 -19
  87. package/src/ai-sdk.ts +2 -0
  88. package/src/core/AssistantStream.ts +39 -0
  89. package/src/core/AssistantStreamChunk.ts +93 -0
  90. package/src/core/accumulators/AssistantMessageStream.ts +56 -0
  91. package/{dist/core/accumulators/assistant-message-accumulator.mjs → src/core/accumulators/assistant-message-accumulator.ts} +152 -67
  92. package/src/core/index.ts +17 -0
  93. package/{dist/core/modules/assistant-stream.mjs → src/core/modules/assistant-stream.ts} +122 -60
  94. package/src/core/modules/text.ts +64 -0
  95. package/{dist/core/modules/tool-call.mjs → src/core/modules/tool-call.ts} +48 -27
  96. package/src/core/serialization/PlainText.ts +68 -0
  97. package/{dist/core/serialization/data-stream/DataStream.mjs → src/core/serialization/data-stream/DataStream.ts} +111 -67
  98. package/src/core/serialization/data-stream/chunk-types.ts +93 -0
  99. package/src/core/serialization/data-stream/serialization.ts +32 -0
  100. package/src/core/tool/ToolCallReader.ts +432 -0
  101. package/{dist/core/tool/ToolExecutionStream.mjs → src/core/tool/ToolExecutionStream.ts} +83 -35
  102. package/src/core/tool/ToolResponse.ts +31 -0
  103. package/src/core/tool/index.ts +8 -0
  104. package/src/core/tool/tool-types.ts +107 -0
  105. package/src/core/tool/toolResultStream.ts +119 -0
  106. package/src/core/tool/type-path-utils.ts +36 -0
  107. package/src/core/utils/Counter.ts +7 -0
  108. package/src/core/utils/generateId.tsx +6 -0
  109. package/src/core/utils/stream/AssistantMetaTransformStream.ts +74 -0
  110. package/src/core/utils/stream/AssistantTransformStream.ts +74 -0
  111. package/{dist/core/utils/stream/LineDecoderStream.mjs → src/core/utils/stream/LineDecoderStream.ts} +12 -10
  112. package/src/core/utils/stream/PipeableTransformStream.ts +10 -0
  113. package/src/core/utils/stream/UnderlyingReadable.ts +5 -0
  114. package/src/core/utils/stream/merge.ts +212 -0
  115. package/src/core/utils/stream/path-utils.ts +71 -0
  116. package/src/core/utils/types.ts +150 -0
  117. package/src/core/utils/withPromiseOrValue.ts +20 -0
  118. package/src/index.ts +1 -0
  119. package/src/utils/AsyncIterableStream.ts +24 -0
  120. package/{dist/utils/json/fix-json.mjs → src/utils/json/fix-json.ts} +146 -20
  121. package/src/utils/json/is-json.ts +43 -0
  122. package/src/utils/json/json-value.ts +13 -0
  123. package/src/utils/json/parse-partial-json-object.test.ts +225 -0
  124. package/src/utils/json/parse-partial-json-object.ts +103 -0
  125. package/src/utils/promiseWithResolvers.ts +10 -0
  126. package/src/utils.ts +13 -0
  127. package/dist/ai-sdk/index.mjs.map +0 -1
  128. package/dist/ai-sdk/language-model.mjs.map +0 -1
  129. package/dist/ai-sdk.mjs +0 -7
  130. package/dist/ai-sdk.mjs.map +0 -1
  131. package/dist/core/AssistantStream.mjs +0 -21
  132. package/dist/core/AssistantStream.mjs.map +0 -1
  133. package/dist/core/AssistantStreamChunk.mjs +0 -1
  134. package/dist/core/AssistantStreamChunk.mjs.map +0 -1
  135. package/dist/core/accumulators/AssistantMessageStream.mjs +0 -54
  136. package/dist/core/accumulators/AssistantMessageStream.mjs.map +0 -1
  137. package/dist/core/accumulators/assistant-message-accumulator.mjs.map +0 -1
  138. package/dist/core/index.mjs +0 -26
  139. package/dist/core/index.mjs.map +0 -1
  140. package/dist/core/modules/assistant-stream.mjs.map +0 -1
  141. package/dist/core/modules/text.mjs +0 -52
  142. package/dist/core/modules/text.mjs.map +0 -1
  143. package/dist/core/modules/tool-call.mjs.map +0 -1
  144. package/dist/core/serialization/PlainText.mjs +0 -44
  145. package/dist/core/serialization/PlainText.mjs.map +0 -1
  146. package/dist/core/serialization/data-stream/DataStream.mjs.map +0 -1
  147. package/dist/core/serialization/data-stream/chunk-types.mjs +0 -24
  148. package/dist/core/serialization/data-stream/chunk-types.mjs.map +0 -1
  149. package/dist/core/serialization/data-stream/serialization.mjs +0 -30
  150. package/dist/core/serialization/data-stream/serialization.mjs.map +0 -1
  151. package/dist/core/tool/ToolCallReader.mjs +0 -315
  152. package/dist/core/tool/ToolCallReader.mjs.map +0 -1
  153. package/dist/core/tool/ToolExecutionStream.mjs.map +0 -1
  154. package/dist/core/tool/ToolResponse.mjs +0 -22
  155. package/dist/core/tool/ToolResponse.mjs.map +0 -1
  156. package/dist/core/tool/index.mjs +0 -14
  157. package/dist/core/tool/index.mjs.map +0 -1
  158. package/dist/core/tool/tool-types.mjs +0 -1
  159. package/dist/core/tool/tool-types.mjs.map +0 -1
  160. package/dist/core/tool/toolResultStream.mjs +0 -78
  161. package/dist/core/tool/toolResultStream.mjs.map +0 -1
  162. package/dist/core/tool/type-path-utils.mjs +0 -1
  163. package/dist/core/tool/type-path-utils.mjs.map +0 -1
  164. package/dist/core/utils/Counter.mjs +0 -11
  165. package/dist/core/utils/Counter.mjs.map +0 -1
  166. package/dist/core/utils/generateId.mjs +0 -10
  167. package/dist/core/utils/generateId.mjs.map +0 -1
  168. package/dist/core/utils/stream/AssistantMetaTransformStream.mjs +0 -44
  169. package/dist/core/utils/stream/AssistantMetaTransformStream.mjs.map +0 -1
  170. package/dist/core/utils/stream/AssistantTransformStream.mjs +0 -46
  171. package/dist/core/utils/stream/AssistantTransformStream.mjs.map +0 -1
  172. package/dist/core/utils/stream/LineDecoderStream.mjs.map +0 -1
  173. package/dist/core/utils/stream/PipeableTransformStream.mjs +0 -15
  174. package/dist/core/utils/stream/PipeableTransformStream.mjs.map +0 -1
  175. package/dist/core/utils/stream/UnderlyingReadable.mjs +0 -1
  176. package/dist/core/utils/stream/UnderlyingReadable.mjs.map +0 -1
  177. package/dist/core/utils/stream/merge.mjs +0 -85
  178. package/dist/core/utils/stream/merge.mjs.map +0 -1
  179. package/dist/core/utils/stream/path-utils.mjs +0 -61
  180. package/dist/core/utils/stream/path-utils.mjs.map +0 -1
  181. package/dist/core/utils/types.mjs +0 -1
  182. package/dist/core/utils/types.mjs.map +0 -1
  183. package/dist/core/utils/withPromiseOrValue.mjs +0 -17
  184. package/dist/core/utils/withPromiseOrValue.mjs.map +0 -1
  185. package/dist/index.mjs +0 -3
  186. package/dist/index.mjs.map +0 -1
  187. package/dist/utils/AsyncIterableStream.mjs +0 -21
  188. package/dist/utils/AsyncIterableStream.mjs.map +0 -1
  189. package/dist/utils/json/fix-json.mjs.map +0 -1
  190. package/dist/utils/json/is-json.mjs +0 -29
  191. package/dist/utils/json/is-json.mjs.map +0 -1
  192. package/dist/utils/json/json-value.mjs +0 -1
  193. package/dist/utils/json/json-value.mjs.map +0 -1
  194. package/dist/utils/json/parse-partial-json-object.mjs +0 -65
  195. package/dist/utils/json/parse-partial-json-object.mjs.map +0 -1
  196. package/dist/utils/promiseWithResolvers.mjs +0 -15
  197. package/dist/utils/promiseWithResolvers.mjs.map +0 -1
  198. package/dist/utils.mjs +0 -14
  199. package/dist/utils.mjs.map +0 -1
  200. package/utils/README.md +0 -1
  201. package/utils/package.json +0 -5
@@ -0,0 +1,71 @@
1
+ import { AssistantStreamChunk } from "../../AssistantStreamChunk";
2
+ import { Counter } from "../Counter";
3
+
4
+ export class PathAppendEncoder extends TransformStream<
5
+ AssistantStreamChunk,
6
+ AssistantStreamChunk
7
+ > {
8
+ constructor(idx: number) {
9
+ super({
10
+ transform(chunk, controller) {
11
+ controller.enqueue({
12
+ ...chunk,
13
+ path: [idx, ...chunk.path],
14
+ });
15
+ },
16
+ });
17
+ }
18
+ }
19
+
20
+ export class PathAppendDecoder extends TransformStream<
21
+ AssistantStreamChunk,
22
+ AssistantStreamChunk
23
+ > {
24
+ constructor(idx: number) {
25
+ super({
26
+ transform(chunk, controller) {
27
+ const {
28
+ path: [idx2, ...path],
29
+ } = chunk;
30
+
31
+ if (idx !== idx2)
32
+ throw new Error(`Path mismatch: expected ${idx}, got ${idx2}`);
33
+
34
+ controller.enqueue({
35
+ ...chunk,
36
+ path,
37
+ });
38
+ },
39
+ });
40
+ }
41
+ }
42
+
43
+ export class PathMergeEncoder extends TransformStream<
44
+ AssistantStreamChunk,
45
+ AssistantStreamChunk
46
+ > {
47
+ constructor(counter: Counter) {
48
+ const innerCounter = new Counter();
49
+ const mapping = new Map<number, number>();
50
+ super({
51
+ transform(chunk, controller) {
52
+ if (chunk.type === "part-start" && chunk.path.length === 0) {
53
+ mapping.set(innerCounter.up(), counter.up());
54
+ }
55
+
56
+ const [idx, ...path] = chunk.path;
57
+ if (idx === undefined) {
58
+ controller.enqueue(chunk);
59
+ return;
60
+ }
61
+ const mappedIdx = mapping.get(idx);
62
+ if (mappedIdx === undefined) throw new Error("Path not found");
63
+
64
+ controller.enqueue({
65
+ ...chunk,
66
+ path: [mappedIdx, ...path],
67
+ });
68
+ },
69
+ });
70
+ }
71
+ }
@@ -0,0 +1,150 @@
1
+ import {
2
+ ReadonlyJSONObject,
3
+ ReadonlyJSONValue,
4
+ } from "../../utils/json/json-value";
5
+
6
+ type TextStatus =
7
+ | {
8
+ type: "running";
9
+ }
10
+ | {
11
+ type: "complete";
12
+ reason: "stop" | "unknown";
13
+ }
14
+ | {
15
+ type: "incomplete";
16
+ reason: "cancelled" | "length" | "content-filter" | "other";
17
+ };
18
+
19
+ // export type StepStartPart = {
20
+ // type: "step-start";
21
+ // };
22
+
23
+ export type TextPart = {
24
+ type: "text";
25
+ text: string;
26
+ status: TextStatus;
27
+ };
28
+
29
+ export type ReasoningPart = {
30
+ type: "reasoning";
31
+ text: string;
32
+ status: TextStatus;
33
+ };
34
+
35
+ type ToolCallStatus =
36
+ | {
37
+ type: "running";
38
+ isArgsComplete: boolean;
39
+ }
40
+ | {
41
+ type: "requires-action";
42
+ reason: "tool-call-result";
43
+ }
44
+ | {
45
+ type: "complete";
46
+ reason: "stop" | "unknown";
47
+ }
48
+ | {
49
+ type: "incomplete";
50
+ reason: "cancelled" | "length" | "content-filter" | "other";
51
+ };
52
+
53
+ export type ToolCallPart = {
54
+ type: "tool-call";
55
+ state: "partial-call" | "call" | "result";
56
+ status: ToolCallStatus;
57
+ toolCallId: string;
58
+ toolName: string;
59
+ argsText: string;
60
+ args: ReadonlyJSONObject;
61
+ artifact?: unknown;
62
+ result?: ReadonlyJSONValue;
63
+ isError?: boolean;
64
+ };
65
+
66
+ export type SourcePart = {
67
+ type: "source";
68
+ sourceType: "url";
69
+ id: string;
70
+ url: string;
71
+ title?: string;
72
+ };
73
+
74
+ export type FilePart = {
75
+ type: "file";
76
+ data: string;
77
+ mimeType: string;
78
+ };
79
+
80
+ export type AssistantMessagePart =
81
+ | TextPart
82
+ | ReasoningPart
83
+ | ToolCallPart
84
+ | SourcePart
85
+ | FilePart;
86
+
87
+ type AssistantMessageStepUsage = {
88
+ promptTokens: number;
89
+ completionTokens: number;
90
+ };
91
+
92
+ type AssistantMessageStepMetadata =
93
+ | {
94
+ state: "started";
95
+ messageId: string;
96
+ }
97
+ | {
98
+ state: "finished";
99
+ messageId: string;
100
+ finishReason:
101
+ | "stop"
102
+ | "length"
103
+ | "content-filter"
104
+ | "tool-calls"
105
+ | "error"
106
+ | "other"
107
+ | "unknown";
108
+ usage?: AssistantMessageStepUsage;
109
+ isContinued: boolean;
110
+ };
111
+
112
+ export type AssistantMessageStatus =
113
+ | {
114
+ type: "running";
115
+ }
116
+ | {
117
+ type: "requires-action";
118
+ reason: "tool-calls";
119
+ }
120
+ | {
121
+ type: "complete";
122
+ reason: "stop" | "unknown";
123
+ }
124
+ | {
125
+ type: "incomplete";
126
+ reason:
127
+ | "cancelled"
128
+ | "tool-calls"
129
+ | "length"
130
+ | "content-filter"
131
+ | "other"
132
+ | "error";
133
+ error?: ReadonlyJSONValue;
134
+ };
135
+
136
+ export type AssistantMessage = {
137
+ role: "assistant";
138
+ status: AssistantMessageStatus;
139
+ parts: AssistantMessagePart[];
140
+ /**
141
+ * @deprecated Use `parts` instead.
142
+ */
143
+ content: AssistantMessagePart[];
144
+ metadata: {
145
+ unstable_data: ReadonlyJSONValue[];
146
+ unstable_annotations: ReadonlyJSONValue[];
147
+ steps: AssistantMessageStepMetadata[];
148
+ custom: Record<string, unknown>;
149
+ };
150
+ };
@@ -0,0 +1,20 @@
1
+ export function withPromiseOrValue<T>(
2
+ callback: () => T | PromiseLike<T>,
3
+ thenHandler: (value: T) => PromiseLike<void> | void,
4
+ catchHandler: (error: unknown) => PromiseLike<void> | void,
5
+ ): PromiseLike<void> | void {
6
+ try {
7
+ const promiseOrValue = callback();
8
+ if (
9
+ typeof promiseOrValue === "object" &&
10
+ promiseOrValue !== null &&
11
+ "then" in promiseOrValue
12
+ ) {
13
+ return promiseOrValue.then(thenHandler, catchHandler);
14
+ } else {
15
+ thenHandler(promiseOrValue);
16
+ }
17
+ } catch (e) {
18
+ catchHandler(e);
19
+ }
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./core";
@@ -0,0 +1,24 @@
1
+ export type AsyncIterableStream<T> = AsyncIterable<T> & ReadableStream<T>;
2
+
3
+ async function* streamGeneratorPolyfill<T>(
4
+ this: ReadableStream<T>,
5
+ ): AsyncIterator<T, undefined, unknown> {
6
+ const reader = this.getReader();
7
+ try {
8
+ while (true) {
9
+ const { done, value } = await reader.read();
10
+ if (done) break;
11
+ yield value;
12
+ }
13
+ } finally {
14
+ reader.releaseLock();
15
+ }
16
+ }
17
+
18
+ export function asAsyncIterableStream<T>(
19
+ source: ReadableStream<T>,
20
+ ): AsyncIterableStream<T> {
21
+ (source as AsyncIterableStream<T>)[Symbol.asyncIterator] ??=
22
+ streamGeneratorPolyfill;
23
+ return source as AsyncIterableStream<T>;
24
+ }
@@ -1,17 +1,81 @@
1
- // src/utils/json/fix-json.ts
2
- function fixJson(input) {
3
- const stack = ["ROOT"];
1
+ // LICENSE for this file only
2
+
3
+ // MIT License
4
+
5
+ // Copyright (c) 2025 AgentbaseAI Inc.
6
+ // Copyright (c) 2023 Lars Grammel
7
+
8
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ // of this software and associated documentation files (the "Software"), to deal
10
+ // in the Software without restriction, including without limitation the rights
11
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ // copies of the Software, and to permit persons to whom the Software is
13
+ // furnished to do so, subject to the following conditions:
14
+
15
+ // The above copyright notice and this permission notice shall be included in all
16
+ // copies or substantial portions of the Software.
17
+
18
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ // SOFTWARE.
25
+
26
+ type State =
27
+ | "ROOT"
28
+ | "FINISH"
29
+ | "INSIDE_STRING"
30
+ | "INSIDE_STRING_ESCAPE"
31
+ | "INSIDE_LITERAL"
32
+ | "INSIDE_NUMBER"
33
+ | "INSIDE_OBJECT_START"
34
+ | "INSIDE_OBJECT_KEY"
35
+ | "INSIDE_OBJECT_AFTER_KEY"
36
+ | "INSIDE_OBJECT_BEFORE_VALUE"
37
+ | "INSIDE_OBJECT_AFTER_VALUE"
38
+ | "INSIDE_OBJECT_AFTER_COMMA"
39
+ | "INSIDE_ARRAY_START"
40
+ | "INSIDE_ARRAY_AFTER_VALUE"
41
+ | "INSIDE_ARRAY_AFTER_COMMA";
42
+
43
+ // Implemented as a scanner with additional fixing
44
+ // that performs a single linear time scan pass over the partial JSON.
45
+ //
46
+ // The states should ideally match relevant states from the JSON spec:
47
+ // https://www.json.org/json-en.html
48
+ //
49
+ // Please note that invalid JSON is not considered/covered, because it
50
+ // is assumed that the resulting JSON will be processed by a standard
51
+ // JSON parser that will detect any invalid JSON.
52
+
53
+ // Returns a tuple of [fixedJson, partialPath]
54
+ // partialPath is an array of object/array keys that represent
55
+ // the currently partial values. An object is considered partial
56
+ // if through appending extra characters to the JSON string, its
57
+ // value could change.
58
+
59
+ // Example input: '{"foo":[{"a":f'
60
+ // Example output: ['{"foo":[{"a":false}]}', ['foo', '0']]
61
+ // Example input: '{"foo":'
62
+ // Example output: ['{}', []]
63
+
64
+ export function fixJson(input: string): [string, string[]] {
65
+ const stack: State[] = ["ROOT"];
4
66
  let lastValidIndex = -1;
5
- let literalStart = null;
6
- const path = [];
7
- let currentKey;
8
- function pushCurrentKeyToPath() {
9
- if (currentKey !== void 0) {
67
+ let literalStart: number | null = null;
68
+ const path: string[] = [];
69
+ let currentKey: string | undefined;
70
+
71
+ function pushCurrentKeyToPath(): void {
72
+ if (currentKey !== undefined) {
10
73
  path.push(JSON.parse('"' + currentKey + '"'));
11
- currentKey = void 0;
74
+ currentKey = undefined;
12
75
  }
13
76
  }
14
- function processValueStart(char, i, swapState) {
77
+
78
+ function processValueStart(char: string, i: number, swapState: State) {
15
79
  {
16
80
  switch (char) {
17
81
  case '"': {
@@ -19,9 +83,11 @@ function fixJson(input) {
19
83
  stack.pop();
20
84
  stack.push(swapState);
21
85
  stack.push("INSIDE_STRING");
86
+
22
87
  pushCurrentKeyToPath();
23
88
  break;
24
89
  }
90
+
25
91
  case "f":
26
92
  case "t":
27
93
  case "n": {
@@ -32,10 +98,12 @@ function fixJson(input) {
32
98
  stack.push("INSIDE_LITERAL");
33
99
  break;
34
100
  }
101
+
35
102
  case "-": {
36
103
  stack.pop();
37
104
  stack.push(swapState);
38
105
  stack.push("INSIDE_NUMBER");
106
+
39
107
  pushCurrentKeyToPath();
40
108
  break;
41
109
  }
@@ -53,29 +121,35 @@ function fixJson(input) {
53
121
  stack.pop();
54
122
  stack.push(swapState);
55
123
  stack.push("INSIDE_NUMBER");
124
+
56
125
  pushCurrentKeyToPath();
57
126
  break;
58
127
  }
128
+
59
129
  case "{": {
60
130
  lastValidIndex = i;
61
131
  stack.pop();
62
132
  stack.push(swapState);
63
133
  stack.push("INSIDE_OBJECT_START");
134
+
64
135
  pushCurrentKeyToPath();
65
136
  break;
66
137
  }
138
+
67
139
  case "[": {
68
140
  lastValidIndex = i;
69
141
  stack.pop();
70
142
  stack.push(swapState);
71
143
  stack.push("INSIDE_ARRAY_START");
144
+
72
145
  pushCurrentKeyToPath();
73
146
  break;
74
147
  }
75
148
  }
76
149
  }
77
150
  }
78
- function processAfterObjectValue(char, i) {
151
+
152
+ function processAfterObjectValue(char: string, i: number) {
79
153
  switch (char) {
80
154
  case ",": {
81
155
  stack.pop();
@@ -90,7 +164,8 @@ function fixJson(input) {
90
164
  }
91
165
  }
92
166
  }
93
- function processAfterArrayValue(char, i) {
167
+
168
+ function processAfterArrayValue(char: string, i: number) {
94
169
  switch (char) {
95
170
  case ",": {
96
171
  stack.pop();
@@ -106,13 +181,16 @@ function fixJson(input) {
106
181
  }
107
182
  }
108
183
  }
184
+
109
185
  for (let i = 0; i < input.length; i++) {
110
- const char = input[i];
186
+ const char = input[i]!;
111
187
  const currentState = stack[stack.length - 1];
188
+
112
189
  switch (currentState) {
113
190
  case "ROOT":
114
191
  processValueStart(char, i, "FINISH");
115
192
  break;
193
+
116
194
  case "INSIDE_OBJECT_START": {
117
195
  switch (char) {
118
196
  case '"': {
@@ -130,6 +208,7 @@ function fixJson(input) {
130
208
  }
131
209
  break;
132
210
  }
211
+
133
212
  case "INSIDE_OBJECT_AFTER_COMMA": {
134
213
  switch (char) {
135
214
  case '"': {
@@ -141,6 +220,7 @@ function fixJson(input) {
141
220
  }
142
221
  break;
143
222
  }
223
+
144
224
  case "INSIDE_OBJECT_KEY": {
145
225
  switch (char) {
146
226
  case '"': {
@@ -160,42 +240,52 @@ function fixJson(input) {
160
240
  }
161
241
  break;
162
242
  }
243
+
163
244
  case "INSIDE_OBJECT_AFTER_KEY": {
164
245
  switch (char) {
165
246
  case ":": {
166
247
  stack.pop();
167
248
  stack.push("INSIDE_OBJECT_BEFORE_VALUE");
249
+
168
250
  break;
169
251
  }
170
252
  }
171
253
  break;
172
254
  }
255
+
173
256
  case "INSIDE_OBJECT_BEFORE_VALUE": {
174
257
  processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE");
175
258
  break;
176
259
  }
260
+
177
261
  case "INSIDE_OBJECT_AFTER_VALUE": {
178
262
  processAfterObjectValue(char, i);
179
263
  break;
180
264
  }
265
+
181
266
  case "INSIDE_STRING": {
182
267
  switch (char) {
183
268
  case '"': {
184
269
  stack.pop();
185
270
  lastValidIndex = i;
271
+
186
272
  currentKey = path.pop();
187
273
  break;
188
274
  }
275
+
189
276
  case "\\": {
190
277
  stack.push("INSIDE_STRING_ESCAPE");
191
278
  break;
192
279
  }
280
+
193
281
  default: {
194
282
  lastValidIndex = i;
195
283
  }
196
284
  }
285
+
197
286
  break;
198
287
  }
288
+
199
289
  case "INSIDE_ARRAY_START": {
200
290
  switch (char) {
201
291
  case "]": {
@@ -204,6 +294,7 @@ function fixJson(input) {
204
294
  currentKey = path.pop();
205
295
  break;
206
296
  }
297
+
207
298
  default: {
208
299
  lastValidIndex = i;
209
300
  currentKey = "0";
@@ -213,40 +304,50 @@ function fixJson(input) {
213
304
  }
214
305
  break;
215
306
  }
307
+
216
308
  case "INSIDE_ARRAY_AFTER_VALUE": {
217
309
  switch (char) {
218
310
  case ",": {
219
311
  stack.pop();
220
312
  stack.push("INSIDE_ARRAY_AFTER_COMMA");
313
+
221
314
  currentKey = (Number(currentKey) + 1).toString();
222
315
  break;
223
316
  }
317
+
224
318
  case "]": {
225
319
  lastValidIndex = i;
226
320
  stack.pop();
227
321
  currentKey = path.pop();
228
322
  break;
229
323
  }
324
+
230
325
  default: {
231
326
  lastValidIndex = i;
232
327
  break;
233
328
  }
234
329
  }
330
+
235
331
  break;
236
332
  }
333
+
237
334
  case "INSIDE_ARRAY_AFTER_COMMA": {
238
335
  processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
239
336
  break;
240
337
  }
338
+
241
339
  case "INSIDE_STRING_ESCAPE": {
242
340
  stack.pop();
341
+
243
342
  if (stack[stack.length - 1] === "INSIDE_STRING") {
244
343
  lastValidIndex = i;
245
344
  } else if (stack[stack.length - 1] === "INSIDE_OBJECT_KEY") {
246
345
  currentKey += char;
247
346
  }
347
+
248
348
  break;
249
349
  }
350
+
250
351
  case "INSIDE_NUMBER": {
251
352
  switch (char) {
252
353
  case "0":
@@ -262,51 +363,71 @@ function fixJson(input) {
262
363
  lastValidIndex = i;
263
364
  break;
264
365
  }
366
+
265
367
  case "e":
266
368
  case "E":
267
369
  case "-":
268
370
  case ".": {
269
371
  break;
270
372
  }
373
+
271
374
  case ",": {
272
375
  stack.pop();
273
376
  currentKey = path.pop();
377
+
274
378
  if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
275
379
  processAfterArrayValue(char, i);
276
380
  }
381
+
277
382
  if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
278
383
  processAfterObjectValue(char, i);
279
384
  }
385
+
280
386
  break;
281
387
  }
388
+
282
389
  case "}": {
283
390
  stack.pop();
284
391
  currentKey = path.pop();
392
+
285
393
  if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
286
394
  processAfterObjectValue(char, i);
287
395
  }
396
+
288
397
  break;
289
398
  }
399
+
290
400
  case "]": {
291
401
  stack.pop();
292
402
  currentKey = path.pop();
403
+
293
404
  if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
294
405
  processAfterArrayValue(char, i);
295
406
  }
407
+
296
408
  break;
297
409
  }
410
+
298
411
  default: {
299
412
  stack.pop();
300
413
  currentKey = path.pop();
301
414
  break;
302
415
  }
303
416
  }
417
+
304
418
  break;
305
419
  }
420
+
306
421
  case "INSIDE_LITERAL": {
307
- const partialLiteral = input.substring(literalStart, i + 1);
308
- if (!"false".startsWith(partialLiteral) && !"true".startsWith(partialLiteral) && !"null".startsWith(partialLiteral)) {
422
+ const partialLiteral = input.substring(literalStart!, i + 1);
423
+
424
+ if (
425
+ !"false".startsWith(partialLiteral) &&
426
+ !"true".startsWith(partialLiteral) &&
427
+ !"null".startsWith(partialLiteral)
428
+ ) {
309
429
  stack.pop();
430
+
310
431
  if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
311
432
  processAfterObjectValue(char, i);
312
433
  } else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
@@ -315,18 +436,23 @@ function fixJson(input) {
315
436
  } else {
316
437
  lastValidIndex = i;
317
438
  }
439
+
318
440
  break;
319
441
  }
320
442
  }
321
443
  }
444
+
322
445
  let result = input.slice(0, lastValidIndex + 1);
446
+
323
447
  for (let i = stack.length - 1; i >= 0; i--) {
324
448
  const state = stack[i];
449
+
325
450
  switch (state) {
326
451
  case "INSIDE_STRING": {
327
452
  result += '"';
328
453
  break;
329
454
  }
455
+
330
456
  case "INSIDE_OBJECT_KEY":
331
457
  case "INSIDE_OBJECT_AFTER_KEY":
332
458
  case "INSIDE_OBJECT_AFTER_COMMA":
@@ -336,14 +462,17 @@ function fixJson(input) {
336
462
  result += "}";
337
463
  break;
338
464
  }
465
+
339
466
  case "INSIDE_ARRAY_START":
340
467
  case "INSIDE_ARRAY_AFTER_COMMA":
341
468
  case "INSIDE_ARRAY_AFTER_VALUE": {
342
469
  result += "]";
343
470
  break;
344
471
  }
472
+
345
473
  case "INSIDE_LITERAL": {
346
- const partialLiteral = input.substring(literalStart, input.length);
474
+ const partialLiteral = input.substring(literalStart!, input.length);
475
+
347
476
  if ("true".startsWith(partialLiteral)) {
348
477
  result += "true".slice(partialLiteral.length);
349
478
  } else if ("false".startsWith(partialLiteral)) {
@@ -354,9 +483,6 @@ function fixJson(input) {
354
483
  }
355
484
  }
356
485
  }
486
+
357
487
  return [result, path];
358
488
  }
359
- export {
360
- fixJson
361
- };
362
- //# sourceMappingURL=fix-json.mjs.map