zodipus 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 +424 -0
- package/dist/cli.cjs +82 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/errors.cjs +48 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +58 -0
- package/dist/index.cjs +696 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/queryEngine.cjs +168 -0
- package/dist/queryEngine.cjs.map +1 -0
- package/dist/queryEngine.d.cts +145 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# Zodipus
|
|
2
|
+
|
|
3
|
+
> **Zodipus: solves your schema tragedies.**
|
|
4
|
+
|
|
5
|
+
A powerful Prisma generator that automatically creates:
|
|
6
|
+
1. **Clean Zod model schemas** (without relations) from your Prisma schema
|
|
7
|
+
2. **Enum schemas** with TypeScript types
|
|
8
|
+
3. **Relation metadata** for query engines and GraphQL resolvers
|
|
9
|
+
4. **Custom schema support** for complex JSON fields
|
|
10
|
+
5. **Query Engine** for composable, runtime-validated Prisma queries
|
|
11
|
+
|
|
12
|
+
## The Problem: Prisma Types vs. Zod Types
|
|
13
|
+
|
|
14
|
+
Prisma and Zod generate **incompatible types**, especially for JSON fields and enums:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
|
|
19
|
+
const user = await prisma.user.findFirst();
|
|
20
|
+
// Prisma type: { settings: JsonValue, role: $Enums.UserRole, ... }
|
|
21
|
+
|
|
22
|
+
function processUser(data: z.infer<typeof UserSchema>) {
|
|
23
|
+
// Zod type: { settings: { theme: 'light' | 'dark', ... }, role: 'USER' | 'ADMIN', ... }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ā TypeScript ERROR:
|
|
27
|
+
// Type 'JsonValue' is not assignable to type '{ theme?: "light" | "dark" | ... }'
|
|
28
|
+
// Type '$Enums.UserRole' is not assignable to type '"USER" | "ADMIN" | "MODERATOR"'
|
|
29
|
+
processUser(user);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Why types are incompatible:**
|
|
33
|
+
- **JSON fields**: Prisma uses generic `JsonValue`, but Zod schemas (via `@zodSchema`) define specific structures
|
|
34
|
+
- **Enums**: Prisma generates `$Enums.UserRole`, while Zod uses literal unions `"USER" | "ADMIN"`
|
|
35
|
+
- **Runtime validation**: Even if types matched, Prisma data is not validated ā malformed JSON or invalid values can slip through
|
|
36
|
+
|
|
37
|
+
## The Solution: Query Engine
|
|
38
|
+
|
|
39
|
+
Zodipus's **Query Engine** creates composable Prisma queries that automatically validate results:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { createRegistry } from 'zodipus/queryEngine';
|
|
43
|
+
import { models, modelRelations } from './generated/generated-index';
|
|
44
|
+
|
|
45
|
+
const registry = createRegistry({ models, relations: modelRelations });
|
|
46
|
+
const userQuery = registry.createQuery('user');
|
|
47
|
+
|
|
48
|
+
// Build a type-safe query with relations
|
|
49
|
+
const query = userQuery({
|
|
50
|
+
select: { id: true, email: true, name: true },
|
|
51
|
+
posts: { select: { title: true, published: true } },
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Execute with Prisma
|
|
55
|
+
const users = await prisma.user.findMany(query.query);
|
|
56
|
+
|
|
57
|
+
// ā
Validate and get properly typed results
|
|
58
|
+
const validatedUsers = query.array().parse(users);
|
|
59
|
+
// Type: { id: string, email: string, name: string | null, posts: { title: string, published: boolean }[] }[]
|
|
60
|
+
|
|
61
|
+
// Now safely pass to functions expecting validated data
|
|
62
|
+
processUsers(validatedUsers); // ā
Guaranteed valid!
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Using npm
|
|
71
|
+
npm install -D zodipus
|
|
72
|
+
|
|
73
|
+
# Using yarn
|
|
74
|
+
yarn add -D zodipus
|
|
75
|
+
|
|
76
|
+
# Using pnpm
|
|
77
|
+
pnpm add -D zodipus
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### 1. Add generator to your Prisma schema
|
|
83
|
+
|
|
84
|
+
```prisma
|
|
85
|
+
generator zodipus {
|
|
86
|
+
provider = "zodipus"
|
|
87
|
+
output = "./generated"
|
|
88
|
+
relationDepth = "5"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
model User {
|
|
92
|
+
id String @id @default(uuid())
|
|
93
|
+
email String @unique
|
|
94
|
+
name String?
|
|
95
|
+
role Role
|
|
96
|
+
posts Post[]
|
|
97
|
+
createdAt DateTime @default(now())
|
|
98
|
+
updatedAt DateTime @updatedAt
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
enum Role {
|
|
102
|
+
USER
|
|
103
|
+
ADMIN
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
model Post {
|
|
107
|
+
id String @id @default(uuid())
|
|
108
|
+
title String
|
|
109
|
+
content String?
|
|
110
|
+
published Boolean @default(false)
|
|
111
|
+
author User @relation(fields: [authorId], references: [id])
|
|
112
|
+
authorId String
|
|
113
|
+
|
|
114
|
+
/// @zodSchema PostMetadataSchema
|
|
115
|
+
metadata Json?
|
|
116
|
+
|
|
117
|
+
createdAt DateTime @default(now())
|
|
118
|
+
updatedAt DateTime @updatedAt
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2. Run Prisma generate
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pnpm prisma generate
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. Use generated schemas
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { UserSchema, RoleSchema, PostSchema } from './generated';
|
|
132
|
+
|
|
133
|
+
// Parse and validate data
|
|
134
|
+
const user = UserSchema.parse({
|
|
135
|
+
id: '123',
|
|
136
|
+
email: 'user@example.com',
|
|
137
|
+
role: 'ADMIN',
|
|
138
|
+
createdAt: new Date(),
|
|
139
|
+
updatedAt: new Date()
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Use enums
|
|
143
|
+
const role = RoleSchema.parse('ADMIN');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Features
|
|
147
|
+
|
|
148
|
+
### Clean Zod Schemas
|
|
149
|
+
|
|
150
|
+
Generate schemas containing only scalar fields - no relation clutter:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
export const UserSchema = z.object({
|
|
154
|
+
id: z.string(),
|
|
155
|
+
email: z.string(),
|
|
156
|
+
name: z.string().optional().nullable(),
|
|
157
|
+
role: RoleSchema,
|
|
158
|
+
createdAt: z.coerce.date(),
|
|
159
|
+
updatedAt: z.coerce.date()
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Custom JSON Field Schemas
|
|
164
|
+
|
|
165
|
+
Add type-safe validation for JSON fields using `@zodSchema` annotations:
|
|
166
|
+
|
|
167
|
+
```prisma
|
|
168
|
+
model Post {
|
|
169
|
+
/// @zodSchema PostMetadataSchema
|
|
170
|
+
metadata Json?
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Then define in `generated/custom-schemas.ts`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
export const PostMetadataSchema = z.object({
|
|
178
|
+
tags: z.array(z.string()),
|
|
179
|
+
views: z.number(),
|
|
180
|
+
featured: z.boolean().optional(),
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Relation Metadata
|
|
185
|
+
|
|
186
|
+
Extracted automatically for building query engines:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const relations = {
|
|
190
|
+
user: {
|
|
191
|
+
posts: {
|
|
192
|
+
type: "post" as const,
|
|
193
|
+
isArray: true,
|
|
194
|
+
relations: { /* nested */ }
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} as const;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Query Engine Integration
|
|
201
|
+
|
|
202
|
+
Zodipus includes a powerful Query Engine that works seamlessly with your generated schemas:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { createRegistry } from 'zodipus/queryEngine';
|
|
206
|
+
import { models, modelRelations } from './generated/generated-index';
|
|
207
|
+
|
|
208
|
+
// Create registry
|
|
209
|
+
const registry = createRegistry({
|
|
210
|
+
models,
|
|
211
|
+
relations: modelRelations,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Create query builders
|
|
215
|
+
export const userQuery = registry.createQuery('user');
|
|
216
|
+
export const postQuery = registry.createQuery('post');
|
|
217
|
+
|
|
218
|
+
// Use with Prisma
|
|
219
|
+
const query = userQuery({
|
|
220
|
+
select: { id: true, email: true, name: true },
|
|
221
|
+
posts: {
|
|
222
|
+
select: { title: true, published: true },
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Execute and validate
|
|
227
|
+
const users = await prisma.user.findMany(query.query);
|
|
228
|
+
const validated = query.array().parse(users);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Features:**
|
|
232
|
+
- ā
Complete compile-time type safety
|
|
233
|
+
- ā
Runtime validation with Zod
|
|
234
|
+
- ā
Automatic relation handling
|
|
235
|
+
- ā
Nested query support
|
|
236
|
+
- ā
Selective validation for performance
|
|
237
|
+
|
|
238
|
+
See [Query Engine Documentation](./queryEngine.md) for complete API reference and examples.
|
|
239
|
+
|
|
240
|
+
## Configuration
|
|
241
|
+
|
|
242
|
+
Add options to your Prisma generator configuration:
|
|
243
|
+
|
|
244
|
+
```prisma
|
|
245
|
+
generator zodipus {
|
|
246
|
+
provider = "zodipus"
|
|
247
|
+
output = "./generated"
|
|
248
|
+
relationDepth = "5"
|
|
249
|
+
dateFormat = "coerce" // or "string"
|
|
250
|
+
passthroughEnabled = "false" // or "true"
|
|
251
|
+
debug = "false" // or "true"
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Options
|
|
256
|
+
|
|
257
|
+
| Option | Type | Default | Description |
|
|
258
|
+
|--------|------|---------|-------------|
|
|
259
|
+
| `provider` | string | Required | Set to `"zodipus"` |
|
|
260
|
+
| `output` | string | Required | Output directory for generated files |
|
|
261
|
+
| `relationDepth` | string | `"5"` | Maximum depth for nested relations |
|
|
262
|
+
| `dateFormat` | string | `"coerce"` | DateTime handling: `"coerce"` uses `z.coerce.date()`, `"string"` uses `z.string().datetime()` |
|
|
263
|
+
| `passthroughEnabled` | string | `"false"` | When `"true"`, objects allow extra keys (passthrough mode). When `"false"` (default), unknown keys are stripped. |
|
|
264
|
+
| `debug` | string | `"false"` | Enable debug logging during generation |
|
|
265
|
+
|
|
266
|
+
### DateTime Format
|
|
267
|
+
|
|
268
|
+
Choose how DateTime fields are validated:
|
|
269
|
+
|
|
270
|
+
- **`coerce` (default)**: Uses `z.coerce.date()` - accepts Date objects, ISO strings, and timestamps
|
|
271
|
+
- **`string`**: Uses `z.string().datetime()` - only accepts ISO 8601 datetime strings
|
|
272
|
+
|
|
273
|
+
```prisma
|
|
274
|
+
// dateFormat = "coerce" (default)
|
|
275
|
+
generator zodipus {
|
|
276
|
+
dateFormat = "coerce"
|
|
277
|
+
}
|
|
278
|
+
// Generates: createdAt: z.coerce.date()
|
|
279
|
+
|
|
280
|
+
// dateFormat = "string"
|
|
281
|
+
generator zodipus {
|
|
282
|
+
dateFormat = "string"
|
|
283
|
+
}
|
|
284
|
+
// Generates: createdAt: z.string().datetime()
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Passthrough Mode
|
|
288
|
+
|
|
289
|
+
Control how objects handle extra properties:
|
|
290
|
+
|
|
291
|
+
- **`false` (default)**: Strict mode - unknown keys are stripped during parsing
|
|
292
|
+
- **`true`**: Passthrough mode - unknown keys are preserved
|
|
293
|
+
|
|
294
|
+
```prisma
|
|
295
|
+
// passthroughEnabled = "false" (default)
|
|
296
|
+
generator zodipus {
|
|
297
|
+
passthroughEnabled = "false"
|
|
298
|
+
}
|
|
299
|
+
// Generates: z.object({ ... })
|
|
300
|
+
|
|
301
|
+
// passthroughEnabled = "true"
|
|
302
|
+
generator zodipus {
|
|
303
|
+
passthroughEnabled = "true"
|
|
304
|
+
}
|
|
305
|
+
// Generates: z.object({ ... }).passthrough()
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Generated Files
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
generated/
|
|
312
|
+
āāā enums.ts # Prisma enums as Zod schemas
|
|
313
|
+
āāā models.ts # Clean model schemas
|
|
314
|
+
āāā custom-schemas.ts # Custom JSON field schemas
|
|
315
|
+
āāā generated-relations.ts # Relation metadata
|
|
316
|
+
āāā generated-index.ts # Exports
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Examples
|
|
320
|
+
|
|
321
|
+
### API Validation
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { UserSchema } from './generated';
|
|
325
|
+
|
|
326
|
+
app.post('/users', (req, res) => {
|
|
327
|
+
const user = UserSchema.parse(req.body);
|
|
328
|
+
await db.user.create({ data: user });
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Partial Updates
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
const UpdateUserSchema = UserSchema.partial();
|
|
336
|
+
const updates = UpdateUserSchema.parse(req.body);
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## API Reference
|
|
340
|
+
|
|
341
|
+
### Main Exports (`zodipus`)
|
|
342
|
+
|
|
343
|
+
| Export | Type | Description |
|
|
344
|
+
|--------|------|-------------|
|
|
345
|
+
| `createRegistry` | Function | Creates a query registry for type-safe Prisma queries |
|
|
346
|
+
|
|
347
|
+
### Query Engine (`zodipus/queryEngine`)
|
|
348
|
+
|
|
349
|
+
| Export | Type | Description |
|
|
350
|
+
|--------|------|-------------|
|
|
351
|
+
| `createRegistry(config)` | Function | Creates query registry with models and relations |
|
|
352
|
+
| `ModelRegistry` | Type | Generic type for model schema map |
|
|
353
|
+
| `ModelRelations` | Type | Generic type for relation metadata |
|
|
354
|
+
| `RelationConfig` | Type | Configuration for a single relation |
|
|
355
|
+
| `QueryExecutor` | Type | Return type of query builders |
|
|
356
|
+
| `SafeParseResult<T>` | Type | Result of `.safeParse()` operations |
|
|
357
|
+
|
|
358
|
+
#### `createRegistry(config)`
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
function createRegistry<TModels, TRelations>(config: {
|
|
362
|
+
models: TModels;
|
|
363
|
+
relations: TRelations;
|
|
364
|
+
}): {
|
|
365
|
+
createQuery: (model: keyof TModels) => QueryBuilder;
|
|
366
|
+
};
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### `QueryExecutor`
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
interface QueryExecutor<T> {
|
|
373
|
+
query: { select: ... } | { include: ... }; // Pass to Prisma
|
|
374
|
+
parse(data: unknown): T; // Throws on invalid
|
|
375
|
+
safeParse(data: unknown): SafeParseResult<T>; // Returns {success, data/error}
|
|
376
|
+
array(): {
|
|
377
|
+
parse(data: unknown[]): T[];
|
|
378
|
+
safeParse(data: unknown[]): SafeParseResult<T[]>;
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Errors (`zodipus/errors`)
|
|
384
|
+
|
|
385
|
+
| Export | Type | Description |
|
|
386
|
+
|--------|------|-------------|
|
|
387
|
+
| `ZodipusError` | Class | Base error class |
|
|
388
|
+
| `ZodipusValidationError` | Class | Thrown when validation fails |
|
|
389
|
+
| `ZodipusGeneratorError` | Class | Thrown during schema generation |
|
|
390
|
+
| `ValidationErrorContext` | Type | Context included in validation errors |
|
|
391
|
+
|
|
392
|
+
#### `ZodipusValidationError`
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
class ZodipusValidationError extends ZodipusError {
|
|
396
|
+
context: {
|
|
397
|
+
model: string; // Model being validated
|
|
398
|
+
field?: string; // Field that failed
|
|
399
|
+
expected?: string; // Expected type/value
|
|
400
|
+
received?: string; // Received type/value
|
|
401
|
+
path?: (string | number)[]; // Path to field
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Generated Files
|
|
407
|
+
|
|
408
|
+
| File | Exports |
|
|
409
|
+
|------|---------|
|
|
410
|
+
| `models.ts` | `UserSchema`, `PostSchema`, etc. (Zod schemas for each model) |
|
|
411
|
+
| `enums.ts` | `RoleSchema`, `StatusSchema`, etc. (Zod schemas for enums) |
|
|
412
|
+
| `generated-relations.ts` | `modelRelations` (relation metadata for Query Engine) |
|
|
413
|
+
| `generated-index.ts` | Re-exports all above + `models` namespace |
|
|
414
|
+
| `custom-schemas.ts` | Your custom JSON field schemas (created once, not overwritten) |
|
|
415
|
+
|
|
416
|
+
### CLI
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
# Inspect schema structure
|
|
420
|
+
zodipus inspect <schema.prisma> [--models] [--enums] [--relations] [--json]
|
|
421
|
+
|
|
422
|
+
# Generate with dry-run
|
|
423
|
+
zodipus generate <schema.prisma> [--dry-run]
|
|
424
|
+
```
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var child_process = require('child_process');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var commander = require('commander');
|
|
7
|
+
|
|
8
|
+
var program = new commander.Command();
|
|
9
|
+
program.name("zodipus").description("Zodipus: solves your schema tragedies").version("0.1.0");
|
|
10
|
+
function runPrismaGenerate(schemaPath, env) {
|
|
11
|
+
return new Promise((resolvePromise, reject) => {
|
|
12
|
+
const resolvedPath = path.resolve(process.cwd(), schemaPath);
|
|
13
|
+
console.log("\n\u{1F680} Running Zodipus via Prisma Generator...");
|
|
14
|
+
console.log(`\u{1F4C4} Schema: ${resolvedPath}
|
|
15
|
+
`);
|
|
16
|
+
const child = child_process.spawn(
|
|
17
|
+
"pnpm dlx",
|
|
18
|
+
[
|
|
19
|
+
"prisma",
|
|
20
|
+
"generate",
|
|
21
|
+
"--schema",
|
|
22
|
+
resolvedPath,
|
|
23
|
+
"--generator",
|
|
24
|
+
"zodipus"
|
|
25
|
+
// Only run our generator
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
stdio: "inherit",
|
|
29
|
+
env: {
|
|
30
|
+
...process.env,
|
|
31
|
+
...env
|
|
32
|
+
},
|
|
33
|
+
shell: true
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
child.on("close", (code) => {
|
|
37
|
+
if (code === 0) {
|
|
38
|
+
resolvePromise();
|
|
39
|
+
} else {
|
|
40
|
+
reject(new Error(`Prisma generate failed with exit code ${code}`));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
child.on("error", (err) => {
|
|
44
|
+
reject(err);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
program.command("inspect").description("Inspect a Prisma schema and show its structure").argument("<schema>", "Path to the Prisma schema file").option("-m, --models", "Show only models").option("-e, --enums", "Show only enums").option("-r, --relations", "Show only relations").option("--json", "Output as JSON").action(async (schemaPath, options) => {
|
|
49
|
+
const flags = [];
|
|
50
|
+
if (options.models) flags.push("models");
|
|
51
|
+
if (options.enums) flags.push("enums");
|
|
52
|
+
if (options.relations) flags.push("relations");
|
|
53
|
+
try {
|
|
54
|
+
await runPrismaGenerate(schemaPath, {
|
|
55
|
+
ZODIPUS_CMD: "inspect",
|
|
56
|
+
ZODIPUS_INSPECT_FLAGS: flags.join(","),
|
|
57
|
+
ZODIPUS_INSPECT_JSON: options.json ? "true" : "false"
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("\n\u274C Inspect failed:", error instanceof Error ? error.message : error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
program.command("generate").description("Generate Zod schemas from a Prisma schema").argument("<schema>", "Path to the Prisma schema file").option("-o, --output <dir>", "Output directory (defaults to schema config)").option("--dry-run", "Preview output without writing files").action(async (schemaPath, options) => {
|
|
65
|
+
try {
|
|
66
|
+
if (options.output) {
|
|
67
|
+
console.warn(
|
|
68
|
+
"\u26A0\uFE0F Note: --output flag is currently ignored by the wrapper. Configure output in your schema.prisma file."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
await runPrismaGenerate(schemaPath, {
|
|
72
|
+
ZODIPUS_CMD: "generate",
|
|
73
|
+
ZODIPUS_DRY_RUN: options.dryRun ? "true" : "false"
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("\n\u274C Generation failed:", error instanceof Error ? error.message : error);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
program.parse();
|
|
81
|
+
//# sourceMappingURL=cli.cjs.map
|
|
82
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"names":["Command","resolve","spawn"],"mappings":"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","file":"cli.cjs","sourcesContent":["#!/usr/bin/env node\n/**\n * Zodipus CLI\n *\n * Command-line interface for schema inspection and generation.\n * Wraps `prisma generate` with special environment variables to trigger\n * inspection or dry-run modes within the native generator execution.\n */\n\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\nimport { Command } from 'commander';\n\nconst program = new Command();\n\nprogram.name('zodipus').description('Zodipus: solves your schema tragedies').version('0.1.0');\n\n/**\n * Execute prisma generate with custom env vars\n */\nfunction runPrismaGenerate(schemaPath: string, env: Record<string, string>): Promise<void> {\n return new Promise((resolvePromise, reject) => {\n const resolvedPath = resolve(process.cwd(), schemaPath);\n console.log('\\nš Running Zodipus via Prisma Generator...');\n console.log(`š Schema: ${resolvedPath}\\n`);\n\n const child = spawn(\n 'pnpm dlx',\n [\n 'prisma',\n 'generate',\n '--schema',\n resolvedPath,\n '--generator',\n 'zodipus', // Only run our generator\n ],\n {\n stdio: 'inherit',\n env: {\n ...process.env,\n ...env,\n },\n shell: true,\n }\n );\n\n child.on('close', (code) => {\n if (code === 0) {\n resolvePromise();\n } else {\n reject(new Error(`Prisma generate failed with exit code ${code}`));\n }\n });\n\n child.on('error', (err) => {\n reject(err);\n });\n });\n}\n\n/**\n * Inspect command - shows models, enums, and relations from a Prisma schema\n */\nprogram\n .command('inspect')\n .description('Inspect a Prisma schema and show its structure')\n .argument('<schema>', 'Path to the Prisma schema file')\n .option('-m, --models', 'Show only models')\n .option('-e, --enums', 'Show only enums')\n .option('-r, --relations', 'Show only relations')\n .option('--json', 'Output as JSON')\n .action(async (schemaPath: string, options) => {\n const flags: string[] = [];\n if (options.models) flags.push('models');\n if (options.enums) flags.push('enums');\n if (options.relations) flags.push('relations');\n\n try {\n await runPrismaGenerate(schemaPath, {\n ZODIPUS_CMD: 'inspect',\n ZODIPUS_INSPECT_FLAGS: flags.join(','),\n ZODIPUS_INSPECT_JSON: options.json ? 'true' : 'false',\n });\n } catch (error) {\n console.error('\\nā Inspect failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\n/**\n * Generate command with --dry-run support\n */\nprogram\n .command('generate')\n .description('Generate Zod schemas from a Prisma schema')\n .argument('<schema>', 'Path to the Prisma schema file')\n .option('-o, --output <dir>', 'Output directory (defaults to schema config)')\n .option('--dry-run', 'Preview output without writing files')\n .action(async (schemaPath: string, options) => {\n try {\n if (options.output) {\n console.warn(\n 'ā ļø Note: --output flag is currently ignored by the wrapper. Configure output in your schema.prisma file.'\n );\n }\n\n await runPrismaGenerate(schemaPath, {\n ZODIPUS_CMD: 'generate',\n ZODIPUS_DRY_RUN: options.dryRun ? 'true' : 'false',\n });\n } catch (error) {\n console.error('\\nā Generation failed:', error instanceof Error ? error.message : error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n"]}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/errors.cjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var ZodipusError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "ZodipusError";
|
|
8
|
+
if (Error.captureStackTrace) {
|
|
9
|
+
Error.captureStackTrace(this, this.constructor);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var ZodipusValidationError = class extends ZodipusError {
|
|
14
|
+
context;
|
|
15
|
+
constructor(message, context) {
|
|
16
|
+
const contextInfo = [
|
|
17
|
+
`Model: ${context.model}`,
|
|
18
|
+
context.field ? `Field: ${context.field}` : null,
|
|
19
|
+
context.expected ? `Expected: ${context.expected}` : null,
|
|
20
|
+
context.received ? `Received: ${context.received}` : null,
|
|
21
|
+
context.path?.length ? `Path: ${context.path.join(".")}` : null
|
|
22
|
+
].filter(Boolean).join(", ");
|
|
23
|
+
super(`${message} (${contextInfo})`);
|
|
24
|
+
this.name = "ZodipusValidationError";
|
|
25
|
+
this.context = context;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var ZodipusGeneratorError = class extends ZodipusError {
|
|
29
|
+
schemaPath;
|
|
30
|
+
modelName;
|
|
31
|
+
constructor(message, options) {
|
|
32
|
+
const contextParts = [
|
|
33
|
+
options?.schemaPath ? `Schema: ${options.schemaPath}` : null,
|
|
34
|
+
options?.modelName ? `Model: ${options.modelName}` : null
|
|
35
|
+
].filter(Boolean);
|
|
36
|
+
const fullMessage = contextParts.length ? `${message} (${contextParts.join(", ")})` : message;
|
|
37
|
+
super(fullMessage);
|
|
38
|
+
this.name = "ZodipusGeneratorError";
|
|
39
|
+
this.schemaPath = options?.schemaPath;
|
|
40
|
+
this.modelName = options?.modelName;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
exports.ZodipusError = ZodipusError;
|
|
45
|
+
exports.ZodipusGeneratorError = ZodipusGeneratorError;
|
|
46
|
+
exports.ZodipusValidationError = ZodipusValidationError;
|
|
47
|
+
//# sourceMappingURL=errors.cjs.map
|
|
48
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";;;AAUO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EACtC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAEZ,IAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,IAChD;AAAA,EACF;AACF;AAgCO,IAAM,sBAAA,GAAN,cAAqC,YAAA,CAAa;AAAA,EAC9C,OAAA;AAAA,EAET,WAAA,CAAY,SAAiB,OAAA,EAAiC;AAC5D,IAAA,MAAM,WAAA,GAAc;AAAA,MAClB,CAAA,OAAA,EAAU,QAAQ,KAAK,CAAA,CAAA;AAAA,MACvB,OAAA,CAAQ,KAAA,GAAQ,CAAA,OAAA,EAAU,OAAA,CAAQ,KAAK,CAAA,CAAA,GAAK,IAAA;AAAA,MAC5C,OAAA,CAAQ,QAAA,GAAW,CAAA,UAAA,EAAa,OAAA,CAAQ,QAAQ,CAAA,CAAA,GAAK,IAAA;AAAA,MACrD,OAAA,CAAQ,QAAA,GAAW,CAAA,UAAA,EAAa,OAAA,CAAQ,QAAQ,CAAA,CAAA,GAAK,IAAA;AAAA,MACrD,OAAA,CAAQ,MAAM,MAAA,GAAS,CAAA,MAAA,EAAS,QAAQ,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,GAAK;AAAA,KAC7D,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,IAAI,CAAA;AAEZ,IAAA,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,EAAA,EAAK,WAAW,CAAA,CAAA,CAAG,CAAA;AACnC,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AACF;AAKO,IAAM,qBAAA,GAAN,cAAoC,YAAA,CAAa;AAAA,EAC7C,UAAA;AAAA,EACA,SAAA;AAAA,EAET,WAAA,CAAY,SAAiB,OAAA,EAAuD;AAClF,IAAA,MAAM,YAAA,GAAe;AAAA,MACnB,OAAA,EAAS,UAAA,GAAa,CAAA,QAAA,EAAW,OAAA,CAAQ,UAAU,CAAA,CAAA,GAAK,IAAA;AAAA,MACxD,OAAA,EAAS,SAAA,GAAY,CAAA,OAAA,EAAU,OAAA,CAAQ,SAAS,CAAA,CAAA,GAAK;AAAA,KACvD,CAAE,OAAO,OAAO,CAAA;AAEhB,IAAA,MAAM,WAAA,GAAc,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG,OAAO,KAAK,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA,GAAM,OAAA;AAEtF,IAAA,KAAA,CAAM,WAAW,CAAA;AACjB,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,aAAa,OAAA,EAAS,UAAA;AAC3B,IAAA,IAAA,CAAK,YAAY,OAAA,EAAS,SAAA;AAAA,EAC5B;AACF","file":"errors.cjs","sourcesContent":["/**\n * Custom error classes for Zodipus\n *\n * These errors provide better context for debugging validation\n * and generation issues.\n */\n\n/**\n * Base error class for all Zodipus errors\n */\nexport class ZodipusError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ZodipusError';\n // Maintains proper stack trace for where error was thrown (V8 engines)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Context for validation errors\n */\nexport interface ValidationErrorContext {\n /** The model being validated */\n model: string;\n /** The field that failed validation (if applicable) */\n field?: string;\n /** The expected type or value */\n expected?: string;\n /** The received type or value */\n received?: string;\n /** The path to the field in nested objects */\n path?: (string | number)[];\n}\n\n/**\n * Error thrown when data validation fails\n *\n * @example\n * ```typescript\n * throw new ZodipusValidationError('Invalid data type', {\n * model: 'User',\n * field: 'email',\n * expected: 'string',\n * received: 'number',\n * path: ['user', 'email'],\n * });\n * ```\n */\nexport class ZodipusValidationError extends ZodipusError {\n readonly context: ValidationErrorContext;\n\n constructor(message: string, context: ValidationErrorContext) {\n const contextInfo = [\n `Model: ${context.model}`,\n context.field ? `Field: ${context.field}` : null,\n context.expected ? `Expected: ${context.expected}` : null,\n context.received ? `Received: ${context.received}` : null,\n context.path?.length ? `Path: ${context.path.join('.')}` : null,\n ]\n .filter(Boolean)\n .join(', ');\n\n super(`${message} (${contextInfo})`);\n this.name = 'ZodipusValidationError';\n this.context = context;\n }\n}\n\n/**\n * Error thrown during schema generation\n */\nexport class ZodipusGeneratorError extends ZodipusError {\n readonly schemaPath?: string;\n readonly modelName?: string;\n\n constructor(message: string, options?: { schemaPath?: string; modelName?: string }) {\n const contextParts = [\n options?.schemaPath ? `Schema: ${options.schemaPath}` : null,\n options?.modelName ? `Model: ${options.modelName}` : null,\n ].filter(Boolean);\n\n const fullMessage = contextParts.length ? `${message} (${contextParts.join(', ')})` : message;\n\n super(fullMessage);\n this.name = 'ZodipusGeneratorError';\n this.schemaPath = options?.schemaPath;\n this.modelName = options?.modelName;\n }\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for Zodipus
|
|
3
|
+
*
|
|
4
|
+
* These errors provide better context for debugging validation
|
|
5
|
+
* and generation issues.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Base error class for all Zodipus errors
|
|
9
|
+
*/
|
|
10
|
+
declare class ZodipusError extends Error {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Context for validation errors
|
|
15
|
+
*/
|
|
16
|
+
interface ValidationErrorContext {
|
|
17
|
+
/** The model being validated */
|
|
18
|
+
model: string;
|
|
19
|
+
/** The field that failed validation (if applicable) */
|
|
20
|
+
field?: string;
|
|
21
|
+
/** The expected type or value */
|
|
22
|
+
expected?: string;
|
|
23
|
+
/** The received type or value */
|
|
24
|
+
received?: string;
|
|
25
|
+
/** The path to the field in nested objects */
|
|
26
|
+
path?: (string | number)[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Error thrown when data validation fails
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* throw new ZodipusValidationError('Invalid data type', {
|
|
34
|
+
* model: 'User',
|
|
35
|
+
* field: 'email',
|
|
36
|
+
* expected: 'string',
|
|
37
|
+
* received: 'number',
|
|
38
|
+
* path: ['user', 'email'],
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare class ZodipusValidationError extends ZodipusError {
|
|
43
|
+
readonly context: ValidationErrorContext;
|
|
44
|
+
constructor(message: string, context: ValidationErrorContext);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Error thrown during schema generation
|
|
48
|
+
*/
|
|
49
|
+
declare class ZodipusGeneratorError extends ZodipusError {
|
|
50
|
+
readonly schemaPath?: string;
|
|
51
|
+
readonly modelName?: string;
|
|
52
|
+
constructor(message: string, options?: {
|
|
53
|
+
schemaPath?: string;
|
|
54
|
+
modelName?: string;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { type ValidationErrorContext, ZodipusError, ZodipusGeneratorError, ZodipusValidationError };
|