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.
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProgressiveValidator = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Navigate a Zod schema to a sub-schema by dot/bracket path.
7
+ * e.g. path "address.city" on z.object({address: z.object({city: z.string()})})
8
+ * returns the z.string() schema.
9
+ * Returns null if the path can't be resolved.
10
+ */
11
+ function getSubSchema(schema, path) {
12
+ if (!path)
13
+ return schema;
14
+ // Unwrap optional/nullable/default wrappers
15
+ let s = schema;
16
+ while (s instanceof zod_1.z.ZodOptional ||
17
+ s instanceof zod_1.z.ZodNullable ||
18
+ s instanceof zod_1.z.ZodDefault) {
19
+ s = s._def.innerType;
20
+ }
21
+ const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.');
22
+ const [head, ...rest] = parts;
23
+ const restPath = rest.join('.');
24
+ if (s instanceof zod_1.z.ZodObject) {
25
+ const shape = s.shape;
26
+ if (!(head in shape))
27
+ return null;
28
+ return getSubSchema(shape[head], restPath);
29
+ }
30
+ if (s instanceof zod_1.z.ZodArray) {
31
+ // head is an array index — recurse into element type
32
+ return getSubSchema(s._def.type, restPath);
33
+ }
34
+ return null;
35
+ }
36
+ /**
37
+ * Set a value at a dot/bracket path in a nested object (mutates).
38
+ */
39
+ function setAtPath(obj, path, value) {
40
+ const parts = path.replace(/\[(\d+)\]/g, '.$1').split('.');
41
+ let cur = obj;
42
+ for (let i = 0; i < parts.length - 1; i++) {
43
+ const k = parts[i];
44
+ if (cur[k] === undefined || cur[k] === null || typeof cur[k] !== 'object') {
45
+ cur[k] = {};
46
+ }
47
+ cur = cur[k];
48
+ }
49
+ cur[parts[parts.length - 1]] = value;
50
+ }
51
+ /**
52
+ * Collect all required field paths from a Zod schema.
53
+ */
54
+ function collectRequiredPaths(schema, prefix = '') {
55
+ let s = schema;
56
+ const isOptional = s instanceof zod_1.z.ZodOptional;
57
+ while (s instanceof zod_1.z.ZodOptional ||
58
+ s instanceof zod_1.z.ZodNullable ||
59
+ s instanceof zod_1.z.ZodDefault) {
60
+ s = s._def.innerType;
61
+ }
62
+ if (s instanceof zod_1.z.ZodObject) {
63
+ const paths = [];
64
+ const shape = s.shape;
65
+ for (const [k, v] of Object.entries(shape)) {
66
+ const childPaths = collectRequiredPaths(v, prefix ? `${prefix}.${k}` : k);
67
+ paths.push(...childPaths);
68
+ }
69
+ return paths;
70
+ }
71
+ // Leaf node
72
+ if (isOptional)
73
+ return [];
74
+ return prefix ? [prefix] : [];
75
+ }
76
+ class ProgressiveValidator {
77
+ schema;
78
+ data = {};
79
+ meta = {};
80
+ seq = 0;
81
+ startTime = Date.now();
82
+ requiredPaths;
83
+ failedPaths = [];
84
+ constructor(schema) {
85
+ this.schema = schema;
86
+ this.requiredPaths = collectRequiredPaths(schema);
87
+ }
88
+ applyField(path, value, options) {
89
+ const strategy = options?.strategy ?? 'skip';
90
+ const subSchema = getSubSchema(this.schema, path);
91
+ let status = 'complete';
92
+ let validationError;
93
+ if (subSchema) {
94
+ const result = subSchema.safeParse(value);
95
+ if (!result.success) {
96
+ const message = result.error.errors.map(e => e.message).join('; ');
97
+ validationError = {
98
+ path,
99
+ value,
100
+ message,
101
+ elapsedMs: Date.now() - this.startTime,
102
+ };
103
+ status = 'error';
104
+ this.failedPaths.push(path);
105
+ if (strategy === 'skip') {
106
+ // Don't set the value, mark as error and return
107
+ this.meta[path] = 'error';
108
+ return { partial: this.buildPartial(), error: validationError };
109
+ }
110
+ else if (strategy === 'error') {
111
+ this.meta[path] = 'error';
112
+ return { partial: this.buildPartial(), error: validationError };
113
+ }
114
+ // include-invalid: fall through and set value anyway
115
+ }
116
+ }
117
+ setAtPath(this.data, path, value);
118
+ this.meta[path] = status;
119
+ this.seq++;
120
+ return { partial: this.buildPartial(), error: validationError };
121
+ }
122
+ isComplete() {
123
+ if (this.requiredPaths.length === 0) {
124
+ // No required paths detected — check if we have any data
125
+ return Object.keys(this.meta).length > 0;
126
+ }
127
+ return this.requiredPaths.every(p => this.meta[p] === 'complete');
128
+ }
129
+ getFailedPaths() {
130
+ return this.failedPaths.slice();
131
+ }
132
+ getRequiredPaths() {
133
+ return this.requiredPaths.slice();
134
+ }
135
+ buildPartial() {
136
+ return {
137
+ data: this.data,
138
+ meta: { ...this.meta },
139
+ isComplete: this.isComplete(),
140
+ seq: this.seq,
141
+ elapsedMs: Date.now() - this.startTime,
142
+ };
143
+ }
144
+ }
145
+ exports.ProgressiveValidator = ProgressiveValidator;
146
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAGvB;;;;;GAKG;AACH,SAAS,YAAY,CAAC,MAAoB,EAAE,IAAY;IACtD,IAAI,CAAC,IAAI;QAAE,OAAO,MAAM,CAAA;IAExB,4CAA4C;IAC5C,IAAI,CAAC,GAAiB,MAAM,CAAA;IAC5B,OACE,CAAC,YAAY,OAAC,CAAC,WAAW;QAC1B,CAAC,YAAY,OAAC,CAAC,WAAW;QAC1B,CAAC,YAAY,OAAC,CAAC,UAAU,EACzB,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAyB,CAAA;IACtC,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1D,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAA;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAE/B,IAAI,CAAC,YAAY,OAAC,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAqC,CAAA;QACrD,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACjC,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,OAAC,CAAC,QAAQ,EAAE,CAAC;QAC5B,qDAAqD;QACrD,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAoB,EAAE,QAAQ,CAAC,CAAA;IAC5D,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,GAA4B,EAAE,IAAY,EAAE,KAAc;IAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1D,IAAI,GAAG,GAA4B,GAAG,CAAA;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAClB,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1E,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;QACb,CAAC;QACD,GAAG,GAAG,GAAG,CAAC,CAAC,CAA4B,CAAA;IACzC,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;AACtC,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAAoB,EAAE,MAAM,GAAG,EAAE;IAC7D,IAAI,CAAC,GAAiB,MAAM,CAAA;IAC5B,MAAM,UAAU,GAAG,CAAC,YAAY,OAAC,CAAC,WAAW,CAAA;IAC7C,OACE,CAAC,YAAY,OAAC,CAAC,WAAW;QAC1B,CAAC,YAAY,OAAC,CAAC,WAAW;QAC1B,CAAC,YAAY,OAAC,CAAC,UAAU,EACzB,CAAC;QACD,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAyB,CAAA;IACtC,CAAC;IAED,IAAI,CAAC,YAAY,OAAC,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAqC,CAAA;QACrD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,MAAM,UAAU,GAAG,oBAAoB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACzE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAA;QAC3B,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,YAAY;IACZ,IAAI,UAAU;QAAE,OAAO,EAAE,CAAA;IACzB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC/B,CAAC;AAED,MAAa,oBAAoB;IAQX;IAPZ,IAAI,GAA4B,EAAE,CAAA;IAClC,IAAI,GAAc,EAAE,CAAA;IACpB,GAAG,GAAG,CAAC,CAAA;IACP,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,aAAa,CAAU;IACvB,WAAW,GAAa,EAAE,CAAA;IAElC,YAAoB,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;QACxC,IAAI,CAAC,aAAa,GAAG,oBAAoB,CAAC,MAAsB,CAAC,CAAA;IACnE,CAAC;IAED,UAAU,CACR,IAAY,EACZ,KAAc,EACd,OAA6D;QAE7D,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,MAAM,CAAA;QAC5C,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,MAAsB,EAAE,IAAI,CAAC,CAAA;QACjE,IAAI,MAAM,GAAgB,UAAU,CAAA;QACpC,IAAI,eAAkD,CAAA;QAEtD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAClE,eAAe,GAAG;oBAChB,IAAI;oBACJ,KAAK;oBACL,OAAO;oBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;iBACvC,CAAA;gBACD,MAAM,GAAG,OAAO,CAAA;gBAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAE3B,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;oBACxB,gDAAgD;oBAChD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;oBACzB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;gBACjE,CAAC;qBAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;oBAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;oBACzB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;gBACjE,CAAC;gBACD,qDAAqD;YACvD,CAAC;QACH,CAAC;QAED,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;QACjC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAA;QACxB,IAAI,CAAC,GAAG,EAAE,CAAA;QAEV,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;IACjE,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,yDAAyD;YACzD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;QAC1C,CAAC;QACD,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAA;IACnE,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;IACjC,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;IACnC,CAAC;IAED,YAAY;QACV,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAwC;YACnD,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE;YACtB,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;YAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;SACvC,CAAA;IACH,CAAC;CACF;AA/ED,oDA+EC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "stream-validate",
3
+ "version": "0.1.0",
4
+ "description": "Progressive Zod validation for streaming LLM responses",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "test": "vitest run",
13
+ "lint": "eslint src/",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "dependencies": {
26
+ "zod": "^3.23.8"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^25.5.0",
30
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
31
+ "@typescript-eslint/parser": "^8.57.1",
32
+ "eslint": "^10.1.0",
33
+ "typescript": "^5.9.3",
34
+ "vitest": "^4.1.0"
35
+ }
36
+ }