@defai.digital/automatosx 12.8.7 → 13.1.3
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/LICENSE +1 -1
- package/README.md +48 -754
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +16 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -74239
- package/dist/index.js.map +1 -0
- package/package.json +35 -160
- package/.github/assets/ax-cli.png +0 -0
- package/.github/assets/axlogo.png +0 -0
- package/CHANGELOG.md +0 -81
- package/SECURITY.md +0 -173
- package/dist/mcp/index.d.ts +0 -2
- package/dist/mcp/index.js +0 -43627
- package/examples/AGENTS_INFO.md +0 -187
- package/examples/README.md +0 -434
- package/examples/abilities/accessibility.md +0 -115
- package/examples/abilities/api-design.md +0 -168
- package/examples/abilities/best-practices.md +0 -102
- package/examples/abilities/caching-strategy.md +0 -165
- package/examples/abilities/ci-cd.md +0 -61
- package/examples/abilities/clean-code.md +0 -398
- package/examples/abilities/code-generation.md +0 -333
- package/examples/abilities/code-review.md +0 -51
- package/examples/abilities/component-architecture.md +0 -112
- package/examples/abilities/content-creation.md +0 -97
- package/examples/abilities/data-modeling.md +0 -171
- package/examples/abilities/data-validation.md +0 -50
- package/examples/abilities/db-modeling.md +0 -167
- package/examples/abilities/debugging.md +0 -52
- package/examples/abilities/design-patterns.md +0 -437
- package/examples/abilities/design-system-implementation.md +0 -126
- package/examples/abilities/documentation.md +0 -54
- package/examples/abilities/etl-pipelines.md +0 -44
- package/examples/abilities/feasibility-study.md +0 -20
- package/examples/abilities/general-assistance.md +0 -26
- package/examples/abilities/idea-evaluation.md +0 -21
- package/examples/abilities/infra-as-code.md +0 -57
- package/examples/abilities/job-orchestration.md +0 -44
- package/examples/abilities/literature-review.md +0 -19
- package/examples/abilities/longform-report.md +0 -25
- package/examples/abilities/mathematical-reasoning.md +0 -170
- package/examples/abilities/observability.md +0 -61
- package/examples/abilities/orbital-mechanics.md +0 -50
- package/examples/abilities/our-architecture-decisions.md +0 -180
- package/examples/abilities/our-code-review-checklist.md +0 -149
- package/examples/abilities/our-coding-standards.md +0 -369
- package/examples/abilities/our-project-structure.md +0 -183
- package/examples/abilities/performance.md +0 -89
- package/examples/abilities/problem-solving.md +0 -50
- package/examples/abilities/propulsion-systems.md +0 -50
- package/examples/abilities/quantum-algorithm-design.md +0 -54
- package/examples/abilities/quantum-error-correction.md +0 -56
- package/examples/abilities/quantum-frameworks-transpilation.md +0 -53
- package/examples/abilities/quantum-noise-modeling.md +0 -58
- package/examples/abilities/refactoring.md +0 -223
- package/examples/abilities/release-strategy.md +0 -58
- package/examples/abilities/secrets-policy.md +0 -61
- package/examples/abilities/secure-coding-review.md +0 -60
- package/examples/abilities/software-architecture.md +0 -394
- package/examples/abilities/solid-principles.md +0 -341
- package/examples/abilities/sql-optimization.md +0 -84
- package/examples/abilities/state-management.md +0 -96
- package/examples/abilities/task-planning.md +0 -65
- package/examples/abilities/technical-writing.md +0 -77
- package/examples/abilities/telemetry-diagnostics.md +0 -51
- package/examples/abilities/testing.md +0 -56
- package/examples/abilities/threat-modeling.md +0 -58
- package/examples/abilities/troubleshooting.md +0 -80
- package/examples/abilities/typescript-zod-validation.md +0 -830
- package/examples/agents/AGENTS_INTEGRATION.md +0 -99
- package/examples/agents/aerospace-scientist.yaml +0 -159
- package/examples/agents/architecture.yaml +0 -244
- package/examples/agents/automatosx.config.json +0 -286
- package/examples/agents/backend.yaml +0 -141
- package/examples/agents/ceo.yaml +0 -105
- package/examples/agents/creative-marketer.yaml +0 -173
- package/examples/agents/cto.yaml +0 -118
- package/examples/agents/data-scientist.yaml +0 -200
- package/examples/agents/data.yaml +0 -106
- package/examples/agents/design.yaml +0 -115
- package/examples/agents/devops.yaml +0 -124
- package/examples/agents/frontend.yaml +0 -171
- package/examples/agents/fullstack.yaml +0 -172
- package/examples/agents/mobile.yaml +0 -185
- package/examples/agents/product.yaml +0 -103
- package/examples/agents/quality.yaml +0 -117
- package/examples/agents/quantum-engineer.yaml +0 -166
- package/examples/agents/researcher.yaml +0 -122
- package/examples/agents/security.yaml +0 -115
- package/examples/agents/standard.yaml +0 -214
- package/examples/agents/writer.yaml +0 -122
- package/examples/providers/README.md +0 -117
- package/examples/providers/claude/CLAUDE_INTEGRATION.md +0 -302
- package/examples/providers/claude/mcp/automatosx.json +0 -244
- package/examples/providers/codex/CODEX_INTEGRATION.md +0 -593
- package/examples/providers/codex/README.md +0 -349
- package/examples/providers/codex/usage-examples.ts +0 -421
- package/examples/providers/gemini/GEMINI_INTEGRATION.md +0 -236
- package/examples/providers/gemini/README.md +0 -76
- package/examples/providers/openai-codex-example.ts +0 -421
- package/examples/pytorch_resnet50_training.py +0 -289
- package/examples/specs/automatosx-release.ax.yaml +0 -380
- package/examples/specs/enterprise.ax.yaml +0 -121
- package/examples/specs/enterprise.yaml.mustache +0 -121
- package/examples/specs/government.ax.yaml +0 -148
- package/examples/specs/government.yaml.mustache +0 -148
- package/examples/specs/minimal.ax.yaml +0 -21
- package/examples/specs/minimal.yaml.mustache +0 -21
- package/examples/teams/business.yaml +0 -56
- package/examples/teams/core.yaml +0 -60
- package/examples/teams/design.yaml +0 -58
- package/examples/teams/engineering.yaml +0 -69
- package/examples/teams/research.yaml +0 -56
- package/examples/use-cases/01-web-app-development.md +0 -374
- package/examples/workflows/analyst.yaml +0 -60
- package/examples/workflows/assistant.yaml +0 -48
- package/examples/workflows/basic-agent.yaml +0 -28
- package/examples/workflows/code-reviewer.yaml +0 -52
- package/examples/workflows/debugger.yaml +0 -63
- package/examples/workflows/designer.yaml +0 -69
- package/examples/workflows/developer.yaml +0 -60
- package/examples/workflows/fullstack-developer.yaml +0 -395
- package/examples/workflows/qa-specialist.yaml +0 -71
- package/schema/ability-metadata.json +0 -21
- package/schema/config.json +0 -703
- package/schema/spec-schema.json +0 -608
|
@@ -1,830 +0,0 @@
|
|
|
1
|
-
# TypeScript + Zod Validation
|
|
2
|
-
|
|
3
|
-
**CRITICAL**: When writing JavaScript or TypeScript scripts, ALWAYS use TypeScript with Zod for runtime type safety and validation. This is a REQUIRED standard for AutomatosX agents.
|
|
4
|
-
|
|
5
|
-
## Why TypeScript + Zod?
|
|
6
|
-
|
|
7
|
-
### The Problem with TypeScript Alone
|
|
8
|
-
|
|
9
|
-
TypeScript provides **compile-time** type safety, but types disappear at runtime:
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
// ❌ TypeScript types don't catch runtime errors
|
|
13
|
-
interface User {
|
|
14
|
-
email: string;
|
|
15
|
-
age: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function createUser(data: unknown): User {
|
|
19
|
-
return data as User; // UNSAFE! No validation!
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// This will compile fine but crash at runtime
|
|
23
|
-
const user = createUser({ email: 123, age: "invalid" });
|
|
24
|
-
console.log(user.email.toLowerCase()); // Runtime error!
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### The Solution: Zod
|
|
28
|
-
|
|
29
|
-
Zod provides **runtime** validation + automatic TypeScript type inference:
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
// ✅ Zod catches errors at runtime
|
|
33
|
-
import { z } from 'zod';
|
|
34
|
-
|
|
35
|
-
const UserSchema = z.object({
|
|
36
|
-
email: z.string().email(),
|
|
37
|
-
age: z.number().int().positive(),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
type User = z.infer<typeof UserSchema>;
|
|
41
|
-
|
|
42
|
-
function createUser(data: unknown): User {
|
|
43
|
-
return UserSchema.parse(data); // Throws if invalid!
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// This will throw a clear error before anything bad happens
|
|
47
|
-
try {
|
|
48
|
-
const user = createUser({ email: 123, age: "invalid" });
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Validation failed:', error.message);
|
|
51
|
-
// Output: "email: Expected string, received number"
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Installation
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npm install zod
|
|
59
|
-
# or
|
|
60
|
-
pnpm add zod
|
|
61
|
-
# or
|
|
62
|
-
yarn add zod
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Core Concepts
|
|
66
|
-
|
|
67
|
-
### 1. Schema Definition
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { z } from 'zod';
|
|
71
|
-
|
|
72
|
-
// Primitive types
|
|
73
|
-
const StringSchema = z.string();
|
|
74
|
-
const NumberSchema = z.number();
|
|
75
|
-
const BooleanSchema = z.boolean();
|
|
76
|
-
const DateSchema = z.date();
|
|
77
|
-
|
|
78
|
-
// Objects
|
|
79
|
-
const UserSchema = z.object({
|
|
80
|
-
id: z.string().uuid(),
|
|
81
|
-
name: z.string().min(1).max(100),
|
|
82
|
-
email: z.string().email(),
|
|
83
|
-
age: z.number().int().min(0).max(150).optional(),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Arrays
|
|
87
|
-
const StringArraySchema = z.array(z.string());
|
|
88
|
-
const UserArraySchema = z.array(UserSchema);
|
|
89
|
-
|
|
90
|
-
// Enums
|
|
91
|
-
const RoleSchema = z.enum(['admin', 'user', 'guest']);
|
|
92
|
-
|
|
93
|
-
// Unions (OR)
|
|
94
|
-
const StringOrNumberSchema = z.union([z.string(), z.number()]);
|
|
95
|
-
const StringOrNumberSchema2 = z.string().or(z.number()); // Alternative syntax
|
|
96
|
-
|
|
97
|
-
// Intersections (AND)
|
|
98
|
-
const BaseUserSchema = z.object({ id: z.string() });
|
|
99
|
-
const ExtendedUserSchema = BaseUserSchema.and(
|
|
100
|
-
z.object({ email: z.string() })
|
|
101
|
-
);
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### 2. Type Inference
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
import { z } from 'zod';
|
|
108
|
-
|
|
109
|
-
// ✅ Define schema once, get TypeScript types for free
|
|
110
|
-
const UserSchema = z.object({
|
|
111
|
-
id: z.string().uuid(),
|
|
112
|
-
email: z.string().email(),
|
|
113
|
-
role: z.enum(['admin', 'user']),
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Type is automatically inferred
|
|
117
|
-
type User = z.infer<typeof UserSchema>;
|
|
118
|
-
// Equivalent to:
|
|
119
|
-
// type User = {
|
|
120
|
-
// id: string;
|
|
121
|
-
// email: string;
|
|
122
|
-
// role: 'admin' | 'user';
|
|
123
|
-
// }
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### 3. Parsing and Validation
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
import { z } from 'zod';
|
|
130
|
-
|
|
131
|
-
const UserSchema = z.object({
|
|
132
|
-
email: z.string().email(),
|
|
133
|
-
age: z.number().int().positive(),
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// parse() - Throws on validation failure
|
|
137
|
-
try {
|
|
138
|
-
const user = UserSchema.parse({ email: 'test@example.com', age: 25 });
|
|
139
|
-
console.log(user); // { email: 'test@example.com', age: 25 }
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (error instanceof z.ZodError) {
|
|
142
|
-
console.error('Validation failed:', error.issues);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// safeParse() - Returns success/error object (recommended)
|
|
147
|
-
const result = UserSchema.safeParse({ email: 'invalid', age: -5 });
|
|
148
|
-
|
|
149
|
-
if (result.success) {
|
|
150
|
-
console.log('Valid user:', result.data);
|
|
151
|
-
} else {
|
|
152
|
-
console.error('Validation errors:', result.error.issues);
|
|
153
|
-
// [
|
|
154
|
-
// { path: ['email'], message: 'Invalid email' },
|
|
155
|
-
// { path: ['age'], message: 'Number must be greater than 0' }
|
|
156
|
-
// ]
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Common Use Cases
|
|
161
|
-
|
|
162
|
-
### Use Case 1: CLI Argument Validation
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
import { z } from 'zod';
|
|
166
|
-
|
|
167
|
-
const CliArgsSchema = z.object({
|
|
168
|
-
input: z.string().min(1, 'Input file required'),
|
|
169
|
-
output: z.string().min(1, 'Output file required'),
|
|
170
|
-
verbose: z.boolean().default(false),
|
|
171
|
-
format: z.enum(['json', 'yaml', 'csv']).default('json'),
|
|
172
|
-
limit: z.number().int().positive().optional(),
|
|
173
|
-
filters: z.array(z.string()).default([]),
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
type CliArgs = z.infer<typeof CliArgsSchema>;
|
|
177
|
-
|
|
178
|
-
function parseCliArgs(argv: string[]): CliArgs {
|
|
179
|
-
// Build args object from process.argv
|
|
180
|
-
const rawArgs = {
|
|
181
|
-
input: argv[2],
|
|
182
|
-
output: argv[3],
|
|
183
|
-
verbose: argv.includes('--verbose'),
|
|
184
|
-
format: argv.includes('--format')
|
|
185
|
-
? argv[argv.indexOf('--format') + 1]
|
|
186
|
-
: undefined,
|
|
187
|
-
limit: argv.includes('--limit')
|
|
188
|
-
? parseInt(argv[argv.indexOf('--limit') + 1], 10)
|
|
189
|
-
: undefined,
|
|
190
|
-
filters: argv.filter(arg => arg.startsWith('--filter=')).map(f => f.slice(9)),
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// Validate and apply defaults
|
|
194
|
-
const result = CliArgsSchema.safeParse(rawArgs);
|
|
195
|
-
|
|
196
|
-
if (!result.success) {
|
|
197
|
-
const errors = result.error.issues.map(
|
|
198
|
-
(issue) => ` ${issue.path.join('.')}: ${issue.message}`
|
|
199
|
-
);
|
|
200
|
-
throw new Error(`Invalid CLI arguments:\n${errors.join('\n')}`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return result.data;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Usage
|
|
207
|
-
try {
|
|
208
|
-
const args = parseCliArgs(process.argv);
|
|
209
|
-
console.log('Parsed args:', args);
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error(error.message);
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Use Case 2: Configuration File Validation
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
import { z } from 'zod';
|
|
220
|
-
import { readFile } from 'fs/promises';
|
|
221
|
-
|
|
222
|
-
const ServerConfigSchema = z.object({
|
|
223
|
-
port: z.number().int().min(1024).max(65535).default(3000),
|
|
224
|
-
host: z.string().default('localhost'),
|
|
225
|
-
cors: z.object({
|
|
226
|
-
enabled: z.boolean().default(true),
|
|
227
|
-
origins: z.array(z.string().url()).default(['http://localhost:3000']),
|
|
228
|
-
}).optional(),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const DatabaseConfigSchema = z.object({
|
|
232
|
-
url: z.string().url(),
|
|
233
|
-
poolSize: z.number().int().positive().default(10),
|
|
234
|
-
ssl: z.boolean().default(false),
|
|
235
|
-
migrations: z.object({
|
|
236
|
-
auto: z.boolean().default(false),
|
|
237
|
-
directory: z.string().default('./migrations'),
|
|
238
|
-
}).optional(),
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const LoggingConfigSchema = z.object({
|
|
242
|
-
level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
243
|
-
format: z.enum(['json', 'text']).default('text'),
|
|
244
|
-
destination: z.string().default('stdout'),
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
const AppConfigSchema = z.object({
|
|
248
|
-
server: ServerConfigSchema,
|
|
249
|
-
database: DatabaseConfigSchema,
|
|
250
|
-
logging: LoggingConfigSchema.optional(),
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
type AppConfig = z.infer<typeof AppConfigSchema>;
|
|
254
|
-
|
|
255
|
-
async function loadConfig(path: string): Promise<AppConfig> {
|
|
256
|
-
try {
|
|
257
|
-
const raw = await readFile(path, 'utf-8');
|
|
258
|
-
const json = JSON.parse(raw);
|
|
259
|
-
|
|
260
|
-
// Validate and apply defaults
|
|
261
|
-
const result = AppConfigSchema.safeParse(json);
|
|
262
|
-
|
|
263
|
-
if (!result.success) {
|
|
264
|
-
const errors = result.error.issues.map((issue) => {
|
|
265
|
-
const path = issue.path.join('.');
|
|
266
|
-
return ` config.${path}: ${issue.message}`;
|
|
267
|
-
});
|
|
268
|
-
throw new Error(
|
|
269
|
-
`Invalid configuration file:\n${errors.join('\n')}\n\nPlease check ${path}`
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return result.data;
|
|
274
|
-
} catch (error) {
|
|
275
|
-
if (error instanceof SyntaxError) {
|
|
276
|
-
throw new Error(`Invalid JSON in config file: ${error.message}`);
|
|
277
|
-
}
|
|
278
|
-
throw error;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Usage
|
|
283
|
-
const config = await loadConfig('./config.json');
|
|
284
|
-
console.log('Server running on:', `${config.server.host}:${config.server.port}`);
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Use Case 3: API Request/Response Validation
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
import { z } from 'zod';
|
|
291
|
-
|
|
292
|
-
// Input schema for creating a blog post
|
|
293
|
-
const CreatePostInputSchema = z.object({
|
|
294
|
-
title: z.string().min(1).max(200),
|
|
295
|
-
content: z.string().min(1).max(50000),
|
|
296
|
-
tags: z.array(z.string().min(1).max(50)).max(10).default([]),
|
|
297
|
-
publishAt: z.string().datetime().optional(),
|
|
298
|
-
draft: z.boolean().default(true),
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// Response schema for a blog post
|
|
302
|
-
const PostResponseSchema = z.object({
|
|
303
|
-
id: z.string().uuid(),
|
|
304
|
-
title: z.string(),
|
|
305
|
-
content: z.string(),
|
|
306
|
-
tags: z.array(z.string()),
|
|
307
|
-
publishAt: z.string().datetime().nullable(),
|
|
308
|
-
draft: z.boolean(),
|
|
309
|
-
createdAt: z.string().datetime(),
|
|
310
|
-
updatedAt: z.string().datetime(),
|
|
311
|
-
author: z.object({
|
|
312
|
-
id: z.string().uuid(),
|
|
313
|
-
name: z.string(),
|
|
314
|
-
email: z.string().email(),
|
|
315
|
-
}),
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
type CreatePostInput = z.infer<typeof CreatePostInputSchema>;
|
|
319
|
-
type PostResponse = z.infer<typeof PostResponseSchema>;
|
|
320
|
-
|
|
321
|
-
// API handler with validation
|
|
322
|
-
async function createPostHandler(req: Request): Promise<Response> {
|
|
323
|
-
try {
|
|
324
|
-
// Validate request body
|
|
325
|
-
const body = await req.json();
|
|
326
|
-
const input = CreatePostInputSchema.parse(body);
|
|
327
|
-
|
|
328
|
-
// Business logic
|
|
329
|
-
const post = await database.posts.create({
|
|
330
|
-
title: input.title,
|
|
331
|
-
content: input.content,
|
|
332
|
-
tags: input.tags,
|
|
333
|
-
publishAt: input.publishAt ? new Date(input.publishAt) : null,
|
|
334
|
-
draft: input.draft,
|
|
335
|
-
authorId: req.user.id,
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// Build response
|
|
339
|
-
const responseData = {
|
|
340
|
-
id: post.id,
|
|
341
|
-
title: post.title,
|
|
342
|
-
content: post.content,
|
|
343
|
-
tags: post.tags,
|
|
344
|
-
publishAt: post.publishAt?.toISOString() ?? null,
|
|
345
|
-
draft: post.draft,
|
|
346
|
-
createdAt: post.createdAt.toISOString(),
|
|
347
|
-
updatedAt: post.updatedAt.toISOString(),
|
|
348
|
-
author: {
|
|
349
|
-
id: req.user.id,
|
|
350
|
-
name: req.user.name,
|
|
351
|
-
email: req.user.email,
|
|
352
|
-
},
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
// Validate response before sending
|
|
356
|
-
const validatedResponse = PostResponseSchema.parse(responseData);
|
|
357
|
-
|
|
358
|
-
return new Response(JSON.stringify(validatedResponse), {
|
|
359
|
-
status: 201,
|
|
360
|
-
headers: { 'Content-Type': 'application/json' },
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
} catch (error) {
|
|
364
|
-
if (error instanceof z.ZodError) {
|
|
365
|
-
return new Response(
|
|
366
|
-
JSON.stringify({
|
|
367
|
-
error: 'Validation failed',
|
|
368
|
-
issues: error.issues.map((issue) => ({
|
|
369
|
-
field: issue.path.join('.'),
|
|
370
|
-
message: issue.message,
|
|
371
|
-
})),
|
|
372
|
-
}),
|
|
373
|
-
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
throw error;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Use Case 4: Environment Variable Validation
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
import { z } from 'zod';
|
|
385
|
-
|
|
386
|
-
const EnvSchema = z.object({
|
|
387
|
-
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
388
|
-
PORT: z.string().transform((val) => parseInt(val, 10)).pipe(
|
|
389
|
-
z.number().int().min(1024).max(65535)
|
|
390
|
-
).default('3000'),
|
|
391
|
-
DATABASE_URL: z.string().url(),
|
|
392
|
-
REDIS_URL: z.string().url().optional(),
|
|
393
|
-
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
394
|
-
API_KEY: z.string().min(32),
|
|
395
|
-
ENABLE_FEATURE_X: z.string()
|
|
396
|
-
.transform((val) => val === 'true')
|
|
397
|
-
.default('false'),
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
type Env = z.infer<typeof EnvSchema>;
|
|
401
|
-
|
|
402
|
-
function loadEnv(): Env {
|
|
403
|
-
const result = EnvSchema.safeParse(process.env);
|
|
404
|
-
|
|
405
|
-
if (!result.success) {
|
|
406
|
-
const errors = result.error.issues.map((issue) => {
|
|
407
|
-
const envVar = issue.path.join('.');
|
|
408
|
-
return ` ${envVar}: ${issue.message}`;
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
console.error('❌ Invalid environment variables:');
|
|
412
|
-
console.error(errors.join('\n'));
|
|
413
|
-
console.error('\nPlease check your .env file or environment configuration.');
|
|
414
|
-
process.exit(1);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
return result.data;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Usage at application startup
|
|
421
|
-
const env = loadEnv();
|
|
422
|
-
console.log(`Starting server on port ${env.PORT} in ${env.NODE_ENV} mode`);
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Use Case 5: File Content Validation
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
import { z } from 'zod';
|
|
429
|
-
import { readFile, writeFile } from 'fs/promises';
|
|
430
|
-
|
|
431
|
-
const PackageJsonSchema = z.object({
|
|
432
|
-
name: z.string().min(1),
|
|
433
|
-
version: z.string().regex(/^\d+\.\d+\.\d+$/),
|
|
434
|
-
description: z.string().optional(),
|
|
435
|
-
main: z.string().default('index.js'),
|
|
436
|
-
scripts: z.record(z.string(), z.string()).optional(),
|
|
437
|
-
dependencies: z.record(z.string(), z.string()).optional(),
|
|
438
|
-
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
439
|
-
engines: z.object({
|
|
440
|
-
node: z.string().optional(),
|
|
441
|
-
npm: z.string().optional(),
|
|
442
|
-
}).optional(),
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
type PackageJson = z.infer<typeof PackageJsonSchema>;
|
|
446
|
-
|
|
447
|
-
async function readPackageJson(path: string): Promise<PackageJson> {
|
|
448
|
-
try {
|
|
449
|
-
const raw = await readFile(path, 'utf-8');
|
|
450
|
-
const json = JSON.parse(raw);
|
|
451
|
-
|
|
452
|
-
const result = PackageJsonSchema.safeParse(json);
|
|
453
|
-
|
|
454
|
-
if (!result.success) {
|
|
455
|
-
const errors = result.error.issues.map((issue) => {
|
|
456
|
-
const field = issue.path.join('.');
|
|
457
|
-
return ` ${field}: ${issue.message}`;
|
|
458
|
-
});
|
|
459
|
-
throw new Error(
|
|
460
|
-
`Invalid package.json at ${path}:\n${errors.join('\n')}`
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
return result.data;
|
|
465
|
-
} catch (error) {
|
|
466
|
-
if (error instanceof SyntaxError) {
|
|
467
|
-
throw new Error(`Invalid JSON in package.json: ${error.message}`);
|
|
468
|
-
}
|
|
469
|
-
throw error;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
async function updatePackageVersion(
|
|
474
|
-
path: string,
|
|
475
|
-
newVersion: string
|
|
476
|
-
): Promise<void> {
|
|
477
|
-
const pkg = await readPackageJson(path);
|
|
478
|
-
|
|
479
|
-
// Validate new version format
|
|
480
|
-
const versionSchema = z.string().regex(/^\d+\.\d+\.\d+$/);
|
|
481
|
-
const validatedVersion = versionSchema.parse(newVersion);
|
|
482
|
-
|
|
483
|
-
pkg.version = validatedVersion;
|
|
484
|
-
|
|
485
|
-
// Re-validate entire package.json before writing
|
|
486
|
-
const validated = PackageJsonSchema.parse(pkg);
|
|
487
|
-
|
|
488
|
-
await writeFile(path, JSON.stringify(validated, null, 2), 'utf-8');
|
|
489
|
-
console.log(`✅ Updated package.json version to ${validatedVersion}`);
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
## Advanced Patterns
|
|
494
|
-
|
|
495
|
-
### Pattern 1: Refinements (Custom Validation)
|
|
496
|
-
|
|
497
|
-
```typescript
|
|
498
|
-
import { z } from 'zod';
|
|
499
|
-
|
|
500
|
-
const PasswordSchema = z.string()
|
|
501
|
-
.min(8, 'Password must be at least 8 characters')
|
|
502
|
-
.refine((password) => /[A-Z]/.test(password), {
|
|
503
|
-
message: 'Password must contain at least one uppercase letter',
|
|
504
|
-
})
|
|
505
|
-
.refine((password) => /[a-z]/.test(password), {
|
|
506
|
-
message: 'Password must contain at least one lowercase letter',
|
|
507
|
-
})
|
|
508
|
-
.refine((password) => /[0-9]/.test(password), {
|
|
509
|
-
message: 'Password must contain at least one number',
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
const UserRegistrationSchema = z.object({
|
|
513
|
-
email: z.string().email(),
|
|
514
|
-
password: PasswordSchema,
|
|
515
|
-
confirmPassword: z.string(),
|
|
516
|
-
}).refine((data) => data.password === data.confirmPassword, {
|
|
517
|
-
message: 'Passwords do not match',
|
|
518
|
-
path: ['confirmPassword'], // Error will be attached to confirmPassword field
|
|
519
|
-
});
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Pattern 2: Transformations
|
|
523
|
-
|
|
524
|
-
```typescript
|
|
525
|
-
import { z } from 'zod';
|
|
526
|
-
|
|
527
|
-
// Transform string to Date
|
|
528
|
-
const DateFromStringSchema = z.string()
|
|
529
|
-
.datetime()
|
|
530
|
-
.transform((str) => new Date(str));
|
|
531
|
-
|
|
532
|
-
// Transform and validate
|
|
533
|
-
const UserInputSchema = z.object({
|
|
534
|
-
email: z.string().email().transform((e) => e.toLowerCase()),
|
|
535
|
-
age: z.string().transform((s) => parseInt(s, 10)).pipe(
|
|
536
|
-
z.number().int().min(0).max(150)
|
|
537
|
-
),
|
|
538
|
-
createdAt: DateFromStringSchema,
|
|
539
|
-
});
|
|
540
|
-
|
|
541
|
-
// Input: { email: 'USER@EXAMPLE.COM', age: '25', createdAt: '2024-01-01T00:00:00Z' }
|
|
542
|
-
// Output: { email: 'user@example.com', age: 25, createdAt: Date object }
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### Pattern 3: Discriminated Unions
|
|
546
|
-
|
|
547
|
-
```typescript
|
|
548
|
-
import { z } from 'zod';
|
|
549
|
-
|
|
550
|
-
const SuccessResponseSchema = z.object({
|
|
551
|
-
status: z.literal('success'),
|
|
552
|
-
data: z.object({
|
|
553
|
-
id: z.string(),
|
|
554
|
-
name: z.string(),
|
|
555
|
-
}),
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
const ErrorResponseSchema = z.object({
|
|
559
|
-
status: z.literal('error'),
|
|
560
|
-
error: z.object({
|
|
561
|
-
code: z.string(),
|
|
562
|
-
message: z.string(),
|
|
563
|
-
}),
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
const ApiResponseSchema = z.discriminatedUnion('status', [
|
|
567
|
-
SuccessResponseSchema,
|
|
568
|
-
ErrorResponseSchema,
|
|
569
|
-
]);
|
|
570
|
-
|
|
571
|
-
type ApiResponse = z.infer<typeof ApiResponseSchema>;
|
|
572
|
-
|
|
573
|
-
function handleResponse(response: ApiResponse) {
|
|
574
|
-
if (response.status === 'success') {
|
|
575
|
-
// TypeScript knows response.data exists here
|
|
576
|
-
console.log('Success:', response.data.name);
|
|
577
|
-
} else {
|
|
578
|
-
// TypeScript knows response.error exists here
|
|
579
|
-
console.error('Error:', response.error.message);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### Pattern 4: Recursive Schemas
|
|
585
|
-
|
|
586
|
-
```typescript
|
|
587
|
-
import { z } from 'zod';
|
|
588
|
-
|
|
589
|
-
type Category = {
|
|
590
|
-
id: string;
|
|
591
|
-
name: string;
|
|
592
|
-
subcategories: Category[];
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
const CategorySchema: z.ZodType<Category> = z.lazy(() =>
|
|
596
|
-
z.object({
|
|
597
|
-
id: z.string().uuid(),
|
|
598
|
-
name: z.string().min(1),
|
|
599
|
-
subcategories: z.array(CategorySchema),
|
|
600
|
-
})
|
|
601
|
-
);
|
|
602
|
-
|
|
603
|
-
// Can validate deeply nested category trees
|
|
604
|
-
const category = CategorySchema.parse({
|
|
605
|
-
id: '123',
|
|
606
|
-
name: 'Electronics',
|
|
607
|
-
subcategories: [
|
|
608
|
-
{
|
|
609
|
-
id: '456',
|
|
610
|
-
name: 'Computers',
|
|
611
|
-
subcategories: [
|
|
612
|
-
{
|
|
613
|
-
id: '789',
|
|
614
|
-
name: 'Laptops',
|
|
615
|
-
subcategories: [],
|
|
616
|
-
},
|
|
617
|
-
],
|
|
618
|
-
},
|
|
619
|
-
],
|
|
620
|
-
});
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
## Error Handling Best Practices
|
|
624
|
-
|
|
625
|
-
### Detailed Error Messages
|
|
626
|
-
|
|
627
|
-
```typescript
|
|
628
|
-
import { z } from 'zod';
|
|
629
|
-
|
|
630
|
-
function validateData(data: unknown) {
|
|
631
|
-
const result = UserSchema.safeParse(data);
|
|
632
|
-
|
|
633
|
-
if (!result.success) {
|
|
634
|
-
// ✅ Extract detailed error information
|
|
635
|
-
const errors = result.error.issues.map((issue) => ({
|
|
636
|
-
field: issue.path.join('.'),
|
|
637
|
-
message: issue.message,
|
|
638
|
-
code: issue.code,
|
|
639
|
-
received: issue.received,
|
|
640
|
-
}));
|
|
641
|
-
|
|
642
|
-
console.error('Validation failed:');
|
|
643
|
-
errors.forEach((err) => {
|
|
644
|
-
console.error(` - ${err.field}: ${err.message} (got: ${err.received})`);
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
throw new ValidationError('Data validation failed', errors);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return result.data;
|
|
651
|
-
}
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
### Custom Error Messages
|
|
655
|
-
|
|
656
|
-
```typescript
|
|
657
|
-
import { z } from 'zod';
|
|
658
|
-
|
|
659
|
-
const UserSchema = z.object({
|
|
660
|
-
email: z.string({
|
|
661
|
-
required_error: 'Email is required',
|
|
662
|
-
invalid_type_error: 'Email must be a string',
|
|
663
|
-
}).email('Please provide a valid email address'),
|
|
664
|
-
|
|
665
|
-
age: z.number({
|
|
666
|
-
required_error: 'Age is required',
|
|
667
|
-
invalid_type_error: 'Age must be a number',
|
|
668
|
-
}).int('Age must be a whole number')
|
|
669
|
-
.min(0, 'Age cannot be negative')
|
|
670
|
-
.max(150, 'Age must be less than 150'),
|
|
671
|
-
|
|
672
|
-
role: z.enum(['admin', 'user', 'guest'], {
|
|
673
|
-
errorMap: () => ({ message: 'Role must be either admin, user, or guest' }),
|
|
674
|
-
}),
|
|
675
|
-
});
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
## Testing with Zod
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
import { describe, it, expect } from 'vitest';
|
|
682
|
-
import { z } from 'zod';
|
|
683
|
-
|
|
684
|
-
const UserSchema = z.object({
|
|
685
|
-
email: z.string().email(),
|
|
686
|
-
age: z.number().int().positive(),
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
describe('UserSchema', () => {
|
|
690
|
-
it('should validate valid user data', () => {
|
|
691
|
-
const result = UserSchema.safeParse({
|
|
692
|
-
email: 'test@example.com',
|
|
693
|
-
age: 25,
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
expect(result.success).toBe(true);
|
|
697
|
-
if (result.success) {
|
|
698
|
-
expect(result.data.email).toBe('test@example.com');
|
|
699
|
-
expect(result.data.age).toBe(25);
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
|
|
703
|
-
it('should reject invalid email', () => {
|
|
704
|
-
const result = UserSchema.safeParse({
|
|
705
|
-
email: 'not-an-email',
|
|
706
|
-
age: 25,
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
expect(result.success).toBe(false);
|
|
710
|
-
if (!result.success) {
|
|
711
|
-
expect(result.error.issues[0].path).toEqual(['email']);
|
|
712
|
-
expect(result.error.issues[0].message).toContain('email');
|
|
713
|
-
}
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
it('should reject negative age', () => {
|
|
717
|
-
const result = UserSchema.safeParse({
|
|
718
|
-
email: 'test@example.com',
|
|
719
|
-
age: -5,
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
expect(result.success).toBe(false);
|
|
723
|
-
if (!result.success) {
|
|
724
|
-
expect(result.error.issues[0].path).toEqual(['age']);
|
|
725
|
-
}
|
|
726
|
-
});
|
|
727
|
-
});
|
|
728
|
-
```
|
|
729
|
-
|
|
730
|
-
## Quick Reference Checklist
|
|
731
|
-
|
|
732
|
-
### Before Writing Any Script
|
|
733
|
-
|
|
734
|
-
- [ ] Installed Zod (`npm install zod`)
|
|
735
|
-
- [ ] Imported Zod (`import { z } from 'zod'`)
|
|
736
|
-
- [ ] Defined schemas for all external data sources
|
|
737
|
-
- [ ] Used `z.infer<typeof Schema>` for type inference
|
|
738
|
-
- [ ] Validated at system boundaries (CLI, files, APIs, env vars)
|
|
739
|
-
- [ ] Used `safeParse()` for better error handling
|
|
740
|
-
- [ ] Provided meaningful custom error messages
|
|
741
|
-
- [ ] No `any` types (use `z.unknown()` if type is truly unknown)
|
|
742
|
-
- [ ] Tested validation logic with unit tests
|
|
743
|
-
|
|
744
|
-
### When to Use Zod (Required)
|
|
745
|
-
|
|
746
|
-
✅ **ALWAYS use Zod for:**
|
|
747
|
-
- CLI argument parsing
|
|
748
|
-
- Configuration file validation (JSON, YAML, etc.)
|
|
749
|
-
- API request/response validation
|
|
750
|
-
- File content validation
|
|
751
|
-
- Environment variable validation
|
|
752
|
-
- Database query results
|
|
753
|
-
- External API responses
|
|
754
|
-
- User input from any source
|
|
755
|
-
|
|
756
|
-
❌ **TypeScript types alone are sufficient for:**
|
|
757
|
-
- Internal function parameters (same file)
|
|
758
|
-
- Return types of pure functions
|
|
759
|
-
- Class properties with known types
|
|
760
|
-
- Type aliases and interfaces for documentation
|
|
761
|
-
- Generic type parameters
|
|
762
|
-
|
|
763
|
-
## Common Zod Methods Reference
|
|
764
|
-
|
|
765
|
-
```typescript
|
|
766
|
-
// String validations
|
|
767
|
-
z.string()
|
|
768
|
-
z.string().min(5)
|
|
769
|
-
z.string().max(100)
|
|
770
|
-
z.string().email()
|
|
771
|
-
z.string().url()
|
|
772
|
-
z.string().uuid()
|
|
773
|
-
z.string().regex(/pattern/)
|
|
774
|
-
z.string().datetime()
|
|
775
|
-
|
|
776
|
-
// Number validations
|
|
777
|
-
z.number()
|
|
778
|
-
z.number().int()
|
|
779
|
-
z.number().positive()
|
|
780
|
-
z.number().negative()
|
|
781
|
-
z.number().min(0)
|
|
782
|
-
z.number().max(100)
|
|
783
|
-
|
|
784
|
-
// Array validations
|
|
785
|
-
z.array(z.string())
|
|
786
|
-
z.array(z.number()).min(1)
|
|
787
|
-
z.array(z.string()).max(10)
|
|
788
|
-
|
|
789
|
-
// Object validations
|
|
790
|
-
z.object({ key: z.string() })
|
|
791
|
-
z.object({ key: z.string() }).strict() // No extra keys
|
|
792
|
-
z.object({ key: z.string() }).partial() // All keys optional
|
|
793
|
-
z.object({ key: z.string() }).required() // All keys required
|
|
794
|
-
|
|
795
|
-
// Optional and nullable
|
|
796
|
-
z.string().optional()
|
|
797
|
-
z.string().nullable()
|
|
798
|
-
z.string().nullish() // null | undefined
|
|
799
|
-
|
|
800
|
-
// Default values
|
|
801
|
-
z.string().default('default value')
|
|
802
|
-
z.number().default(0)
|
|
803
|
-
|
|
804
|
-
// Enums
|
|
805
|
-
z.enum(['option1', 'option2'])
|
|
806
|
-
z.nativeEnum(MyEnum)
|
|
807
|
-
|
|
808
|
-
// Unions and intersections
|
|
809
|
-
z.union([z.string(), z.number()])
|
|
810
|
-
z.string().or(z.number())
|
|
811
|
-
z.intersection(Schema1, Schema2)
|
|
812
|
-
Schema1.and(Schema2)
|
|
813
|
-
|
|
814
|
-
// Transformations
|
|
815
|
-
z.string().transform((val) => val.toLowerCase())
|
|
816
|
-
z.string().transform((val) => parseInt(val, 10))
|
|
817
|
-
|
|
818
|
-
// Refinements
|
|
819
|
-
z.string().refine((val) => val.length > 5, 'Must be longer than 5')
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
## Resources
|
|
823
|
-
|
|
824
|
-
- Official Docs: https://zod.dev
|
|
825
|
-
- GitHub: https://github.com/colinhacks/zod
|
|
826
|
-
- NPM: https://www.npmjs.com/package/zod
|
|
827
|
-
|
|
828
|
-
---
|
|
829
|
-
|
|
830
|
-
**Remember**: When an AutomatosX agent writes a TypeScript script, Zod validation is **MANDATORY** for all external data. No exceptions!
|