@stdout-design/core 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +4 -1368
- package/dist/schema.mjs +1 -2
- package/dist/validation-BzP0M2-C.mjs +1 -0
- package/package.json +1 -1
- package/dist/validation-CMTs74Vm.mjs +0 -202
package/dist/schema.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export { defineSchema };
|
|
1
|
+
import{n as defineSchema}from"./validation-BzP0M2-C.mjs";export{defineSchema};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z}from"zod";const defineSchema=schema=>schema;var PropValidationError=class extends Error{issues;constructor(issues){super(`Invalid props: ${issues.map(i=>`${i.field||`(root)`} - ${i.message}${i.suggestion?`. Suggestion: ${i.suggestion}`:``}`).join(`; `)}`),this.name=`PropValidationError`,this.issues=issues}};const isObject=schema=>schema.type===`object`,isArray=schema=>schema.type===`array`,isEnum=schema=>schema.type===`enum`,isUnion=schema=>schema.type===`union`,isZodString=schema=>schema.type===`string`,isWrappable=schema=>schema.type==="default"||schema.type===`optional`||schema.type===`nullable`,resolveFieldSchema=(schema,path)=>{let current=schema;for(let i=0;i<path.length;i+=1){let segment=path[i];if(i!==path.length-1&&isWrappable(current)&&(current=current.unwrap()),typeof segment==`number`){if(isArray(current)){current=current.element;continue}return}if(isObject(current)){let fieldType=current.shape[segment];if(fieldType){current=fieldType;continue}}return}return current},primitiveLabels={any:`any value`,bigint:`bigint`,boolean:`boolean`,date:`date`,never:`never`,null:`null`,number:`number`,string:`string`,symbol:`symbol`,undefined:`undefined`,unknown:`unknown value`,void:`void`},describeType=schema=>{let{type}=schema;if(isWrappable(schema)){let desc=describeType(schema.unwrap());return type===`optional`?`${desc} (optional)`:type===`nullable`?`${desc} | null`:desc}if(type in primitiveLabels)return primitiveLabels[type];if(isObject(schema))return`object (${Object.entries(schema.shape).map(([key,val])=>{let inner=isWrappable(val)?val.unwrap():val;return`${key}: ${describeType(inner)}`}).join(`, `)})`;if(isArray(schema))return`array of ${describeType(schema.element)}`;if(isEnum(schema))return`one of: ${schema.options.join(`, `)}`;if(isUnion(schema))return`one of: ${schema.options.map(o=>{let inner=isWrappable(o)?o.unwrap():o;return describeType(inner)}).join(` | `)}`;if(schema.type===`literal`){let val=schema.value;return JSON.stringify(val??`?`)}return schema.type===`record`?`object (record)`:isZodString(schema)?schema.format===`url`?`URL string`:schema.format===`email`?`email string`:schema.format===`uuid`?`UUID string`:schema.minLength===null?`string`:`string (min ${schema.minLength} characters)`:type},getObjectShapeKeys=schema=>{let inner=isWrappable(schema)?schema.unwrap():schema;if(isObject(inner))return Object.keys(inner.shape)},handleInvalidType=(issue,fieldSchema)=>{let received=`received`in issue?String(issue.received):`unknown`;if(isWrappable(fieldSchema)){let inner=fieldSchema.unwrap();return`expected ${describeType(inner)} but received ${received} — this field has a default value configured, ensure props are merged correctly before validation`}if(isObject(fieldSchema)||isWrappable(fieldSchema)&&isObject(fieldSchema.unwrap())){let keys=getObjectShapeKeys(fieldSchema);return`expected object${keys?` with properties (${keys.join(`, `)})`:``} but received ${received} — add .default({}) to make this field optional`}return isArray(fieldSchema)?`expected ${describeType(fieldSchema)} but received ${received} — add .default([]) to make this field optional`:issue.message},handleInvalidFormat=(issue,field)=>{let{format:fmt}=issue;return fmt===`url`?`invalid URL format for field '${field}' — expected a valid URL (e.g., https://example.com/image.png)`:fmt===`email`?`invalid email format for field '${field}' — expected a valid email address`:issue.message},handleInvalidValue=issue=>{let{values}=issue;return`expected one of: ${values?.join(`, `)??`unknown`}`},handleTooSmall=issue=>{let{minimum:min,origin}=issue;if(min!==null){let suffix=``;return origin===`string`?suffix=`character(s)`:origin===`array`&&(suffix=`item(s)`),`must be at least ${min} ${suffix}`}return issue.message},enhanceMessage=(issue,fieldSchema)=>{let field=issue.path.join(`.`);if(!fieldSchema)return issue.message;switch(issue.code){case`invalid_type`:return handleInvalidType(issue,fieldSchema);case`invalid_format`:return handleInvalidFormat(issue,field);case`invalid_value`:return handleInvalidValue(issue);case`too_small`:return handleTooSmall(issue);case`too_big`:return issue.message;case`unrecognized_keys`:{let{keys}=issue;return`unexpected field '${keys?.join(`, `)??field}' — not recognized by the schema, check for typos or remove it`}case`invalid_union`:{let inner=isWrappable(fieldSchema)?fieldSchema.unwrap():fieldSchema;return isUnion(inner)?`did not match any valid type — expected ${describeType(inner)}`:issue.message}default:return issue.message}},getSuggestion=(issue,fieldSchema)=>{if(fieldSchema){if(issue.code===`invalid_type`){if(isObject(fieldSchema))return`add .default({}) to make this object optional`;if(isArray(fieldSchema))return`add .default([]) to make this array optional`;if(isWrappable(fieldSchema))return`ensure props are merged correctly before validation`}if(issue.code===`invalid_value`){let{values}=issue;if(values)return`use one of: ${values.join(`, `)}`}if(issue.code===`invalid_format`){let{format:fmt}=issue;if(fmt===`url`)return`provide a valid URL starting with http:// or https://`;if(fmt===`email`)return`provide a valid email address`}}},validateProps=(schema,props)=>{let result=schema.safeParse(props);if(result.success)return result.data;throw new PropValidationError(result.error.issues.map(issue=>{let path=issue.path,fieldSchema=resolveFieldSchema(schema,path);return{code:String(issue.code),expected:`expected`in issue?String(issue.expected):`unknown`,field:path.join(`.`),message:enhanceMessage(issue,fieldSchema),received:`received`in issue?String(issue.received):`unknown`,suggestion:getSuggestion(issue,fieldSchema)}}))},isZodObject=schema=>typeof schema!=`object`||!schema?!1:schema.type===`object`,zodToJsonSchemaShape=schema=>typeof schema!=`object`||!schema||!(`shape`in schema)?{}:z.toJSONSchema(schema);export{zodToJsonSchemaShape as a,validateProps as i,defineSchema as n,isZodObject as r,PropValidationError as t};
|
package/package.json
CHANGED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
//#region src/shared/validation.ts
|
|
3
|
-
/** Identity helper purely for inference ergonomics at the template definition site. */
|
|
4
|
-
const defineSchema = (schema) => schema;
|
|
5
|
-
var PropValidationError = class extends Error {
|
|
6
|
-
issues;
|
|
7
|
-
constructor(issues) {
|
|
8
|
-
super(`Invalid props: ${issues.map((i) => `${i.field || "(root)"} - ${i.message}${i.suggestion ? `. Suggestion: ${i.suggestion}` : ""}`).join("; ")}`);
|
|
9
|
-
this.name = "PropValidationError";
|
|
10
|
-
this.issues = issues;
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
const isObject = (schema) => schema.type === "object";
|
|
14
|
-
const isArray = (schema) => schema.type === "array";
|
|
15
|
-
const isEnum = (schema) => schema.type === "enum";
|
|
16
|
-
const isUnion = (schema) => schema.type === "union";
|
|
17
|
-
const isZodString = (schema) => schema.type === "string";
|
|
18
|
-
const isWrappable = (schema) => schema.type === "default" || schema.type === "optional" || schema.type === "nullable";
|
|
19
|
-
const resolveFieldSchema = (schema, path) => {
|
|
20
|
-
let current = schema;
|
|
21
|
-
for (let i = 0; i < path.length; i += 1) {
|
|
22
|
-
const segment = path[i];
|
|
23
|
-
if (!(i === path.length - 1) && isWrappable(current)) current = current.unwrap();
|
|
24
|
-
if (typeof segment === "number") {
|
|
25
|
-
if (isArray(current)) {
|
|
26
|
-
current = current.element;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (isObject(current)) {
|
|
32
|
-
const fieldType = current.shape[segment];
|
|
33
|
-
if (fieldType) {
|
|
34
|
-
current = fieldType;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
return current;
|
|
41
|
-
};
|
|
42
|
-
const primitiveLabels = {
|
|
43
|
-
any: "any value",
|
|
44
|
-
bigint: "bigint",
|
|
45
|
-
boolean: "boolean",
|
|
46
|
-
date: "date",
|
|
47
|
-
never: "never",
|
|
48
|
-
null: "null",
|
|
49
|
-
number: "number",
|
|
50
|
-
string: "string",
|
|
51
|
-
symbol: "symbol",
|
|
52
|
-
undefined: "undefined",
|
|
53
|
-
unknown: "unknown value",
|
|
54
|
-
void: "void"
|
|
55
|
-
};
|
|
56
|
-
const describeType = (schema) => {
|
|
57
|
-
const { type } = schema;
|
|
58
|
-
if (isWrappable(schema)) {
|
|
59
|
-
const desc = describeType(schema.unwrap());
|
|
60
|
-
if (type === "optional") return `${desc} (optional)`;
|
|
61
|
-
if (type === "nullable") return `${desc} | null`;
|
|
62
|
-
return desc;
|
|
63
|
-
}
|
|
64
|
-
if (type in primitiveLabels) return primitiveLabels[type];
|
|
65
|
-
if (isObject(schema)) return `object (${Object.entries(schema.shape).map(([key, val]) => {
|
|
66
|
-
const inner = isWrappable(val) ? val.unwrap() : val;
|
|
67
|
-
return `${key}: ${describeType(inner)}`;
|
|
68
|
-
}).join(", ")})`;
|
|
69
|
-
if (isArray(schema)) return `array of ${describeType(schema.element)}`;
|
|
70
|
-
if (isEnum(schema)) return `one of: ${schema.options.join(", ")}`;
|
|
71
|
-
if (isUnion(schema)) return `one of: ${schema.options.map((o) => {
|
|
72
|
-
const inner = isWrappable(o) ? o.unwrap() : o;
|
|
73
|
-
return describeType(inner);
|
|
74
|
-
}).join(" | ")}`;
|
|
75
|
-
if (schema.type === "literal") {
|
|
76
|
-
const val = schema.value;
|
|
77
|
-
return JSON.stringify(val ?? "?");
|
|
78
|
-
}
|
|
79
|
-
if (schema.type === "record") return "object (record)";
|
|
80
|
-
if (isZodString(schema)) {
|
|
81
|
-
if (schema.format === "url") return "URL string";
|
|
82
|
-
if (schema.format === "email") return "email string";
|
|
83
|
-
if (schema.format === "uuid") return "UUID string";
|
|
84
|
-
if (schema.minLength !== null) return `string (min ${schema.minLength} characters)`;
|
|
85
|
-
return "string";
|
|
86
|
-
}
|
|
87
|
-
return type;
|
|
88
|
-
};
|
|
89
|
-
const getObjectShapeKeys = (schema) => {
|
|
90
|
-
const inner = isWrappable(schema) ? schema.unwrap() : schema;
|
|
91
|
-
if (!isObject(inner)) return;
|
|
92
|
-
return Object.keys(inner.shape);
|
|
93
|
-
};
|
|
94
|
-
const handleInvalidType = (issue, fieldSchema) => {
|
|
95
|
-
const received = "received" in issue ? String(issue.received) : "unknown";
|
|
96
|
-
if (isWrappable(fieldSchema)) {
|
|
97
|
-
const inner = fieldSchema.unwrap();
|
|
98
|
-
return `expected ${describeType(inner)} but received ${received} — this field has a default value configured, ensure props are merged correctly before validation`;
|
|
99
|
-
}
|
|
100
|
-
if (isObject(fieldSchema) || isWrappable(fieldSchema) && isObject(fieldSchema.unwrap())) {
|
|
101
|
-
const keys = getObjectShapeKeys(fieldSchema);
|
|
102
|
-
return `expected object${keys ? ` with properties (${keys.join(", ")})` : ""} but received ${received} — add .default({}) to make this field optional`;
|
|
103
|
-
}
|
|
104
|
-
if (isArray(fieldSchema)) return `expected ${describeType(fieldSchema)} but received ${received} — add .default([]) to make this field optional`;
|
|
105
|
-
return issue.message;
|
|
106
|
-
};
|
|
107
|
-
const handleInvalidFormat = (issue, field) => {
|
|
108
|
-
const { format: fmt } = issue;
|
|
109
|
-
if (fmt === "url") return `invalid URL format for field '${field}' — expected a valid URL (e.g., https://example.com/image.png)`;
|
|
110
|
-
if (fmt === "email") return `invalid email format for field '${field}' — expected a valid email address`;
|
|
111
|
-
return issue.message;
|
|
112
|
-
};
|
|
113
|
-
const handleInvalidValue = (issue) => {
|
|
114
|
-
const { values } = issue;
|
|
115
|
-
return `expected one of: ${values?.join(", ") ?? "unknown"}`;
|
|
116
|
-
};
|
|
117
|
-
const handleTooSmall = (issue) => {
|
|
118
|
-
const { minimum: min, origin } = issue;
|
|
119
|
-
if (min !== null) {
|
|
120
|
-
let suffix = "";
|
|
121
|
-
if (origin === "string") suffix = "character(s)";
|
|
122
|
-
else if (origin === "array") suffix = "item(s)";
|
|
123
|
-
return `must be at least ${min} ${suffix}`;
|
|
124
|
-
}
|
|
125
|
-
return issue.message;
|
|
126
|
-
};
|
|
127
|
-
const enhanceMessage = (issue, fieldSchema) => {
|
|
128
|
-
const field = issue.path.join(".");
|
|
129
|
-
if (!fieldSchema) return issue.message;
|
|
130
|
-
switch (issue.code) {
|
|
131
|
-
case "invalid_type": return handleInvalidType(issue, fieldSchema);
|
|
132
|
-
case "invalid_format": return handleInvalidFormat(issue, field);
|
|
133
|
-
case "invalid_value": return handleInvalidValue(issue);
|
|
134
|
-
case "too_small": return handleTooSmall(issue);
|
|
135
|
-
case "too_big": return issue.message;
|
|
136
|
-
case "unrecognized_keys": {
|
|
137
|
-
const { keys } = issue;
|
|
138
|
-
return `unexpected field '${keys?.join(", ") ?? field}' — not recognized by the schema, check for typos or remove it`;
|
|
139
|
-
}
|
|
140
|
-
case "invalid_union": {
|
|
141
|
-
const inner = isWrappable(fieldSchema) ? fieldSchema.unwrap() : fieldSchema;
|
|
142
|
-
if (isUnion(inner)) return `did not match any valid type — expected ${describeType(inner)}`;
|
|
143
|
-
return issue.message;
|
|
144
|
-
}
|
|
145
|
-
default: return issue.message;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const getSuggestion = (issue, fieldSchema) => {
|
|
149
|
-
if (!fieldSchema) return;
|
|
150
|
-
if (issue.code === "invalid_type") {
|
|
151
|
-
if (isObject(fieldSchema)) return "add .default({}) to make this object optional";
|
|
152
|
-
if (isArray(fieldSchema)) return "add .default([]) to make this array optional";
|
|
153
|
-
if (isWrappable(fieldSchema)) return "ensure props are merged correctly before validation";
|
|
154
|
-
}
|
|
155
|
-
if (issue.code === "invalid_value") {
|
|
156
|
-
const { values } = issue;
|
|
157
|
-
if (values) return `use one of: ${values.join(", ")}`;
|
|
158
|
-
}
|
|
159
|
-
if (issue.code === "invalid_format") {
|
|
160
|
-
const { format: fmt } = issue;
|
|
161
|
-
if (fmt === "url") return "provide a valid URL starting with http:// or https://";
|
|
162
|
-
if (fmt === "email") return "provide a valid email address";
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
/**
|
|
166
|
-
* Validates props against a template's zod schema, reshaping any failure
|
|
167
|
-
* into a structured, agent-consumable error rather than letting a raw
|
|
168
|
-
* ZodError (verbose, deeply nested, not designed for programmatic
|
|
169
|
-
* consumption) escape to CLI/HTTP/MCP callers.
|
|
170
|
-
*
|
|
171
|
-
* This is the ONLY validation entry point in the package -- it is the
|
|
172
|
-
* function `index.ts` re-exports at the package root, and every downstream
|
|
173
|
-
* consumer (CLI exit-code reporting, MCP "error strings returned to LLM",
|
|
174
|
-
* dev-server field-level error boundaries) is expected to catch
|
|
175
|
-
* `PropValidationError` and read `.issues`, not a raw `ZodError`.
|
|
176
|
-
*/
|
|
177
|
-
const validateProps = (schema, props) => {
|
|
178
|
-
const result = schema.safeParse(props);
|
|
179
|
-
if (result.success) return result.data;
|
|
180
|
-
throw new PropValidationError(result.error.issues.map((issue) => {
|
|
181
|
-
const path = issue.path;
|
|
182
|
-
const fieldSchema = resolveFieldSchema(schema, path);
|
|
183
|
-
return {
|
|
184
|
-
code: String(issue.code),
|
|
185
|
-
expected: "expected" in issue ? String(issue.expected) : "unknown",
|
|
186
|
-
field: path.join("."),
|
|
187
|
-
message: enhanceMessage(issue, fieldSchema),
|
|
188
|
-
received: "received" in issue ? String(issue.received) : "unknown",
|
|
189
|
-
suggestion: getSuggestion(issue, fieldSchema)
|
|
190
|
-
};
|
|
191
|
-
}));
|
|
192
|
-
};
|
|
193
|
-
const isZodObject = (schema) => {
|
|
194
|
-
if (typeof schema !== "object" || schema === null) return false;
|
|
195
|
-
return schema.type === "object";
|
|
196
|
-
};
|
|
197
|
-
const zodToJsonSchemaShape = (schema) => {
|
|
198
|
-
if (typeof schema !== "object" || !schema || !("shape" in schema)) return {};
|
|
199
|
-
return z.toJSONSchema(schema);
|
|
200
|
-
};
|
|
201
|
-
//#endregion
|
|
202
|
-
export { zodToJsonSchemaShape as a, validateProps as i, defineSchema as n, isZodObject as r, PropValidationError as t };
|