stream-validate 0.1.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/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # stream-validate
2
+
3
+ Progressive Zod validation for streaming LLM responses. Parse and validate JSON incrementally as it arrives chunk-by-chunk, emitting typed partial results as each field completes.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install stream-validate zod
9
+ ```
10
+
11
+ ## Quick Start — `streamValidate` async generator
12
+
13
+ ```typescript
14
+ import { z } from 'zod'
15
+ import { streamValidate } from 'stream-validate'
16
+
17
+ const schema = z.object({
18
+ name: z.string(),
19
+ age: z.number(),
20
+ address: z.object({ city: z.string() }),
21
+ })
22
+
23
+ // Any AsyncIterable<string> — e.g. an LLM streaming response
24
+ async function* llmStream(): AsyncIterable<string> {
25
+ yield '{"name":"Al'
26
+ yield 'ice","age":30,"address":{"city":"NYC"}}'
27
+ }
28
+
29
+ for await (const partial of streamValidate(llmStream(), schema)) {
30
+ console.log(partial.data) // DeepPartial<T> — fields filled in as they arrive
31
+ console.log(partial.meta) // { "name": "complete", "age": "pending", ... }
32
+ console.log(partial.isComplete) // true when all required fields are validated
33
+ console.log(partial.seq) // monotonically increasing sequence number
34
+ console.log(partial.elapsedMs) // ms since validation started
35
+ }
36
+ ```
37
+
38
+ ## Push-based API — `createStreamValidator`
39
+
40
+ ```typescript
41
+ import { z } from 'zod'
42
+ import { createStreamValidator } from 'stream-validate'
43
+
44
+ const schema = z.object({ name: z.string(), score: z.number() })
45
+
46
+ const validator = createStreamValidator(schema, {
47
+ validationErrorStrategy: 'skip', // 'skip' | 'include-invalid' | 'error'
48
+ timeoutMs: 5000,
49
+ onValidationError: (err) => console.error('Invalid field', err.path, err.message),
50
+ onParseError: (err) => console.error('Parse error', err.message),
51
+ })
52
+
53
+ // Subscribe to events
54
+ const unsub = validator.on('partial', (partial) => {
55
+ console.log('Got partial:', partial.data)
56
+ })
57
+
58
+ validator.on('complete', (event) => {
59
+ console.log('Done:', event.isComplete, event.truncated, event.totalMs + 'ms')
60
+ console.log('Failed paths:', event.failedPaths)
61
+ })
62
+
63
+ // Push chunks as they arrive
64
+ validator.write('{"name":"Bob"')
65
+ validator.write(',"score":95}')
66
+ validator.end() // flush and emit final complete event
67
+
68
+ // Inspect current state at any time
69
+ console.log(validator.current?.data)
70
+
71
+ // Unsubscribe
72
+ unsub()
73
+
74
+ // Abort mid-stream (emits complete with truncated=true)
75
+ validator.abort()
76
+ ```
77
+
78
+ ### Async iterator on validator
79
+
80
+ ```typescript
81
+ const validator = createStreamValidator(schema)
82
+
83
+ ;(async () => {
84
+ for await (const partial of validator) {
85
+ console.log(partial.data)
86
+ }
87
+ })()
88
+
89
+ validator.write('{"name":"Charlie","score":88}')
90
+ validator.end()
91
+ ```
92
+
93
+ ## API
94
+
95
+ ### `streamValidate<T>(stream, schema, options?)`
96
+
97
+ | Parameter | Type | Description |
98
+ |---|---|---|
99
+ | `stream` | `AsyncIterable<string>` | Source of JSON chunks |
100
+ | `schema` | `z.ZodSchema<T>` | Zod schema to validate against |
101
+ | `options` | `StreamValidatorOptions` | Optional configuration |
102
+
103
+ Returns `AsyncIterable<ValidatedPartial<T>>`.
104
+
105
+ ### `createStreamValidator<T>(schema, options?)`
106
+
107
+ Returns a `StreamValidator<T>` with:
108
+
109
+ - `write(chunk: string)` — push a new chunk
110
+ - `end()` — signal end of stream
111
+ - `abort(error?)` — cancel with truncated=true
112
+ - `on('partial', fn)` — subscribe to partial updates; returns unsubscribe function
113
+ - `on('complete', fn)` — subscribe to final completion event
114
+ - `on('parse-error', fn)` — subscribe to JSON parse errors
115
+ - `on('validation-error', fn)` — subscribe to Zod validation errors
116
+ - `current` — getter for the latest `ValidatedPartial<T>` or `null`
117
+ - `[Symbol.asyncIterator]()` — async iterate over partials
118
+
119
+ ### `StreamValidatorOptions`
120
+
121
+ ```typescript
122
+ interface StreamValidatorOptions {
123
+ onParseError?: (err: StreamParseError) => void
124
+ onValidationError?: (err: StreamValidationError) => void
125
+ validationErrorStrategy?: 'skip' | 'include-invalid' | 'error'
126
+ timeoutMs?: number
127
+ signal?: AbortSignal
128
+ }
129
+ ```
130
+
131
+ ### `ValidatedPartial<T>`
132
+
133
+ ```typescript
134
+ interface ValidatedPartial<T> {
135
+ data: DeepPartial<T> // partial object filled in so far
136
+ meta: FieldMeta // per-field status: 'complete' | 'active' | 'pending' | 'error'
137
+ isComplete: boolean // true when all required fields validated
138
+ seq: number // monotonically increasing
139
+ elapsedMs: number // ms since validator created
140
+ }
141
+ ```
142
+
143
+ ## License
144
+
145
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/parser.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const parser_1 = require("../parser");
5
+ (0, vitest_1.describe)('IncrementalJsonParser', () => {
6
+ (0, vitest_1.it)('parses a simple object fed in 3 chunks', () => {
7
+ const parser = new parser_1.IncrementalJsonParser();
8
+ const fields = [
9
+ ...parser.feed('{"na'),
10
+ ...parser.feed('me":"Al'),
11
+ ...parser.feed('ice"}'),
12
+ ];
13
+ const nameField = fields.find(f => f.path === 'name');
14
+ (0, vitest_1.expect)(nameField).toBeDefined();
15
+ (0, vitest_1.expect)(nameField?.value).toBe('Alice');
16
+ });
17
+ (0, vitest_1.it)('parses two top-level fields from a single feed', () => {
18
+ const parser = new parser_1.IncrementalJsonParser();
19
+ const fields = parser.feed('{"a":1,"b":2}');
20
+ (0, vitest_1.expect)(fields.find(f => f.path === 'a')?.value).toBe(1);
21
+ (0, vitest_1.expect)(fields.find(f => f.path === 'b')?.value).toBe(2);
22
+ });
23
+ (0, vitest_1.it)('parses nested object and emits path for leaf field', () => {
24
+ const parser = new parser_1.IncrementalJsonParser();
25
+ const fields = parser.feed('{"addr":{"city":"NYC"}}');
26
+ (0, vitest_1.expect)(fields.find(f => f.path === 'addr.city')?.value).toBe('NYC');
27
+ });
28
+ (0, vitest_1.it)('parses boolean and null values', () => {
29
+ const parser = new parser_1.IncrementalJsonParser();
30
+ const fields = parser.feed('{"flag":true,"empty":null}');
31
+ (0, vitest_1.expect)(fields.find(f => f.path === 'flag')?.value).toBe(true);
32
+ (0, vitest_1.expect)(fields.find(f => f.path === 'empty')?.value).toBeNull();
33
+ });
34
+ (0, vitest_1.it)('parses numeric values', () => {
35
+ const parser = new parser_1.IncrementalJsonParser();
36
+ const fields = parser.feed('{"count":42,"ratio":3.14}');
37
+ (0, vitest_1.expect)(fields.find(f => f.path === 'count')?.value).toBe(42);
38
+ (0, vitest_1.expect)(fields.find(f => f.path === 'ratio')?.value).toBeCloseTo(3.14);
39
+ });
40
+ (0, vitest_1.it)('handles chunk boundary mid-key', () => {
41
+ const parser = new parser_1.IncrementalJsonParser();
42
+ const f1 = parser.feed('{"foo');
43
+ (0, vitest_1.expect)(f1).toHaveLength(0); // key incomplete
44
+ const f2 = parser.feed('":"bar"}');
45
+ const field = f2.find(f => f.path === 'foo');
46
+ (0, vitest_1.expect)(field?.value).toBe('bar');
47
+ });
48
+ (0, vitest_1.it)('handles chunk boundary mid-value', () => {
49
+ const parser = new parser_1.IncrementalJsonParser();
50
+ const f1 = parser.feed('{"x":"hel');
51
+ (0, vitest_1.expect)(f1).toHaveLength(0);
52
+ const f2 = parser.feed('lo"}');
53
+ (0, vitest_1.expect)(f2.find(f => f.path === 'x')?.value).toBe('hello');
54
+ });
55
+ (0, vitest_1.it)('end() flushes remaining content', () => {
56
+ const parser = new parser_1.IncrementalJsonParser();
57
+ parser.feed('{"z":99');
58
+ const fields = parser.end();
59
+ // After end with a complete-enough buffer
60
+ // The end() may or may not extract depending on completeness
61
+ // At minimum it should not throw
62
+ (0, vitest_1.expect)(Array.isArray(fields)).toBe(true);
63
+ });
64
+ (0, vitest_1.it)('emits position > 0 for fields', () => {
65
+ const parser = new parser_1.IncrementalJsonParser();
66
+ const fields = parser.feed('{"name":"Bob"}');
67
+ const field = fields.find(f => f.path === 'name');
68
+ (0, vitest_1.expect)(field).toBeDefined();
69
+ (0, vitest_1.expect)(field.position).toBeGreaterThan(0);
70
+ });
71
+ (0, vitest_1.it)('parses string with escaped quotes', () => {
72
+ const parser = new parser_1.IncrementalJsonParser();
73
+ const fields = parser.feed('{"msg":"say \\"hi\\""}');
74
+ (0, vitest_1.expect)(fields.find(f => f.path === 'msg')?.value).toBe('say "hi"');
75
+ });
76
+ });
77
+ //# sourceMappingURL=parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/__tests__/parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stream-validate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-validate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/stream-validate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const zod_1 = require("zod");
5
+ const stream_validator_1 = require("../stream-validator");
6
+ const stream_validate_1 = require("../stream-validate");
7
+ // Helper: split string into individual characters as async iterable
8
+ async function* charStream(s) {
9
+ for (const ch of s) {
10
+ yield ch;
11
+ }
12
+ }
13
+ // Helper: split string into chunks of given size
14
+ async function* chunkStream(s, size) {
15
+ for (let i = 0; i < s.length; i += size) {
16
+ yield s.slice(i, i + size);
17
+ }
18
+ }
19
+ (0, vitest_1.describe)('createStreamValidator', () => {
20
+ (0, vitest_1.it)('emits partial events as fields complete', () => {
21
+ const schema = zod_1.z.object({ name: zod_1.z.string() });
22
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
23
+ const partials = [];
24
+ validator.on('partial', p => partials.push(p));
25
+ validator.write('{"name":"Alice"}');
26
+ validator.end();
27
+ const namePartial = partials.find(p => p?.data && p.data.name === 'Alice');
28
+ (0, vitest_1.expect)(namePartial).toBeDefined();
29
+ });
30
+ (0, vitest_1.it)('emits complete event after end()', () => {
31
+ const schema = zod_1.z.object({ x: zod_1.z.number() });
32
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
33
+ const completeEvents = [];
34
+ validator.on('complete', e => completeEvents.push(e));
35
+ validator.write('{"x":42}');
36
+ validator.end();
37
+ (0, vitest_1.expect)(completeEvents).toHaveLength(1);
38
+ const evt = completeEvents[0];
39
+ (0, vitest_1.expect)(evt.truncated).toBe(false);
40
+ });
41
+ (0, vitest_1.it)('reports isComplete=true after all required fields are received', () => {
42
+ const schema = zod_1.z.object({ name: zod_1.z.string(), age: zod_1.z.number() });
43
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
44
+ const partials = [];
45
+ validator.on('partial', p => partials.push(p));
46
+ validator.write('{"name":"Bob","age":30}');
47
+ validator.end();
48
+ const last = partials[partials.length - 1];
49
+ (0, vitest_1.expect)(last?.isComplete).toBe(true);
50
+ });
51
+ (0, vitest_1.it)('current getter returns latest partial', () => {
52
+ const schema = zod_1.z.object({ val: zod_1.z.string() });
53
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
54
+ (0, vitest_1.expect)(validator.current).toBeNull();
55
+ validator.write('{"val":"test"}');
56
+ (0, vitest_1.expect)(validator.current).not.toBeNull();
57
+ (0, vitest_1.expect)(validator.current.data.val).toBe('test');
58
+ });
59
+ (0, vitest_1.it)('abort() emits complete with truncated=true', () => {
60
+ const schema = zod_1.z.object({ name: zod_1.z.string() });
61
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
62
+ const completeEvents = [];
63
+ validator.on('complete', e => completeEvents.push(e));
64
+ validator.write('{"na');
65
+ validator.abort();
66
+ (0, vitest_1.expect)(completeEvents[0]?.truncated).toBe(true);
67
+ });
68
+ (0, vitest_1.it)('emits validation-error for wrong type with include-invalid strategy', () => {
69
+ const schema = zod_1.z.object({ count: zod_1.z.number() });
70
+ const validationErrors = [];
71
+ const validator = (0, stream_validator_1.createStreamValidator)(schema, {
72
+ validationErrorStrategy: 'include-invalid',
73
+ onValidationError: e => validationErrors.push(e),
74
+ });
75
+ validator.write('{"count":"not-a-number"}');
76
+ validator.end();
77
+ (0, vitest_1.expect)(validationErrors.length).toBeGreaterThan(0);
78
+ });
79
+ (0, vitest_1.it)('invokes onParseError callback for malformed JSON context', () => {
80
+ // The parser is resilient but we can test the callback is wired
81
+ const schema = zod_1.z.object({ x: zod_1.z.number() });
82
+ const onParseError = vitest_1.vi.fn();
83
+ const validator = (0, stream_validator_1.createStreamValidator)(schema, { onParseError });
84
+ // Feed valid JSON — no parse error
85
+ validator.write('{"x":1}');
86
+ validator.end();
87
+ (0, vitest_1.expect)(onParseError).not.toHaveBeenCalled();
88
+ });
89
+ (0, vitest_1.it)('async iterator yields partials', async () => {
90
+ const schema = zod_1.z.object({ a: zod_1.z.string(), b: zod_1.z.number() });
91
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
92
+ const results = [];
93
+ const iterPromise = (async () => {
94
+ for await (const partial of validator) {
95
+ results.push(partial);
96
+ }
97
+ })();
98
+ validator.write('{"a":"hello","b":99}');
99
+ validator.end();
100
+ await iterPromise;
101
+ (0, vitest_1.expect)(results.length).toBeGreaterThan(0);
102
+ });
103
+ (0, vitest_1.it)('on() returns unsubscribe function', () => {
104
+ const schema = zod_1.z.object({ x: zod_1.z.string() });
105
+ const validator = (0, stream_validator_1.createStreamValidator)(schema);
106
+ const calls = [];
107
+ const unsub = validator.on('partial', p => calls.push(p));
108
+ validator.write('{"x":');
109
+ unsub();
110
+ validator.write('"hello"}');
111
+ validator.end();
112
+ // After unsub, no more events
113
+ (0, vitest_1.expect)(calls).toHaveLength(0);
114
+ });
115
+ (0, vitest_1.it)('timeoutMs causes truncated complete event', async () => {
116
+ const schema = zod_1.z.object({ slow: zod_1.z.string() });
117
+ const completeEvents = [];
118
+ const validator = (0, stream_validator_1.createStreamValidator)(schema, { timeoutMs: 10 });
119
+ validator.on('complete', e => completeEvents.push(e));
120
+ // Don't write anything — let timeout fire
121
+ await new Promise(r => setTimeout(r, 50));
122
+ (0, vitest_1.expect)(completeEvents[0]?.truncated).toBe(true);
123
+ });
124
+ });
125
+ (0, vitest_1.describe)('streamValidate', () => {
126
+ (0, vitest_1.it)('yields partials for single-field schema char by char', async () => {
127
+ const schema = zod_1.z.object({ name: zod_1.z.string() });
128
+ const input = '{"name":"Alice"}';
129
+ const results = [];
130
+ for await (const partial of (0, stream_validate_1.streamValidate)(charStream(input), schema)) {
131
+ results.push(partial);
132
+ }
133
+ const withName = results.find(r => r.data.name === 'Alice');
134
+ (0, vitest_1.expect)(withName).toBeDefined();
135
+ });
136
+ (0, vitest_1.it)('handles multi-field schema with chunked input', async () => {
137
+ const schema = zod_1.z.object({ first: zod_1.z.string(), last: zod_1.z.string() });
138
+ const input = '{"first":"John","last":"Doe"}';
139
+ const results = [];
140
+ for await (const partial of (0, stream_validate_1.streamValidate)(chunkStream(input, 5), schema)) {
141
+ results.push(partial);
142
+ }
143
+ const complete = results.find(r => r.isComplete);
144
+ (0, vitest_1.expect)(complete).toBeDefined();
145
+ });
146
+ (0, vitest_1.it)('yields seq numbers that increment', async () => {
147
+ const schema = zod_1.z.object({ a: zod_1.z.string(), b: zod_1.z.string() });
148
+ const input = '{"a":"x","b":"y"}';
149
+ const seqs = [];
150
+ for await (const partial of (0, stream_validate_1.streamValidate)(charStream(input), schema)) {
151
+ seqs.push(partial.seq);
152
+ }
153
+ // seqs should be non-empty and monotonically increasing
154
+ (0, vitest_1.expect)(seqs.length).toBeGreaterThan(0);
155
+ for (let i = 1; i < seqs.length; i++) {
156
+ (0, vitest_1.expect)(seqs[i]).toBeGreaterThanOrEqual(seqs[i - 1]);
157
+ }
158
+ });
159
+ (0, vitest_1.it)('elapsedMs is a non-negative number', async () => {
160
+ const schema = zod_1.z.object({ v: zod_1.z.number() });
161
+ const input = '{"v":7}';
162
+ for await (const partial of (0, stream_validate_1.streamValidate)(charStream(input), schema)) {
163
+ (0, vitest_1.expect)(partial.elapsedMs).toBeGreaterThanOrEqual(0);
164
+ }
165
+ });
166
+ (0, vitest_1.it)('meta marks received fields as complete', async () => {
167
+ const schema = zod_1.z.object({ city: zod_1.z.string() });
168
+ const input = '{"city":"Paris"}';
169
+ let lastPartial = null;
170
+ for await (const partial of (0, stream_validate_1.streamValidate)(charStream(input), schema)) {
171
+ lastPartial = partial;
172
+ }
173
+ (0, vitest_1.expect)(lastPartial?.meta?.city).toBe('complete');
174
+ });
175
+ (0, vitest_1.it)('works with nested schema', async () => {
176
+ const schema = zod_1.z.object({
177
+ user: zod_1.z.object({
178
+ name: zod_1.z.string(),
179
+ age: zod_1.z.number(),
180
+ }),
181
+ });
182
+ const input = '{"user":{"name":"Eve","age":25}}';
183
+ const results = [];
184
+ for await (const partial of (0, stream_validate_1.streamValidate)(chunkStream(input, 4), schema)) {
185
+ results.push(partial);
186
+ }
187
+ const withUser = results.find(r => r.data.user?.name === 'Eve');
188
+ (0, vitest_1.expect)(withUser).toBeDefined();
189
+ });
190
+ (0, vitest_1.it)('passes options through to validator', async () => {
191
+ const schema = zod_1.z.object({ n: zod_1.z.number() });
192
+ const validationErrors = [];
193
+ const input = '{"n":"not-a-number"}';
194
+ for await (const _partial of (0, stream_validate_1.streamValidate)(charStream(input), schema, {
195
+ validationErrorStrategy: 'include-invalid',
196
+ onValidationError: e => validationErrors.push(e),
197
+ })) {
198
+ // consume
199
+ }
200
+ (0, vitest_1.expect)(validationErrors.length).toBeGreaterThan(0);
201
+ });
202
+ });
203
+ //# sourceMappingURL=stream-validate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-validate.test.js","sourceRoot":"","sources":["../../src/__tests__/stream-validate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ type Listener<T> = (payload: T) => void;
2
+ export declare class TypedEmitter<Events extends Record<string, unknown>> {
3
+ private listeners;
4
+ on<K extends keyof Events & string>(event: K, fn: Listener<Events[K]>): () => void;
5
+ off<K extends keyof Events & string>(event: K, fn: Listener<Events[K]>): void;
6
+ emit<K extends keyof Events & string>(event: K, payload: Events[K]): void;
7
+ }
8
+ export {};
9
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAA;AAEvC,qBAAa,YAAY,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,OAAO,CAAC,SAAS,CAAwD;IAEzE,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAMlF,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAO7E,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;CAO1E"}
package/dist/events.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEmitter = void 0;
4
+ class TypedEmitter {
5
+ listeners = {};
6
+ on(event, fn) {
7
+ if (!this.listeners[event])
8
+ this.listeners[event] = [];
9
+ this.listeners[event].push(fn);
10
+ return () => this.off(event, fn);
11
+ }
12
+ off(event, fn) {
13
+ const arr = this.listeners[event];
14
+ if (!arr)
15
+ return;
16
+ const idx = arr.indexOf(fn);
17
+ if (idx !== -1)
18
+ arr.splice(idx, 1);
19
+ }
20
+ emit(event, payload) {
21
+ const arr = this.listeners[event];
22
+ if (!arr)
23
+ return;
24
+ for (const fn of arr.slice()) {
25
+ fn(payload);
26
+ }
27
+ }
28
+ }
29
+ exports.TypedEmitter = TypedEmitter;
30
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":";;;AAEA,MAAa,YAAY;IACf,SAAS,GAAsD,EAAE,CAAA;IAEzE,EAAE,CAAkC,KAAQ,EAAE,EAAuB;QACnE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QACtD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,EAAuB,CAAC,CAAA;QACpD,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAClC,CAAC;IAED,GAAG,CAAkC,KAAQ,EAAE,EAAuB;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAuB,CAAC,CAAA;QAChD,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,CAAkC,KAAQ,EAAE,OAAkB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;YAC7B,EAAE,CAAC,OAAO,CAAC,CAAA;QACb,CAAC;IACH,CAAC;CACF;AAvBD,oCAuBC"}
@@ -0,0 +1,4 @@
1
+ export { createStreamValidator } from './stream-validator';
2
+ export { streamValidate } from './stream-validate';
3
+ export type { DeepPartial, FieldStatus, FieldMeta, ValidatedPartial, StreamCompletionEvent, StreamValidationError, StreamParseError, StreamValidatorOptions, StreamValidator, } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,YAAY,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,GAChB,MAAM,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.streamValidate = exports.createStreamValidator = void 0;
4
+ // stream-validate - Progressive Zod validation for streaming LLM responses
5
+ var stream_validator_1 = require("./stream-validator");
6
+ Object.defineProperty(exports, "createStreamValidator", { enumerable: true, get: function () { return stream_validator_1.createStreamValidator; } });
7
+ var stream_validate_1 = require("./stream-validate");
8
+ Object.defineProperty(exports, "streamValidate", { enumerable: true, get: function () { return stream_validate_1.streamValidate; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2EAA2E;AAC3E,uDAA0D;AAAjD,yHAAA,qBAAqB,OAAA;AAC9B,qDAAkD;AAAzC,iHAAA,cAAc,OAAA"}
@@ -0,0 +1,40 @@
1
+ export interface ParsedField {
2
+ path: string;
3
+ value: unknown;
4
+ position: number;
5
+ }
6
+ /**
7
+ * Incremental JSON parser that emits ParsedField events as object fields complete.
8
+ * Uses a buffering + scanning approach: buffer incoming chunks, scan for complete
9
+ * JSON values using balanced delimiter counting.
10
+ */
11
+ export declare class IncrementalJsonParser {
12
+ private buf;
13
+ private streamPos;
14
+ private scanPos;
15
+ feed(chunk: string): ParsedField[];
16
+ end(): ParsedField[];
17
+ private insideObject;
18
+ private scan;
19
+ private skipWhitespace;
20
+ /**
21
+ * Parse a JSON string starting at pos (which must be `"`).
22
+ * Returns { value, endPos } where endPos is one past the closing `"`, or null if incomplete.
23
+ */
24
+ private parseString;
25
+ /**
26
+ * Parse any JSON value starting at pos.
27
+ * Returns { value, endPos } or null if incomplete.
28
+ */
29
+ private parseValue;
30
+ /**
31
+ * Parse a balanced `{...}` or `[...]` block.
32
+ */
33
+ private parseBalanced;
34
+ /**
35
+ * Recursively extract leaf fields from a (possibly nested) object/value,
36
+ * emitting one ParsedField per leaf.
37
+ */
38
+ private extractFields;
39
+ }
40
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;GAIG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,OAAO,CAAI;IAEnB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE;IAMlC,GAAG,IAAI,WAAW,EAAE;IAepB,OAAO,CAAC,YAAY,CAAQ;IAE5B,OAAO,CAAC,IAAI;IA4EZ,OAAO,CAAC,cAAc;IAKtB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAqBnB;;;OAGG;IACH,OAAO,CAAC,UAAU;IA+ClB;;OAEG;IACH,OAAO,CAAC,aAAa;IAgCrB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAkCtB"}