@kortyx/stream 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -19,8 +19,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/browser.ts
20
20
  var browser_exports = {};
21
21
  __export(browser_exports, {
22
+ applyStructuredChunk: () => applyStructuredChunk,
22
23
  consumeStream: () => consumeStream,
24
+ createStructuredStreamAccumulator: () => createStructuredStreamAccumulator,
23
25
  readStream: () => readStream,
26
+ reduceStructuredChunks: () => reduceStructuredChunks,
24
27
  streamFromRoute: () => streamFromRoute
25
28
  });
26
29
  module.exports = __toCommonJS(browser_exports);
@@ -124,10 +127,146 @@ async function* streamFromRoute(args) {
124
127
  }
125
128
  yield* readStream(response.body);
126
129
  }
130
+
131
+ // src/structured/apply-structured-chunk.ts
132
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
133
+ var cloneContainer = (value) => {
134
+ if (Array.isArray(value)) return [...value];
135
+ if (isPlainObject(value)) return { ...value };
136
+ return {};
137
+ };
138
+ var isArrayIndex = (value) => /^\d+$/.test(value);
139
+ var setByParts = (input, parts, value) => {
140
+ if (parts.length === 0) return value;
141
+ const [part, ...rest] = parts;
142
+ if (part === void 0) return value;
143
+ if (Array.isArray(input) || isArrayIndex(part)) {
144
+ const nextArray = Array.isArray(input) ? [...input] : [];
145
+ const index = Number(part);
146
+ if (!Number.isInteger(index)) return nextArray;
147
+ nextArray[index] = setByParts(nextArray[index], rest, value);
148
+ return nextArray;
149
+ }
150
+ const nextObject = isPlainObject(input) ? { ...input } : {};
151
+ nextObject[part] = setByParts(nextObject[part], rest, value);
152
+ return nextObject;
153
+ };
154
+ var getByPath = (input, path) => {
155
+ const parts = path.split(".").filter(Boolean);
156
+ let current = input;
157
+ for (const part of parts) {
158
+ if (Array.isArray(current)) {
159
+ const index = Number(part);
160
+ current = Number.isInteger(index) ? current[index] : void 0;
161
+ continue;
162
+ }
163
+ if (isPlainObject(current)) {
164
+ current = current[part];
165
+ continue;
166
+ }
167
+ return void 0;
168
+ }
169
+ return current;
170
+ };
171
+ var setByPath = (input, path, value) => {
172
+ const parts = path.split(".").filter(Boolean);
173
+ return parts.length === 0 ? value : setByParts(cloneContainer(input), parts, value);
174
+ };
175
+ function applyStructuredChunk(current, chunk) {
176
+ if (current && current.streamId !== chunk.streamId) {
177
+ throw new Error(
178
+ `Structured chunk streamId mismatch: expected ${current.streamId}, received ${chunk.streamId}.`
179
+ );
180
+ }
181
+ if (current?.status === "done") {
182
+ throw new Error(
183
+ `Structured stream ${chunk.streamId} already completed with a final chunk.`
184
+ );
185
+ }
186
+ const nextBase = {
187
+ streamId: chunk.streamId,
188
+ dataType: chunk.dataType,
189
+ status: chunk.kind === "final" ? "done" : "streaming",
190
+ ...chunk.node ? { node: chunk.node } : {},
191
+ ...chunk.id ? { id: chunk.id } : {},
192
+ ...chunk.schemaId ? { schemaId: chunk.schemaId } : {},
193
+ ...chunk.schemaVersion ? { schemaVersion: chunk.schemaVersion } : {}
194
+ };
195
+ if (chunk.kind === "final") {
196
+ return {
197
+ ...nextBase,
198
+ data: chunk.data
199
+ };
200
+ }
201
+ const previousData = current?.data !== void 0 ? current.data : {};
202
+ if (chunk.kind === "set") {
203
+ return {
204
+ ...nextBase,
205
+ data: setByPath(previousData, chunk.path, chunk.value)
206
+ };
207
+ }
208
+ if (chunk.kind === "append") {
209
+ const existing2 = getByPath(previousData, chunk.path);
210
+ const nextItems = Array.isArray(existing2) ? [...existing2, ...chunk.items] : [...chunk.items];
211
+ return {
212
+ ...nextBase,
213
+ data: setByPath(previousData, chunk.path, nextItems)
214
+ };
215
+ }
216
+ const existing = getByPath(previousData, chunk.path);
217
+ const text = typeof existing === "string" ? existing : "";
218
+ return {
219
+ ...nextBase,
220
+ data: setByPath(previousData, chunk.path, text + chunk.delta)
221
+ };
222
+ }
223
+ function reduceStructuredChunks(chunks) {
224
+ return chunks.reduce(
225
+ (acc, chunk) => {
226
+ acc[chunk.streamId] = applyStructuredChunk(acc[chunk.streamId], chunk);
227
+ return acc;
228
+ },
229
+ {}
230
+ );
231
+ }
232
+
233
+ // src/structured/structured-stream-accumulator.ts
234
+ var isStructuredDataChunk = (chunk) => chunk.type === "structured-data";
235
+ function createStructuredStreamAccumulator(initialChunks = []) {
236
+ const states = /* @__PURE__ */ new Map();
237
+ const apply = (chunk) => {
238
+ const nextState = applyStructuredChunk(states.get(chunk.streamId), chunk);
239
+ states.set(chunk.streamId, nextState);
240
+ return nextState;
241
+ };
242
+ for (const chunk of initialChunks) {
243
+ apply(chunk);
244
+ }
245
+ return {
246
+ apply,
247
+ applyStreamChunk: (chunk) => {
248
+ if (!isStructuredDataChunk(chunk)) return void 0;
249
+ return apply(chunk);
250
+ },
251
+ get: (streamId) => states.get(streamId),
252
+ has: (streamId) => states.has(streamId),
253
+ delete: (streamId) => states.delete(streamId),
254
+ clear: () => {
255
+ states.clear();
256
+ },
257
+ size: () => states.size,
258
+ entries: () => Array.from(states.entries()),
259
+ values: () => Array.from(states.values()),
260
+ toRecord: () => Object.fromEntries(states.entries())
261
+ };
262
+ }
127
263
  // Annotate the CommonJS export names for ESM import in node:
128
264
  0 && (module.exports = {
265
+ applyStructuredChunk,
129
266
  consumeStream,
267
+ createStructuredStreamAccumulator,
130
268
  readStream,
269
+ reduceStructuredChunks,
131
270
  streamFromRoute
132
271
  });
133
272
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser.ts","../src/client/consume-stream.ts","../src/client/read-stream.ts","../src/client/stream-from-route.ts"],"sourcesContent":["// Browser-safe exports for client bundles.\n// Keep this file free of Node-only imports (fs, path, etc).\n\nexport type { ConsumeStreamHandlers } from \"./client/consume-stream\";\nexport { consumeStream } from \"./client/consume-stream\";\nexport { readStream } from \"./client/read-stream\";\nexport type { StreamFromRouteArgs } from \"./client/stream-from-route\";\nexport { streamFromRoute } from \"./client/stream-from-route\";\nexport type { StreamChunk } from \"./types/stream-chunk\";\n","import type { StreamChunk } from \"../types/stream-chunk\";\n\nexport interface ConsumeStreamHandlers {\n onChunk?: (\n chunk: StreamChunk,\n ) => void | boolean | Promise<boolean | undefined>;\n onDone?: () => void | Promise<void>;\n onError?: (error: Error, chunk?: StreamChunk) => void | Promise<void>;\n}\n\nconst toError = (value: unknown): Error =>\n value instanceof Error ? value : new Error(String(value));\n\n/**\n * Consume a stream of StreamChunk events with callback handlers.\n * Returns when a `done` chunk is seen, the stream ends, or `onChunk` returns `false`.\n */\nexport async function consumeStream(\n stream: AsyncIterable<StreamChunk>,\n handlers: ConsumeStreamHandlers = {},\n): Promise<void> {\n let doneCalled = false;\n\n const callDone = async () => {\n if (doneCalled) return;\n doneCalled = true;\n await handlers.onDone?.();\n };\n\n try {\n for await (const chunk of stream) {\n const shouldContinue = await handlers.onChunk?.(chunk);\n\n if (chunk.type === \"error\") {\n await handlers.onError?.(\n new Error(chunk.message || \"Stream error.\"),\n chunk,\n );\n }\n\n if (chunk.type === \"done\" || shouldContinue === false) {\n await callDone();\n return;\n }\n }\n\n await callDone();\n } catch (error) {\n const normalized = toError(error);\n await handlers.onError?.(normalized);\n throw normalized;\n }\n}\n","// packages/stream/src/client/read-stream.ts\nimport type { StreamChunk } from \"../types/stream-chunk\";\n\n/**\n * Reads a server-sent event (SSE) Response body and yields StreamChunk objects.\n */\nexport async function* readStream(\n body: ReadableStream<Uint8Array> | null,\n): AsyncGenerator<StreamChunk, void, void> {\n if (!body) return;\n\n const reader = body.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop() ?? \"\";\n\n for (const part of parts) {\n if (!part.startsWith(\"data: \")) continue;\n const payload = part.slice(6);\n if (payload.trim() === \"[DONE]\") return;\n try {\n yield JSON.parse(payload) as StreamChunk;\n } catch (err: any) {\n console.warn(\"Invalid JSON in stream chunk:\", payload);\n console.error(err);\n }\n }\n }\n}\n","import type { StreamChunk } from \"../types/stream-chunk\";\nimport { readStream } from \"./read-stream\";\n\nexport interface StreamFromRouteArgs<TBody = unknown> {\n endpoint: string;\n body: TBody;\n method?: string | undefined;\n fetchImpl?: typeof fetch;\n headers?: Record<string, string> | undefined;\n}\n\nconst toErrorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nconst readErrorMessage = async (response: Response): Promise<string> => {\n let message = `Request failed (${response.status})`;\n try {\n const payload = (await response.json()) as unknown;\n if (\n payload &&\n typeof payload === \"object\" &&\n \"error\" in payload &&\n typeof (payload as { error?: unknown }).error === \"string\"\n ) {\n message = (payload as { error: string }).error;\n }\n } catch {}\n return message;\n};\n\nexport async function* streamFromRoute<TBody = unknown>(\n args: StreamFromRouteArgs<TBody>,\n): AsyncGenerator<StreamChunk, void, void> {\n const fetchFn = args.fetchImpl ?? globalThis.fetch;\n if (!fetchFn) {\n yield { type: \"error\", message: \"No fetch implementation available.\" };\n yield { type: \"done\" };\n return;\n }\n\n let response: Response;\n try {\n response = await fetchFn(args.endpoint, {\n method: args.method ?? \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(args.headers ?? {}),\n },\n body: JSON.stringify(args.body),\n });\n } catch (error) {\n yield { type: \"error\", message: toErrorMessage(error) };\n yield { type: \"done\" };\n return;\n }\n\n if (!response.ok) {\n yield { type: \"error\", message: await readErrorMessage(response) };\n yield { type: \"done\" };\n return;\n }\n\n yield* readStream(response.body);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/browser.ts","../src/client/consume-stream.ts","../src/client/read-stream.ts","../src/client/stream-from-route.ts","../src/structured/apply-structured-chunk.ts","../src/structured/structured-stream-accumulator.ts"],"sourcesContent":["// Browser-safe exports for client bundles.\n// Keep this file free of Node-only imports (fs, path, etc).\n\nexport type { ConsumeStreamHandlers } from \"./client/consume-stream\";\nexport { consumeStream } from \"./client/consume-stream\";\nexport { readStream } from \"./client/read-stream\";\nexport type { StreamFromRouteArgs } from \"./client/stream-from-route\";\nexport { streamFromRoute } from \"./client/stream-from-route\";\nexport * from \"./structured/apply-structured-chunk\";\nexport * from \"./structured/structured-stream-accumulator\";\nexport type { StreamChunk } from \"./types/stream-chunk\";\nexport type { StructuredDataChunk } from \"./types/structured-data\";\n","import type { StreamChunk } from \"../types/stream-chunk\";\n\nexport interface ConsumeStreamHandlers {\n onChunk?: (\n chunk: StreamChunk,\n ) => undefined | boolean | Promise<boolean | undefined>;\n onDone?: () => void | Promise<void>;\n onError?: (error: Error, chunk?: StreamChunk) => void | Promise<void>;\n}\n\nconst toError = (value: unknown): Error =>\n value instanceof Error ? value : new Error(String(value));\n\n/**\n * Consume a stream of StreamChunk events with callback handlers.\n * Returns when a `done` chunk is seen, the stream ends, or `onChunk` returns `false`.\n */\nexport async function consumeStream(\n stream: AsyncIterable<StreamChunk>,\n handlers: ConsumeStreamHandlers = {},\n): Promise<void> {\n let doneCalled = false;\n\n const callDone = async () => {\n if (doneCalled) return;\n doneCalled = true;\n await handlers.onDone?.();\n };\n\n try {\n for await (const chunk of stream) {\n const shouldContinue = await handlers.onChunk?.(chunk);\n\n if (chunk.type === \"error\") {\n await handlers.onError?.(\n new Error(chunk.message || \"Stream error.\"),\n chunk,\n );\n }\n\n if (chunk.type === \"done\" || shouldContinue === false) {\n await callDone();\n return;\n }\n }\n\n await callDone();\n } catch (error) {\n const normalized = toError(error);\n await handlers.onError?.(normalized);\n throw normalized;\n }\n}\n","// packages/stream/src/client/read-stream.ts\nimport type { StreamChunk } from \"../types/stream-chunk\";\n\n/**\n * Reads a server-sent event (SSE) Response body and yields StreamChunk objects.\n */\nexport async function* readStream(\n body: ReadableStream<Uint8Array> | null,\n): AsyncGenerator<StreamChunk, void, void> {\n if (!body) return;\n\n const reader = body.getReader();\n const decoder = new TextDecoder(\"utf-8\");\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop() ?? \"\";\n\n for (const part of parts) {\n if (!part.startsWith(\"data: \")) continue;\n const payload = part.slice(6);\n if (payload.trim() === \"[DONE]\") return;\n try {\n yield JSON.parse(payload) as StreamChunk;\n } catch (err: any) {\n console.warn(\"Invalid JSON in stream chunk:\", payload);\n console.error(err);\n }\n }\n }\n}\n","import type { StreamChunk } from \"../types/stream-chunk\";\nimport { readStream } from \"./read-stream\";\n\nexport interface StreamFromRouteArgs<TBody = unknown> {\n endpoint: string;\n body: TBody;\n method?: string | undefined;\n fetchImpl?: typeof fetch;\n headers?: Record<string, string> | undefined;\n}\n\nconst toErrorMessage = (error: unknown): string =>\n error instanceof Error ? error.message : String(error);\n\nconst readErrorMessage = async (response: Response): Promise<string> => {\n let message = `Request failed (${response.status})`;\n try {\n const payload = (await response.json()) as unknown;\n if (\n payload &&\n typeof payload === \"object\" &&\n \"error\" in payload &&\n typeof (payload as { error?: unknown }).error === \"string\"\n ) {\n message = (payload as { error: string }).error;\n }\n } catch {}\n return message;\n};\n\nexport async function* streamFromRoute<TBody = unknown>(\n args: StreamFromRouteArgs<TBody>,\n): AsyncGenerator<StreamChunk, void, void> {\n const fetchFn = args.fetchImpl ?? globalThis.fetch;\n if (!fetchFn) {\n yield { type: \"error\", message: \"No fetch implementation available.\" };\n yield { type: \"done\" };\n return;\n }\n\n let response: Response;\n try {\n response = await fetchFn(args.endpoint, {\n method: args.method ?? \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(args.headers ?? {}),\n },\n body: JSON.stringify(args.body),\n });\n } catch (error) {\n yield { type: \"error\", message: toErrorMessage(error) };\n yield { type: \"done\" };\n return;\n }\n\n if (!response.ok) {\n yield { type: \"error\", message: await readErrorMessage(response) };\n yield { type: \"done\" };\n return;\n }\n\n yield* readStream(response.body);\n}\n","import type { StructuredDataChunk } from \"../types/structured-data\";\n\nexport type StructuredStreamState<TData = unknown> = {\n streamId: string;\n dataType: string;\n status: \"streaming\" | \"done\";\n data: TData;\n node?: string;\n id?: string;\n schemaId?: string;\n schemaVersion?: string;\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst cloneContainer = (value: unknown): unknown => {\n if (Array.isArray(value)) return [...value];\n if (isPlainObject(value)) return { ...value };\n return {};\n};\n\nconst isArrayIndex = (value: string): boolean => /^\\d+$/.test(value);\n\nconst setByParts = (\n input: unknown,\n parts: string[],\n value: unknown,\n): unknown => {\n if (parts.length === 0) return value;\n\n const [part, ...rest] = parts;\n if (part === undefined) return value;\n\n if (Array.isArray(input) || isArrayIndex(part)) {\n const nextArray = Array.isArray(input) ? [...input] : [];\n const index = Number(part);\n if (!Number.isInteger(index)) return nextArray;\n nextArray[index] = setByParts(nextArray[index], rest, value);\n return nextArray;\n }\n\n const nextObject = isPlainObject(input) ? { ...input } : {};\n nextObject[part] = setByParts(nextObject[part], rest, value);\n return nextObject;\n};\n\nconst getByPath = (input: unknown, path: string): unknown => {\n const parts = path.split(\".\").filter(Boolean);\n let current: unknown = input;\n\n for (const part of parts) {\n if (Array.isArray(current)) {\n const index = Number(part);\n current = Number.isInteger(index) ? current[index] : undefined;\n continue;\n }\n\n if (isPlainObject(current)) {\n current = current[part];\n continue;\n }\n\n return undefined;\n }\n\n return current;\n};\n\nconst setByPath = (input: unknown, path: string, value: unknown): unknown => {\n const parts = path.split(\".\").filter(Boolean);\n return parts.length === 0\n ? value\n : setByParts(cloneContainer(input), parts, value);\n};\n\nexport function applyStructuredChunk<TData = unknown>(\n current: StructuredStreamState<TData> | undefined,\n chunk: StructuredDataChunk,\n): StructuredStreamState<TData> {\n if (current && current.streamId !== chunk.streamId) {\n throw new Error(\n `Structured chunk streamId mismatch: expected ${current.streamId}, received ${chunk.streamId}.`,\n );\n }\n\n if (current?.status === \"done\") {\n throw new Error(\n `Structured stream ${chunk.streamId} already completed with a final chunk.`,\n );\n }\n\n const nextBase = {\n streamId: chunk.streamId,\n dataType: chunk.dataType,\n status: chunk.kind === \"final\" ? (\"done\" as const) : (\"streaming\" as const),\n ...(chunk.node ? { node: chunk.node } : {}),\n ...(chunk.id ? { id: chunk.id } : {}),\n ...(chunk.schemaId ? { schemaId: chunk.schemaId } : {}),\n ...(chunk.schemaVersion ? { schemaVersion: chunk.schemaVersion } : {}),\n };\n\n if (chunk.kind === \"final\") {\n return {\n ...nextBase,\n data: chunk.data as TData,\n };\n }\n\n const previousData =\n current?.data !== undefined\n ? current.data\n : ({} as TData | Record<string, unknown>);\n\n if (chunk.kind === \"set\") {\n return {\n ...nextBase,\n data: setByPath(previousData, chunk.path, chunk.value) as TData,\n };\n }\n\n if (chunk.kind === \"append\") {\n const existing = getByPath(previousData, chunk.path);\n const nextItems = Array.isArray(existing)\n ? [...existing, ...chunk.items]\n : [...chunk.items];\n return {\n ...nextBase,\n data: setByPath(previousData, chunk.path, nextItems) as TData,\n };\n }\n\n const existing = getByPath(previousData, chunk.path);\n const text = typeof existing === \"string\" ? existing : \"\";\n return {\n ...nextBase,\n data: setByPath(previousData, chunk.path, text + chunk.delta) as TData,\n };\n}\n\nexport function reduceStructuredChunks<TData = unknown>(\n chunks: StructuredDataChunk[],\n): Record<string, StructuredStreamState<TData>> {\n return chunks.reduce<Record<string, StructuredStreamState<TData>>>(\n (acc, chunk) => {\n acc[chunk.streamId] = applyStructuredChunk(acc[chunk.streamId], chunk);\n return acc;\n },\n {},\n );\n}\n","import type { StreamChunk } from \"../types/stream-chunk\";\nimport type { StructuredDataChunk } from \"../types/structured-data\";\nimport {\n applyStructuredChunk,\n type StructuredStreamState,\n} from \"./apply-structured-chunk\";\n\nexport type StructuredStreamAccumulator<TData = unknown> = {\n apply: (chunk: StructuredDataChunk) => StructuredStreamState<TData>;\n applyStreamChunk: (\n chunk: StreamChunk,\n ) => StructuredStreamState<TData> | undefined;\n get: (streamId: string) => StructuredStreamState<TData> | undefined;\n has: (streamId: string) => boolean;\n delete: (streamId: string) => boolean;\n clear: () => void;\n size: () => number;\n entries: () => Array<[string, StructuredStreamState<TData>]>;\n values: () => StructuredStreamState<TData>[];\n toRecord: () => Record<string, StructuredStreamState<TData>>;\n};\n\nconst isStructuredDataChunk = (\n chunk: StreamChunk,\n): chunk is StructuredDataChunk => chunk.type === \"structured-data\";\n\nexport function createStructuredStreamAccumulator<TData = unknown>(\n initialChunks: Iterable<StructuredDataChunk> = [],\n): StructuredStreamAccumulator<TData> {\n const states = new Map<string, StructuredStreamState<TData>>();\n\n const apply = (chunk: StructuredDataChunk): StructuredStreamState<TData> => {\n const nextState = applyStructuredChunk(states.get(chunk.streamId), chunk);\n states.set(chunk.streamId, nextState);\n return nextState;\n };\n\n for (const chunk of initialChunks) {\n apply(chunk);\n }\n\n return {\n apply,\n applyStreamChunk: (chunk) => {\n if (!isStructuredDataChunk(chunk)) return undefined;\n return apply(chunk);\n },\n get: (streamId) => states.get(streamId),\n has: (streamId) => states.has(streamId),\n delete: (streamId) => states.delete(streamId),\n clear: () => {\n states.clear();\n },\n size: () => states.size,\n entries: () => Array.from(states.entries()),\n values: () => Array.from(states.values()),\n toRecord: () => Object.fromEntries(states.entries()),\n };\n}\n"],"mappings":"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","names":["existing"]}
package/dist/index.d.ts CHANGED
@@ -1,22 +1,11 @@
1
- import { S as StreamChunk } from './browser-ojdAqLMO.js';
2
- export { C as ConsumeStreamHandlers, a as StreamChunkSchema, b as StreamFromRouteArgs, c as consumeStream, r as readStream, s as streamFromRoute } from './browser-ojdAqLMO.js';
1
+ import { S as StreamChunk, a as StructuredDataChunk } from './browser-DBfCMmkm.js';
2
+ export { C as ConsumeStreamHandlers, b as StreamChunkSchema, c as StreamFromRouteArgs, d as StructuredDataChunkSchema, e as StructuredStreamAccumulator, f as StructuredStreamState, g as applyStructuredChunk, h as consumeStream, i as createStructuredStreamAccumulator, r as readStream, j as reduceStructuredChunks, s as streamFromRoute } from './browser-DBfCMmkm.js';
3
3
  import 'zod';
4
4
 
5
- type StructuredStreamChunk = {
6
- type: "structured-data";
7
- data: unknown;
8
- dataType?: string;
9
- mode?: "final" | "patch" | "snapshot";
10
- schemaId?: string;
11
- schemaVersion?: string;
12
- id?: string;
13
- opId?: string;
14
- node?: string;
15
- };
16
5
  interface BufferedStreamResult {
17
6
  chunks: StreamChunk[];
18
7
  text: string;
19
- structured: StructuredStreamChunk[];
8
+ structured: StructuredDataChunk[];
20
9
  }
21
10
  /**
22
11
  * Derive convenient buffered fields from raw chunks.
@@ -51,4 +40,4 @@ declare function createStreamResponse(stream: AsyncIterable<StreamChunk>): Respo
51
40
  */
52
41
  declare function toSSE(stream: AsyncIterable<StreamChunk>): Response;
53
42
 
54
- export { type BufferedStreamResult, STREAM_HEADERS, StreamChunk, type StructuredStreamChunk, collectBufferedStream, collectStream, createStreamResponse, summarizeStreamChunks, toSSE };
43
+ export { type BufferedStreamResult, STREAM_HEADERS, StreamChunk, StructuredDataChunk, collectBufferedStream, collectStream, createStreamResponse, summarizeStreamChunks, toSSE };
package/dist/index.js CHANGED
@@ -21,11 +21,15 @@ var index_exports = {};
21
21
  __export(index_exports, {
22
22
  STREAM_HEADERS: () => STREAM_HEADERS,
23
23
  StreamChunkSchema: () => StreamChunkSchema,
24
+ StructuredDataChunkSchema: () => StructuredDataChunkSchema,
25
+ applyStructuredChunk: () => applyStructuredChunk,
24
26
  collectBufferedStream: () => collectBufferedStream,
25
27
  collectStream: () => collectStream,
26
28
  consumeStream: () => consumeStream,
27
29
  createStreamResponse: () => createStreamResponse,
30
+ createStructuredStreamAccumulator: () => createStructuredStreamAccumulator,
28
31
  readStream: () => readStream,
32
+ reduceStructuredChunks: () => reduceStructuredChunks,
29
33
  streamFromRoute: () => streamFromRoute,
30
34
  summarizeStreamChunks: () => summarizeStreamChunks,
31
35
  toSSE: () => toSSE
@@ -223,155 +227,289 @@ function toSSE(stream) {
223
227
  return createStreamResponse(stream);
224
228
  }
225
229
 
226
- // src/types/stream-chunk.ts
227
- var import_zod3 = require("zod");
230
+ // src/structured/apply-structured-chunk.ts
231
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
232
+ var cloneContainer = (value) => {
233
+ if (Array.isArray(value)) return [...value];
234
+ if (isPlainObject(value)) return { ...value };
235
+ return {};
236
+ };
237
+ var isArrayIndex = (value) => /^\d+$/.test(value);
238
+ var setByParts = (input, parts, value) => {
239
+ if (parts.length === 0) return value;
240
+ const [part, ...rest] = parts;
241
+ if (part === void 0) return value;
242
+ if (Array.isArray(input) || isArrayIndex(part)) {
243
+ const nextArray = Array.isArray(input) ? [...input] : [];
244
+ const index = Number(part);
245
+ if (!Number.isInteger(index)) return nextArray;
246
+ nextArray[index] = setByParts(nextArray[index], rest, value);
247
+ return nextArray;
248
+ }
249
+ const nextObject = isPlainObject(input) ? { ...input } : {};
250
+ nextObject[part] = setByParts(nextObject[part], rest, value);
251
+ return nextObject;
252
+ };
253
+ var getByPath = (input, path) => {
254
+ const parts = path.split(".").filter(Boolean);
255
+ let current = input;
256
+ for (const part of parts) {
257
+ if (Array.isArray(current)) {
258
+ const index = Number(part);
259
+ current = Number.isInteger(index) ? current[index] : void 0;
260
+ continue;
261
+ }
262
+ if (isPlainObject(current)) {
263
+ current = current[part];
264
+ continue;
265
+ }
266
+ return void 0;
267
+ }
268
+ return current;
269
+ };
270
+ var setByPath = (input, path, value) => {
271
+ const parts = path.split(".").filter(Boolean);
272
+ return parts.length === 0 ? value : setByParts(cloneContainer(input), parts, value);
273
+ };
274
+ function applyStructuredChunk(current, chunk) {
275
+ if (current && current.streamId !== chunk.streamId) {
276
+ throw new Error(
277
+ `Structured chunk streamId mismatch: expected ${current.streamId}, received ${chunk.streamId}.`
278
+ );
279
+ }
280
+ if (current?.status === "done") {
281
+ throw new Error(
282
+ `Structured stream ${chunk.streamId} already completed with a final chunk.`
283
+ );
284
+ }
285
+ const nextBase = {
286
+ streamId: chunk.streamId,
287
+ dataType: chunk.dataType,
288
+ status: chunk.kind === "final" ? "done" : "streaming",
289
+ ...chunk.node ? { node: chunk.node } : {},
290
+ ...chunk.id ? { id: chunk.id } : {},
291
+ ...chunk.schemaId ? { schemaId: chunk.schemaId } : {},
292
+ ...chunk.schemaVersion ? { schemaVersion: chunk.schemaVersion } : {}
293
+ };
294
+ if (chunk.kind === "final") {
295
+ return {
296
+ ...nextBase,
297
+ data: chunk.data
298
+ };
299
+ }
300
+ const previousData = current?.data !== void 0 ? current.data : {};
301
+ if (chunk.kind === "set") {
302
+ return {
303
+ ...nextBase,
304
+ data: setByPath(previousData, chunk.path, chunk.value)
305
+ };
306
+ }
307
+ if (chunk.kind === "append") {
308
+ const existing2 = getByPath(previousData, chunk.path);
309
+ const nextItems = Array.isArray(existing2) ? [...existing2, ...chunk.items] : [...chunk.items];
310
+ return {
311
+ ...nextBase,
312
+ data: setByPath(previousData, chunk.path, nextItems)
313
+ };
314
+ }
315
+ const existing = getByPath(previousData, chunk.path);
316
+ const text = typeof existing === "string" ? existing : "";
317
+ return {
318
+ ...nextBase,
319
+ data: setByPath(previousData, chunk.path, text + chunk.delta)
320
+ };
321
+ }
322
+ function reduceStructuredChunks(chunks) {
323
+ return chunks.reduce(
324
+ (acc, chunk) => {
325
+ acc[chunk.streamId] = applyStructuredChunk(acc[chunk.streamId], chunk);
326
+ return acc;
327
+ },
328
+ {}
329
+ );
330
+ }
228
331
 
229
- // src/types/structured-data/index.ts
332
+ // src/structured/structured-stream-accumulator.ts
333
+ var isStructuredDataChunk = (chunk) => chunk.type === "structured-data";
334
+ function createStructuredStreamAccumulator(initialChunks = []) {
335
+ const states = /* @__PURE__ */ new Map();
336
+ const apply = (chunk) => {
337
+ const nextState = applyStructuredChunk(states.get(chunk.streamId), chunk);
338
+ states.set(chunk.streamId, nextState);
339
+ return nextState;
340
+ };
341
+ for (const chunk of initialChunks) {
342
+ apply(chunk);
343
+ }
344
+ return {
345
+ apply,
346
+ applyStreamChunk: (chunk) => {
347
+ if (!isStructuredDataChunk(chunk)) return void 0;
348
+ return apply(chunk);
349
+ },
350
+ get: (streamId) => states.get(streamId),
351
+ has: (streamId) => states.has(streamId),
352
+ delete: (streamId) => states.delete(streamId),
353
+ clear: () => {
354
+ states.clear();
355
+ },
356
+ size: () => states.size,
357
+ entries: () => Array.from(states.entries()),
358
+ values: () => Array.from(states.values()),
359
+ toRecord: () => Object.fromEntries(states.entries())
360
+ };
361
+ }
362
+
363
+ // src/types/stream-chunk.ts
230
364
  var import_zod2 = require("zod");
231
365
 
232
- // src/types/structured-data/jobs.ts
366
+ // src/types/structured-data/index.ts
233
367
  var import_zod = require("zod");
234
- var JobsStructuredData = import_zod.z.object({
235
- jobs: import_zod.z.array(
236
- import_zod.z.object({
237
- id: import_zod.z.string(),
238
- title: import_zod.z.string(),
239
- company: import_zod.z.string(),
240
- location: import_zod.z.string(),
241
- match: import_zod.z.number()
242
- })
243
- ),
244
- reasoning: import_zod.z.string().optional()
368
+ var StructuredDataBase = import_zod.z.object({
369
+ type: import_zod.z.literal("structured-data"),
370
+ streamId: import_zod.z.string(),
371
+ dataType: import_zod.z.string(),
372
+ kind: import_zod.z.enum(["set", "append", "text-delta", "final"]),
373
+ schemaId: import_zod.z.string().optional(),
374
+ schemaVersion: import_zod.z.string().optional(),
375
+ id: import_zod.z.string().optional(),
376
+ node: import_zod.z.string().optional()
245
377
  });
246
-
247
- // src/types/structured-data/index.ts
248
- var StructuredDataBase = import_zod2.z.object({
249
- type: import_zod2.z.literal("structured-data"),
250
- dataType: import_zod2.z.string().optional(),
251
- mode: import_zod2.z.enum(["final", "patch", "snapshot"]).optional(),
252
- schemaId: import_zod2.z.string().optional(),
253
- schemaVersion: import_zod2.z.string().optional(),
254
- id: import_zod2.z.string().optional(),
255
- opId: import_zod2.z.string().optional(),
256
- node: import_zod2.z.string().optional()
378
+ var StructuredSetChunkSchema = StructuredDataBase.extend({
379
+ kind: import_zod.z.literal("set"),
380
+ path: import_zod.z.string().min(1),
381
+ value: import_zod.z.unknown()
257
382
  });
258
- var JobsStructuredDataChunkSchema = StructuredDataBase.extend({
259
- dataType: import_zod2.z.literal("jobs"),
260
- data: JobsStructuredData
383
+ var StructuredAppendChunkSchema = StructuredDataBase.extend({
384
+ kind: import_zod.z.literal("append"),
385
+ path: import_zod.z.string().min(1),
386
+ items: import_zod.z.array(import_zod.z.unknown())
261
387
  });
262
- var GenericStructuredDataChunkSchema = StructuredDataBase.extend({
263
- data: import_zod2.z.unknown()
388
+ var StructuredTextDeltaChunkSchema = StructuredDataBase.extend({
389
+ kind: import_zod.z.literal("text-delta"),
390
+ path: import_zod.z.string().min(1),
391
+ delta: import_zod.z.string()
264
392
  });
265
- var StructuredDataChunkSchema = import_zod2.z.union([
266
- JobsStructuredDataChunkSchema,
267
- GenericStructuredDataChunkSchema
393
+ var StructuredFinalChunkSchema = StructuredDataBase.extend({
394
+ kind: import_zod.z.literal("final"),
395
+ data: import_zod.z.unknown()
396
+ });
397
+ var StructuredDataChunkSchema = import_zod.z.union([
398
+ StructuredSetChunkSchema,
399
+ StructuredAppendChunkSchema,
400
+ StructuredTextDeltaChunkSchema,
401
+ StructuredFinalChunkSchema
268
402
  ]);
269
403
 
270
404
  // src/types/stream-chunk.ts
271
- var StreamChunkSchema = import_zod3.z.union([
272
- import_zod3.z.object({
273
- type: import_zod3.z.literal("session"),
274
- sessionId: import_zod3.z.string()
405
+ var StreamChunkSchema = import_zod2.z.union([
406
+ import_zod2.z.object({
407
+ type: import_zod2.z.literal("session"),
408
+ sessionId: import_zod2.z.string()
275
409
  }),
276
- import_zod3.z.object({
277
- type: import_zod3.z.literal("status"),
278
- message: import_zod3.z.string(),
279
- node: import_zod3.z.string().optional()
410
+ import_zod2.z.object({
411
+ type: import_zod2.z.literal("status"),
412
+ message: import_zod2.z.string(),
413
+ node: import_zod2.z.string().optional()
280
414
  }),
281
- import_zod3.z.object({
282
- type: import_zod3.z.literal("message"),
283
- content: import_zod3.z.string(),
284
- node: import_zod3.z.string().optional()
415
+ import_zod2.z.object({
416
+ type: import_zod2.z.literal("message"),
417
+ content: import_zod2.z.string(),
418
+ node: import_zod2.z.string().optional()
285
419
  }),
286
- import_zod3.z.object({
287
- type: import_zod3.z.literal("text-start"),
288
- node: import_zod3.z.string().optional(),
289
- id: import_zod3.z.string().optional(),
290
- opId: import_zod3.z.string().optional(),
291
- segmentId: import_zod3.z.string().optional()
420
+ import_zod2.z.object({
421
+ type: import_zod2.z.literal("text-start"),
422
+ node: import_zod2.z.string().optional(),
423
+ id: import_zod2.z.string().optional(),
424
+ opId: import_zod2.z.string().optional(),
425
+ segmentId: import_zod2.z.string().optional()
292
426
  }),
293
- import_zod3.z.object({
294
- type: import_zod3.z.literal("text-delta"),
295
- delta: import_zod3.z.string(),
296
- node: import_zod3.z.string().optional(),
297
- id: import_zod3.z.string().optional(),
298
- opId: import_zod3.z.string().optional(),
299
- segmentId: import_zod3.z.string().optional()
427
+ import_zod2.z.object({
428
+ type: import_zod2.z.literal("text-delta"),
429
+ delta: import_zod2.z.string(),
430
+ node: import_zod2.z.string().optional(),
431
+ id: import_zod2.z.string().optional(),
432
+ opId: import_zod2.z.string().optional(),
433
+ segmentId: import_zod2.z.string().optional()
300
434
  }),
301
- import_zod3.z.object({
302
- type: import_zod3.z.literal("text-end"),
303
- node: import_zod3.z.string().optional(),
304
- id: import_zod3.z.string().optional(),
305
- opId: import_zod3.z.string().optional(),
306
- segmentId: import_zod3.z.string().optional()
435
+ import_zod2.z.object({
436
+ type: import_zod2.z.literal("text-end"),
437
+ node: import_zod2.z.string().optional(),
438
+ id: import_zod2.z.string().optional(),
439
+ opId: import_zod2.z.string().optional(),
440
+ segmentId: import_zod2.z.string().optional()
307
441
  }),
308
- import_zod3.z.object({
309
- type: import_zod3.z.literal("tool-result"),
310
- tool: import_zod3.z.string().optional(),
311
- node: import_zod3.z.string().optional(),
312
- content: import_zod3.z.unknown()
442
+ import_zod2.z.object({
443
+ type: import_zod2.z.literal("tool-result"),
444
+ tool: import_zod2.z.string().optional(),
445
+ node: import_zod2.z.string().optional(),
446
+ content: import_zod2.z.unknown()
313
447
  }),
314
448
  // Interrupt request chunk (used to render interactive options in UI)
315
- import_zod3.z.object({
316
- type: import_zod3.z.literal("interrupt"),
317
- requestId: import_zod3.z.string(),
318
- resumeToken: import_zod3.z.string(),
319
- workflow: import_zod3.z.string().optional(),
320
- node: import_zod3.z.string().optional(),
321
- id: import_zod3.z.string().optional(),
322
- opId: import_zod3.z.string().optional(),
323
- schemaId: import_zod3.z.string().optional(),
324
- schemaVersion: import_zod3.z.string().optional(),
325
- input: import_zod3.z.discriminatedUnion("kind", [
449
+ import_zod2.z.object({
450
+ type: import_zod2.z.literal("interrupt"),
451
+ requestId: import_zod2.z.string(),
452
+ resumeToken: import_zod2.z.string(),
453
+ workflow: import_zod2.z.string().optional(),
454
+ node: import_zod2.z.string().optional(),
455
+ id: import_zod2.z.string().optional(),
456
+ opId: import_zod2.z.string().optional(),
457
+ schemaId: import_zod2.z.string().optional(),
458
+ schemaVersion: import_zod2.z.string().optional(),
459
+ input: import_zod2.z.discriminatedUnion("kind", [
326
460
  // Text input: question optional, no options
327
- import_zod3.z.object({
328
- kind: import_zod3.z.literal("text"),
329
- multiple: import_zod3.z.boolean(),
330
- question: import_zod3.z.string().optional(),
331
- id: import_zod3.z.string().optional(),
332
- schemaId: import_zod3.z.string().optional(),
333
- schemaVersion: import_zod3.z.string().optional(),
334
- meta: import_zod3.z.record(import_zod3.z.unknown()).optional(),
335
- options: import_zod3.z.array(import_zod3.z.never()).optional()
461
+ import_zod2.z.object({
462
+ kind: import_zod2.z.literal("text"),
463
+ multiple: import_zod2.z.boolean(),
464
+ question: import_zod2.z.string().optional(),
465
+ id: import_zod2.z.string().optional(),
466
+ schemaId: import_zod2.z.string().optional(),
467
+ schemaVersion: import_zod2.z.string().optional(),
468
+ meta: import_zod2.z.record(import_zod2.z.unknown()).optional(),
469
+ options: import_zod2.z.array(import_zod2.z.never()).optional()
336
470
  }),
337
471
  // Choice/multi-choice: question + options required
338
- import_zod3.z.object({
339
- kind: import_zod3.z.enum(["choice", "multi-choice"]),
340
- multiple: import_zod3.z.boolean(),
341
- question: import_zod3.z.string(),
342
- id: import_zod3.z.string().optional(),
343
- schemaId: import_zod3.z.string().optional(),
344
- schemaVersion: import_zod3.z.string().optional(),
345
- meta: import_zod3.z.record(import_zod3.z.unknown()).optional(),
346
- options: import_zod3.z.array(
347
- import_zod3.z.object({
348
- id: import_zod3.z.string(),
349
- label: import_zod3.z.string(),
350
- description: import_zod3.z.string().optional()
472
+ import_zod2.z.object({
473
+ kind: import_zod2.z.enum(["choice", "multi-choice"]),
474
+ multiple: import_zod2.z.boolean(),
475
+ question: import_zod2.z.string(),
476
+ id: import_zod2.z.string().optional(),
477
+ schemaId: import_zod2.z.string().optional(),
478
+ schemaVersion: import_zod2.z.string().optional(),
479
+ meta: import_zod2.z.record(import_zod2.z.unknown()).optional(),
480
+ options: import_zod2.z.array(
481
+ import_zod2.z.object({
482
+ id: import_zod2.z.string(),
483
+ label: import_zod2.z.string(),
484
+ description: import_zod2.z.string().optional()
351
485
  })
352
486
  )
353
487
  })
354
488
  ]),
355
- meta: import_zod3.z.record(import_zod3.z.unknown()).optional()
489
+ meta: import_zod2.z.record(import_zod2.z.unknown()).optional()
356
490
  }),
357
491
  StructuredDataChunkSchema,
358
- import_zod3.z.object({
359
- type: import_zod3.z.literal("transition"),
360
- transitionTo: import_zod3.z.string(),
361
- payload: import_zod3.z.any().optional()
492
+ import_zod2.z.object({
493
+ type: import_zod2.z.literal("transition"),
494
+ transitionTo: import_zod2.z.string(),
495
+ payload: import_zod2.z.any().optional()
362
496
  }),
363
- import_zod3.z.object({ type: import_zod3.z.literal("done"), data: import_zod3.z.any().optional() }),
364
- import_zod3.z.object({ type: import_zod3.z.literal("error"), message: import_zod3.z.string() })
497
+ import_zod2.z.object({ type: import_zod2.z.literal("done"), data: import_zod2.z.any().optional() }),
498
+ import_zod2.z.object({ type: import_zod2.z.literal("error"), message: import_zod2.z.string() })
365
499
  ]);
366
500
  // Annotate the CommonJS export names for ESM import in node:
367
501
  0 && (module.exports = {
368
502
  STREAM_HEADERS,
369
503
  StreamChunkSchema,
504
+ StructuredDataChunkSchema,
505
+ applyStructuredChunk,
370
506
  collectBufferedStream,
371
507
  collectStream,
372
508
  consumeStream,
373
509
  createStreamResponse,
510
+ createStructuredStreamAccumulator,
374
511
  readStream,
512
+ reduceStructuredChunks,
375
513
  streamFromRoute,
376
514
  summarizeStreamChunks,
377
515
  toSSE