mutano 2.6.7 → 3.0.1
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 +118 -580
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -1,180 +1,24 @@
|
|
|
1
1
|
# Mutano
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Convert database schemas to TypeScript types, Zod schemas, or Kysely definitions.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- Generates Zod schemas, Typescript interfaces or Kysely type definitions for MySQL, PostgreSQL, SQLite, and Prisma schemas
|
|
8
|
-
- Supports camelCase conversion
|
|
9
|
-
- Handles nullable, default, auto-increment and enum fields
|
|
10
|
-
- Supports custom type overrides via configuration or database comments
|
|
11
|
-
- Intelligently handles field nullability based on operation type (table, insertable, updateable, selectable)
|
|
12
|
-
- All fields in updateable schemas are automatically made optional
|
|
5
|
+
**Supports:** MySQL, PostgreSQL, SQLite, Prisma • **Features:** Views, Magic Comments, Type Overrides, Multiple Outputs
|
|
13
6
|
|
|
14
7
|
## Installation
|
|
15
8
|
|
|
16
|
-
Install `mutano` with npm
|
|
17
|
-
|
|
18
9
|
```bash
|
|
19
10
|
npm install mutano
|
|
20
11
|
```
|
|
21
12
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
Create user table:
|
|
25
|
-
|
|
26
|
-
```sql
|
|
27
|
-
CREATE TABLE `user` (
|
|
28
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
29
|
-
`name` varchar(255) NOT NULL COMMENT '@zod(z.string().min(10).max(255))', -- this will override the Zod type
|
|
30
|
-
`username` varchar(255) NOT NULL,
|
|
31
|
-
`password` varchar(255) NOT NULL,
|
|
32
|
-
`profile_picture` varchar(255) DEFAULT NULL,
|
|
33
|
-
`metadata` json NOT NULL COMMENT '@ts(Record<string, unknown>) @kysely(Record<string, string>)', -- this will override the TypeScript and Kysely type
|
|
34
|
-
`role` enum('admin','user') NOT NULL,
|
|
35
|
-
PRIMARY KEY (`id`)
|
|
36
|
-
);
|
|
37
|
-
```
|
|
38
|
-
Use the mutano API:
|
|
39
|
-
|
|
40
|
-
### MySQL Example with Zod Schemas
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { generate } from 'mutano'
|
|
44
|
-
|
|
45
|
-
await generate({
|
|
46
|
-
origin: {
|
|
47
|
-
type: 'mysql',
|
|
48
|
-
host: '127.0.0.1',
|
|
49
|
-
port: 3306,
|
|
50
|
-
user: 'root',
|
|
51
|
-
password: 'secret',
|
|
52
|
-
database: 'myapp',
|
|
53
|
-
overrideTypes: {
|
|
54
|
-
json: 'z.record(z.string())'
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
destinations: [{
|
|
58
|
-
type: 'zod',
|
|
59
|
-
useDateType: true,
|
|
60
|
-
useTrim: false,
|
|
61
|
-
nullish: false, // When true, nullable fields use nullish() instead of nullable()
|
|
62
|
-
folder: './generated',
|
|
63
|
-
suffix: 'schema'
|
|
64
|
-
}]
|
|
65
|
-
})
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### MySQL Example with TypeScript Type Aliases (Instead of Interfaces)
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
import { generate } from 'mutano'
|
|
72
|
-
|
|
73
|
-
await generate({
|
|
74
|
-
origin: {
|
|
75
|
-
type: 'mysql',
|
|
76
|
-
host: '127.0.0.1',
|
|
77
|
-
port: 3306,
|
|
78
|
-
user: 'root',
|
|
79
|
-
password: 'secret',
|
|
80
|
-
database: 'myapp',
|
|
81
|
-
overrideTypes: {
|
|
82
|
-
json: 'z.record(z.string())'
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
destinations: [{
|
|
86
|
-
type: 'ts',
|
|
87
|
-
modelType: 'type', // Generate TypeScript type aliases instead of interfaces
|
|
88
|
-
folder: './types',
|
|
89
|
-
suffix: 'types'
|
|
90
|
-
}]
|
|
91
|
-
})
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### MySQL Example with Custom Header for TypeScript
|
|
13
|
+
## Quick Start
|
|
95
14
|
|
|
96
15
|
```typescript
|
|
97
16
|
import { generate } from 'mutano'
|
|
98
17
|
|
|
18
|
+
// Basic usage
|
|
99
19
|
await generate({
|
|
100
20
|
origin: {
|
|
101
|
-
type: 'mysql',
|
|
102
|
-
host: '127.0.0.1',
|
|
103
|
-
port: 3306,
|
|
104
|
-
user: 'root',
|
|
105
|
-
password: 'secret',
|
|
106
|
-
database: 'myapp',
|
|
107
|
-
overrideTypes: {
|
|
108
|
-
json: 'z.record(z.string())'
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
destinations: [{
|
|
112
|
-
type: 'ts',
|
|
113
|
-
header: "import type { CustomType } from './types';\nimport type { BaseModel } from './models';"
|
|
114
|
-
}]
|
|
115
|
-
})
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### MySQL Example with Custom Header for Zod
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import { generate } from 'mutano'
|
|
122
|
-
|
|
123
|
-
await generate({
|
|
124
|
-
origin: {
|
|
125
|
-
type: 'mysql',
|
|
126
|
-
host: '127.0.0.1',
|
|
127
|
-
port: 3306,
|
|
128
|
-
user: 'root',
|
|
129
|
-
password: 'secret',
|
|
130
|
-
database: 'myapp',
|
|
131
|
-
overrideTypes: {
|
|
132
|
-
json: 'z.record(z.string())'
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
destinations: [{
|
|
136
|
-
type: 'zod',
|
|
137
|
-
header: "import { z } from 'zod';\nimport { CustomValidator } from './validators';"
|
|
138
|
-
}]
|
|
139
|
-
})
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### MySQL Example with Kysely Type Definitions (Custom Schema Name)
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { generate } from 'mutano'
|
|
146
|
-
|
|
147
|
-
await generate({
|
|
148
|
-
origin: {
|
|
149
|
-
type: 'mysql',
|
|
150
|
-
host: '127.0.0.1',
|
|
151
|
-
port: 3306,
|
|
152
|
-
user: 'root',
|
|
153
|
-
password: 'secret',
|
|
154
|
-
database: 'myapp',
|
|
155
|
-
overrideTypes: {
|
|
156
|
-
json: 'z.record(z.string())'
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
destinations: [{
|
|
160
|
-
type: 'kysely',
|
|
161
|
-
schemaName: 'Database', // Default is 'DB'
|
|
162
|
-
header: "import { Generated, ColumnType } from 'kysely';\nimport { CustomTypes } from './types';",
|
|
163
|
-
folder: './db/types',
|
|
164
|
-
suffix: 'db'
|
|
165
|
-
}]
|
|
166
|
-
})
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Example with Dry Run Option
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
import { generate } from 'mutano'
|
|
173
|
-
|
|
174
|
-
// Generate without writing to disk
|
|
175
|
-
const output = await generate({
|
|
176
|
-
origin: {
|
|
177
|
-
type: 'mysql',
|
|
21
|
+
type: 'mysql', // or 'postgres', 'sqlite', 'prisma'
|
|
178
22
|
host: '127.0.0.1',
|
|
179
23
|
port: 3306,
|
|
180
24
|
user: 'root',
|
|
@@ -182,504 +26,198 @@ const output = await generate({
|
|
|
182
26
|
database: 'myapp'
|
|
183
27
|
},
|
|
184
28
|
destinations: [{
|
|
185
|
-
type: 'zod'
|
|
186
|
-
|
|
187
|
-
dryRun: true // Return content and don't write to files
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
// Output is an object where keys are filenames and values are file content
|
|
191
|
-
console.log(Object.keys(output)) // ['user.ts', 'product.ts', ...]
|
|
192
|
-
|
|
193
|
-
// You can access the content for a specific file
|
|
194
|
-
console.log(output['user.ts'])
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### PostgreSQL Example
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
import { generate } from 'mutano'
|
|
201
|
-
|
|
202
|
-
await generate({
|
|
203
|
-
origin: {
|
|
204
|
-
type: 'postgres',
|
|
205
|
-
host: '127.0.0.1',
|
|
206
|
-
port: 5432,
|
|
207
|
-
user: 'postgres',
|
|
208
|
-
password: 'secret',
|
|
209
|
-
database: 'myapp',
|
|
210
|
-
schema: 'public', // optional, defaults to 'public'
|
|
211
|
-
overrideTypes: {
|
|
212
|
-
jsonb: 'z.record(z.string())'
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
destinations: [{
|
|
216
|
-
type: 'zod',
|
|
217
|
-
useDateType: true
|
|
29
|
+
type: 'zod', // or 'ts', 'kysely'
|
|
30
|
+
folder: './generated'
|
|
218
31
|
}]
|
|
219
32
|
})
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### SQLite Example
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
import { generate } from 'mutano'
|
|
226
33
|
|
|
34
|
+
// Multiple outputs
|
|
227
35
|
await generate({
|
|
228
|
-
origin: {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
},
|
|
235
|
-
destinations: [{
|
|
236
|
-
type: 'ts'
|
|
237
|
-
}]
|
|
36
|
+
origin: { /* ... */ },
|
|
37
|
+
destinations: [
|
|
38
|
+
{ type: 'zod', folder: './zod' },
|
|
39
|
+
{ type: 'ts', folder: './types' },
|
|
40
|
+
{ type: 'kysely', outFile: './db.ts' }
|
|
41
|
+
]
|
|
238
42
|
})
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Example with Multiple Destinations
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
import { generate } from 'mutano'
|
|
245
43
|
|
|
44
|
+
// With views support
|
|
246
45
|
await generate({
|
|
247
|
-
origin: {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
password: 'secret',
|
|
253
|
-
database: 'myapp',
|
|
254
|
-
overrideTypes: {
|
|
255
|
-
json: 'z.record(z.string())'
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
destinations: [
|
|
259
|
-
{
|
|
260
|
-
type: 'zod',
|
|
261
|
-
useDateType: true,
|
|
262
|
-
folder: './generated/zod',
|
|
263
|
-
suffix: 'schema'
|
|
264
|
-
},
|
|
265
|
-
{
|
|
266
|
-
type: 'ts',
|
|
267
|
-
folder: './generated/types',
|
|
268
|
-
suffix: 'type'
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
type: 'kysely',
|
|
272
|
-
folder: './generated/kysely',
|
|
273
|
-
suffix: 'db'
|
|
274
|
-
}
|
|
275
|
-
]
|
|
46
|
+
origin: { /* ... */ },
|
|
47
|
+
destinations: [{ type: 'zod' }],
|
|
48
|
+
includeViews: true,
|
|
49
|
+
views: ['user_profile_view'], // optional filter
|
|
50
|
+
ignoreViews: ['temp_view'] // optional exclude
|
|
276
51
|
})
|
|
277
52
|
```
|
|
278
53
|
|
|
279
|
-
|
|
54
|
+
## Database Support
|
|
280
55
|
|
|
281
|
-
|
|
56
|
+
| Database | Connection | Views | Magic Comments |
|
|
57
|
+
|----------|------------|-------|----------------|
|
|
58
|
+
| **MySQL** | Host/Port | ✅ | ✅ |
|
|
59
|
+
| **PostgreSQL** | Host/Port | ✅ | ✅ |
|
|
60
|
+
| **SQLite** | File Path | ✅ | ❌ |
|
|
61
|
+
| **Prisma** | Schema File | ✅ | ❌ |
|
|
282
62
|
|
|
283
|
-
|
|
63
|
+
## Output Examples
|
|
284
64
|
|
|
65
|
+
**Zod Schema:**
|
|
285
66
|
```typescript
|
|
286
|
-
import { z } from 'zod';
|
|
287
|
-
import { CustomValidator } from './validators';
|
|
288
|
-
|
|
289
67
|
export const user = z.object({
|
|
290
68
|
id: z.number().nonnegative(),
|
|
291
|
-
name: z.string().min(
|
|
292
|
-
|
|
293
|
-
password: z.string(),
|
|
294
|
-
profile_picture: z.string().nullable(),
|
|
69
|
+
name: z.string().min(1),
|
|
70
|
+
email: z.string().email(),
|
|
295
71
|
role: z.enum(['admin', 'user']),
|
|
296
72
|
})
|
|
297
73
|
|
|
298
74
|
export const insertable_user = z.object({
|
|
299
|
-
name: z.string().min(
|
|
300
|
-
|
|
301
|
-
password: z.string(),
|
|
302
|
-
profile_picture: z.string().nullable(),
|
|
303
|
-
role: z.enum(['admin', 'user']),
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
export const updateable_user = z.object({
|
|
307
|
-
name: z.string().min(10).max(255).optional(),
|
|
308
|
-
username: z.string().optional(),
|
|
309
|
-
password: z.string().optional(),
|
|
310
|
-
profile_picture: z.string().nullable().optional(),
|
|
311
|
-
role: z.enum(['admin', 'user']).optional(),
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
export const selectable_user = z.object({
|
|
315
|
-
id: z.number().nonnegative(),
|
|
316
|
-
name: z.string(),
|
|
317
|
-
username: z.string(),
|
|
318
|
-
password: z.string(),
|
|
319
|
-
profile_picture: z.string().nullable(),
|
|
75
|
+
name: z.string().min(1),
|
|
76
|
+
email: z.string().email(),
|
|
320
77
|
role: z.enum(['admin', 'user']),
|
|
321
78
|
})
|
|
322
79
|
|
|
323
|
-
export type
|
|
324
|
-
export type InsertableUserType = z.infer<typeof insertable_user>
|
|
325
|
-
export type UpdateableUserType = z.infer<typeof updateable_user>
|
|
326
|
-
export type SelectableUserType = z.infer<typeof selectable_user>
|
|
80
|
+
export type UserType = z.infer<typeof user>
|
|
327
81
|
```
|
|
328
82
|
|
|
329
|
-
|
|
330
|
-
|
|
83
|
+
**TypeScript Interface:**
|
|
331
84
|
```typescript
|
|
332
|
-
import { CustomType } from './types';
|
|
333
|
-
import { BaseModel } from './models';
|
|
334
|
-
|
|
335
|
-
// TypeScript interfaces for user
|
|
336
|
-
|
|
337
85
|
export interface User {
|
|
338
86
|
id: number;
|
|
339
87
|
name: string;
|
|
340
|
-
|
|
341
|
-
password: string;
|
|
342
|
-
profile_picture: string | null;
|
|
343
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
88
|
+
email: string;
|
|
344
89
|
role: 'admin' | 'user';
|
|
345
90
|
}
|
|
346
91
|
|
|
347
92
|
export interface InsertableUser {
|
|
348
|
-
name: string | null; // Optional because it has a default value
|
|
349
|
-
username: string;
|
|
350
|
-
password: string;
|
|
351
|
-
profile_picture: string | null;
|
|
352
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
353
|
-
role: 'admin' | 'user';
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
export interface UpdateableUser {
|
|
357
|
-
name: string | null; // Optional for updates
|
|
358
|
-
username: string | null; // Optional for updates
|
|
359
|
-
password: string | null; // Optional for updates
|
|
360
|
-
profile_picture: string | null;
|
|
361
|
-
metadata: Record<string, unknown> | null; // Custom type from @ts comment, optional for updates
|
|
362
|
-
role: 'admin' | 'user' | null; // Optional for updates
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export interface SelectableUser {
|
|
366
|
-
id: number;
|
|
367
93
|
name: string;
|
|
368
|
-
|
|
369
|
-
password: string;
|
|
370
|
-
profile_picture: string | null;
|
|
371
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
94
|
+
email: string;
|
|
372
95
|
role: 'admin' | 'user';
|
|
373
96
|
}
|
|
374
97
|
```
|
|
375
98
|
|
|
376
|
-
|
|
377
|
-
|
|
99
|
+
**Kysely Types:**
|
|
378
100
|
```typescript
|
|
379
|
-
import { Generated, ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
|
380
|
-
|
|
381
|
-
// JSON type definitions
|
|
382
|
-
export type Json = ColumnType<JsonValue, string, string>;
|
|
383
|
-
|
|
384
|
-
export type JsonArray = JsonValue[];
|
|
385
|
-
|
|
386
|
-
export type JsonObject = {
|
|
387
|
-
[x: string]: JsonValue | undefined;
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
export type JsonPrimitive = boolean | number | string | null;
|
|
391
|
-
|
|
392
|
-
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
393
|
-
|
|
394
|
-
// Kysely type definitions for user
|
|
395
|
-
|
|
396
|
-
// This interface defines the structure of the 'user' table
|
|
397
101
|
export interface UserTable {
|
|
398
102
|
id: Generated<number>;
|
|
399
103
|
name: string;
|
|
400
|
-
|
|
401
|
-
password: string;
|
|
402
|
-
profile_picture: string | null;
|
|
403
|
-
metadata: Record<string, unknown>; // Custom type from @kysely comment
|
|
104
|
+
email: string;
|
|
404
105
|
role: 'admin' | 'user';
|
|
405
106
|
}
|
|
406
107
|
|
|
407
|
-
// Define the database interface
|
|
408
|
-
export interface DB {
|
|
409
|
-
user: UserTable;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Use these types for inserting, selecting and updating the table
|
|
413
108
|
export type User = Selectable<UserTable>;
|
|
414
109
|
export type NewUser = Insertable<UserTable>;
|
|
415
110
|
export type UserUpdate = Updateable<UserTable>;
|
|
416
111
|
```
|
|
417
112
|
|
|
418
|
-
##
|
|
113
|
+
## Configuration
|
|
419
114
|
|
|
420
|
-
|
|
115
|
+
### Origin Options
|
|
116
|
+
```typescript
|
|
117
|
+
// MySQL/PostgreSQL
|
|
421
118
|
{
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
},
|
|
432
|
-
"ssl": {
|
|
433
|
-
"ca": "path/to/ca.pem",
|
|
434
|
-
"cert": "path/to/cert.pem",
|
|
435
|
-
"key": "path/to/key.pem"
|
|
436
|
-
},
|
|
437
|
-
} | {
|
|
438
|
-
"type": "postgres",
|
|
439
|
-
"host": "127.0.0.1",
|
|
440
|
-
"port": 5432,
|
|
441
|
-
"user": "postgres",
|
|
442
|
-
"password": "secret",
|
|
443
|
-
"database": "myapp",
|
|
444
|
-
"schema": "public",
|
|
445
|
-
"overrideTypes": {
|
|
446
|
-
"jsonb": "z.record(z.string())"
|
|
447
|
-
},
|
|
448
|
-
"ssl": {
|
|
449
|
-
"ca": "path/to/ca.pem",
|
|
450
|
-
"cert": "path/to/cert.pem",
|
|
451
|
-
"key": "path/to/key.pem"
|
|
452
|
-
},
|
|
453
|
-
} | {
|
|
454
|
-
"type": "sqlite",
|
|
455
|
-
"path": "path/to/database.db",
|
|
456
|
-
"overrideTypes": {
|
|
457
|
-
"json": "z.record(z.string())"
|
|
458
|
-
}
|
|
459
|
-
} | {
|
|
460
|
-
"type": "prisma",
|
|
461
|
-
"path": "path/to/schema.prisma",
|
|
462
|
-
"overrideTypes": {
|
|
463
|
-
"Json": "z.record(z.string())"
|
|
464
|
-
}
|
|
465
|
-
},
|
|
466
|
-
"destinations": [
|
|
467
|
-
{
|
|
468
|
-
"type": "zod",
|
|
469
|
-
"useDateType": true,
|
|
470
|
-
"useTrim": false,
|
|
471
|
-
"nullish": false, // When true, nullable fields use nullish() instead of nullable()
|
|
472
|
-
"requiredString": false, // When true, adds min(1) validation to non-nullable string fields
|
|
473
|
-
"header": "import { z } from 'zod';\nimport { CustomValidator } from './validators';",
|
|
474
|
-
"folder": "@zod",
|
|
475
|
-
"suffix": "table"
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
"type": "ts",
|
|
479
|
-
"enumType": "union",
|
|
480
|
-
"modelType": "interface",
|
|
481
|
-
"header": "import { CustomType } from './types';\nimport { BaseModel } from './models';",
|
|
482
|
-
"folder": "types",
|
|
483
|
-
"suffix": "type"
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
"type": "kysely",
|
|
487
|
-
"schemaName": "Database",
|
|
488
|
-
"header": "import { Generated, ColumnType } from 'kysely';\nimport { CustomTypes } from './types';",
|
|
489
|
-
"outFile": "db.ts"
|
|
490
|
-
}
|
|
491
|
-
],
|
|
492
|
-
"tables": ["user", "log"],
|
|
493
|
-
"ignore": ["log", "/^temp/"],
|
|
494
|
-
"camelCase": false,
|
|
495
|
-
"silent": false,
|
|
496
|
-
"dryRun": false,
|
|
497
|
-
"magicComments": true
|
|
119
|
+
type: 'mysql' | 'postgres',
|
|
120
|
+
host: string,
|
|
121
|
+
port: number,
|
|
122
|
+
user: string,
|
|
123
|
+
password: string,
|
|
124
|
+
database: string,
|
|
125
|
+
schema?: string, // PostgreSQL only
|
|
126
|
+
ssl?: { ca, cert, key },
|
|
127
|
+
overrideTypes?: Record<string, string>
|
|
498
128
|
}
|
|
499
|
-
```
|
|
500
129
|
|
|
501
|
-
|
|
502
|
-
| ------ | ----------- |
|
|
503
|
-
| destinations | An array of destination configurations to generate multiple output formats from a single origin |
|
|
504
|
-
| destinations[].type | The type of output to generate: "zod", "ts", or "kysely" |
|
|
505
|
-
| destinations[].useDateType | (Zod only) Use a specialized Zod type for date-like fields instead of string |
|
|
506
|
-
| destinations[].useTrim | (Zod only) Use `z.string().trim()` instead of `z.string()` |
|
|
507
|
-
| destinations[].nullish | (Zod only) Use `nullish()` instead of `nullable()` for nullable fields. In updateable schemas, fields that were already nullable will become nullish |
|
|
508
|
-
| destinations[].version | (Zod only) Zod version to use. Defaults to 3. Set to 4 to use Zod v4 |
|
|
509
|
-
| destinations[].requiredString | (Zod only) Add `min(1)` for non-nullable string fields |
|
|
510
|
-
| destinations[].enumType | (TypeScript only) How to represent enum types: "union" (default) or "enum" |
|
|
511
|
-
| destinations[].modelType | (TypeScript only) How to represent models: "interface" (default) or "type" |
|
|
512
|
-
| destinations[].schemaName | (Kysely only) Name of the database interface (default: "DB") |
|
|
513
|
-
| destinations[].header | Custom header to include at the beginning of generated files (e.g., custom imports) |
|
|
514
|
-
| destinations[].folder | Specify the output directory for the generated files |
|
|
515
|
-
| destinations[].suffix | Suffix to the name of a generated file (eg: `user.table.ts`) |
|
|
516
|
-
| destinations[].outFile | (Kysely only) Specify the output file for the generated content. All tables will be written to this file |
|
|
517
|
-
| tables | Filter the tables to include only those specified |
|
|
518
|
-
| ignore | Filter the tables to exclude those specified. If a table name begins and ends with "/", it will be processed as a regular expression |
|
|
519
|
-
| camelCase | Convert all table names and their properties to camelcase. (eg: `profile_picture` becomes `profilePicture`) |
|
|
520
|
-
| silent | Don't log anything to the console |
|
|
521
|
-
| dryRun | When true, doesn't write files to disk but returns an object with filenames as keys and generated content as values |
|
|
522
|
-
| magicComments | Use @zod and @ts comments to override types (unsupported by SQLite) |
|
|
523
|
-
|
|
524
|
-
## overrideTypes
|
|
525
|
-
|
|
526
|
-
You can override the default type for a specific column type. This is specific to each database type and is placed inside the origin object. Each database type has its own set of valid types that can be overridden:
|
|
527
|
-
|
|
528
|
-
### MySQL overrideTypes
|
|
529
|
-
|
|
530
|
-
```json
|
|
130
|
+
// SQLite
|
|
531
131
|
{
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
"port": 3306,
|
|
536
|
-
"user": "root",
|
|
537
|
-
"password": "secret",
|
|
538
|
-
"database": "myapp",
|
|
539
|
-
"overrideTypes": {
|
|
540
|
-
"json": "z.record(z.string())",
|
|
541
|
-
"text": "z.string().max(1000)"
|
|
542
|
-
}
|
|
543
|
-
}
|
|
132
|
+
type: 'sqlite',
|
|
133
|
+
path: string,
|
|
134
|
+
overrideTypes?: Record<string, string>
|
|
544
135
|
}
|
|
545
|
-
```
|
|
546
136
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
```json
|
|
137
|
+
// Prisma
|
|
550
138
|
{
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
"port": 5432,
|
|
555
|
-
"user": "postgres",
|
|
556
|
-
"password": "secret",
|
|
557
|
-
"database": "myapp",
|
|
558
|
-
"schema": "public",
|
|
559
|
-
"overrideTypes": {
|
|
560
|
-
"jsonb": "z.record(z.string())",
|
|
561
|
-
"uuid": "z.string().uuid()"
|
|
562
|
-
}
|
|
563
|
-
}
|
|
139
|
+
type: 'prisma',
|
|
140
|
+
path: string,
|
|
141
|
+
overrideTypes?: Record<string, string>
|
|
564
142
|
}
|
|
565
143
|
```
|
|
566
144
|
|
|
567
|
-
###
|
|
568
|
-
|
|
569
|
-
```json
|
|
145
|
+
### Destination Options
|
|
146
|
+
```typescript
|
|
570
147
|
{
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
148
|
+
type: 'zod' | 'ts' | 'kysely',
|
|
149
|
+
folder?: string,
|
|
150
|
+
suffix?: string,
|
|
151
|
+
outFile?: string, // Kysely only
|
|
152
|
+
header?: string, // Custom imports
|
|
153
|
+
|
|
154
|
+
// Zod specific
|
|
155
|
+
useDateType?: boolean,
|
|
156
|
+
useTrim?: boolean,
|
|
157
|
+
nullish?: boolean,
|
|
158
|
+
requiredString?: boolean,
|
|
159
|
+
version?: 3 | 4,
|
|
160
|
+
|
|
161
|
+
// TypeScript specific
|
|
162
|
+
enumType?: 'union' | 'enum',
|
|
163
|
+
modelType?: 'interface' | 'type',
|
|
164
|
+
|
|
165
|
+
// Kysely specific
|
|
166
|
+
schemaName?: string // Default: 'DB'
|
|
579
167
|
}
|
|
580
168
|
```
|
|
581
169
|
|
|
582
|
-
###
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
```
|
|
170
|
+
### Global Options
|
|
171
|
+
| Option | Description |
|
|
172
|
+
|--------|-------------|
|
|
173
|
+
| `tables` | Include only specified tables |
|
|
174
|
+
| `views` | Include only specified views |
|
|
175
|
+
| `ignore` | Exclude specified tables (supports regex) |
|
|
176
|
+
| `ignoreViews` | Exclude specified views (supports regex) |
|
|
177
|
+
| `includeViews` | Process database views |
|
|
178
|
+
| `camelCase` | Convert to camelCase |
|
|
179
|
+
| `dryRun` | Return content without writing files |
|
|
180
|
+
| `magicComments` | Enable @zod/@ts/@kysely comments |
|
|
596
181
|
|
|
597
182
|
## Magic Comments
|
|
598
183
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
You can use the `@zod` comment to override the Zod type for a specific column. This is useful when you want to add custom validation or transformation to a field.
|
|
184
|
+
Override types for specific columns using database comments (MySQL/PostgreSQL only):
|
|
602
185
|
|
|
603
186
|
```sql
|
|
604
187
|
CREATE TABLE `user` (
|
|
605
|
-
`id` int(11)
|
|
606
|
-
`name` varchar(255)
|
|
607
|
-
`email` varchar(255)
|
|
188
|
+
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
189
|
+
`name` varchar(255) COMMENT '@zod(z.string().min(2).max(50))',
|
|
190
|
+
`email` varchar(255) COMMENT '@ts(EmailAddress) @kysely(string)',
|
|
191
|
+
`metadata` json COMMENT '@ts(UserMetadata)',
|
|
608
192
|
PRIMARY KEY (`id`)
|
|
609
193
|
);
|
|
610
194
|
```
|
|
611
195
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
id: z.number().nonnegative(),
|
|
617
|
-
name: z.string().min(10).max(255),
|
|
618
|
-
email: z.string().email(),
|
|
619
|
-
})
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
### @ts Comments
|
|
623
|
-
|
|
624
|
-
You can use the `@ts` comment to override the TypeScript type for a specific column. This is useful when you want to specify a more precise type for a field.
|
|
196
|
+
**Supported Comments:**
|
|
197
|
+
- `@zod(...)` - Override Zod schema
|
|
198
|
+
- `@ts(...)` - Override TypeScript type
|
|
199
|
+
- `@kysely(...)` - Override Kysely type
|
|
625
200
|
|
|
626
|
-
|
|
627
|
-
CREATE TABLE `user` (
|
|
628
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
629
|
-
`metadata` json NOT NULL COMMENT '@ts(Record<string, unknown>)',
|
|
630
|
-
`settings` json NOT NULL COMMENT '@ts(UserSettings)',
|
|
631
|
-
PRIMARY KEY (`id`)
|
|
632
|
-
);
|
|
633
|
-
```
|
|
201
|
+
## Type Overrides
|
|
634
202
|
|
|
635
|
-
|
|
203
|
+
Override default types globally in your origin config:
|
|
636
204
|
|
|
637
205
|
```typescript
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
```sql
|
|
650
|
-
CREATE TABLE `user` (
|
|
651
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
652
|
-
`metadata` json NOT NULL COMMENT '@kysely(Record<string, string>)',
|
|
653
|
-
PRIMARY KEY (`id`)
|
|
654
|
-
);
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
This will generate:
|
|
658
|
-
|
|
659
|
-
```typescript
|
|
660
|
-
export interface UserTable {
|
|
661
|
-
id: Generated<number>;
|
|
662
|
-
metadata: Record<string, string>;
|
|
206
|
+
{
|
|
207
|
+
origin: {
|
|
208
|
+
type: 'mysql',
|
|
209
|
+
// ... connection config
|
|
210
|
+
overrideTypes: {
|
|
211
|
+
json: 'z.record(z.string())',
|
|
212
|
+
text: 'z.string().max(1000)',
|
|
213
|
+
decimal: 'z.number().positive()'
|
|
214
|
+
}
|
|
215
|
+
}
|
|
663
216
|
}
|
|
664
217
|
```
|
|
665
218
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
CREATE TABLE `product` (
|
|
672
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
673
|
-
`variants` json NOT NULL COMMENT '@ts(Array<{ id: string; price: number; stock: number }>)',
|
|
674
|
-
PRIMARY KEY (`id`)
|
|
675
|
-
);
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
This will generate:
|
|
679
|
-
|
|
680
|
-
```typescript
|
|
681
|
-
export interface Product {
|
|
682
|
-
id: number;
|
|
683
|
-
variants: Array<{ id: string; price: number; stock: number }>;
|
|
684
|
-
}
|
|
685
|
-
```
|
|
219
|
+
**Common Overrides:**
|
|
220
|
+
- **MySQL**: `json`, `text`, `decimal`, `enum`
|
|
221
|
+
- **PostgreSQL**: `jsonb`, `uuid`, `text`, `numeric`
|
|
222
|
+
- **SQLite**: `json`, `text`, `real`
|
|
223
|
+
- **Prisma**: `Json`, `String`, `Decimal`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mutano",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.1",
|
|
5
5
|
"description": "Converts Prisma/MySQL/PostgreSQL/SQLite schemas to Zod/TS/Kysely interfaces",
|
|
6
6
|
"author": "Alisson Cavalcante Agiani <thelinuxlich@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -14,20 +14,22 @@
|
|
|
14
14
|
"test": "vitest run"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@mrleebo/prisma-ast": "^0.
|
|
17
|
+
"@mrleebo/prisma-ast": "^0.13.0",
|
|
18
18
|
"camelcase": "^8.0.0",
|
|
19
|
-
"fs-extra": "^11.3.
|
|
19
|
+
"fs-extra": "^11.3.2",
|
|
20
20
|
"knex": "^3.1.0",
|
|
21
|
-
"mysql2": "^3.14.
|
|
22
|
-
"pg": "^8.16.
|
|
21
|
+
"mysql2": "^3.14.4",
|
|
22
|
+
"pg": "^8.16.3",
|
|
23
23
|
"sqlite3": "^5.1.7"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
+
"@electric-sql/pglite": "^0.3.8",
|
|
26
27
|
"@types/fs-extra": "^11.0.4",
|
|
27
|
-
"
|
|
28
|
+
"@types/pg": "^8.15.5",
|
|
29
|
+
"esbuild": "^0.25.9",
|
|
28
30
|
"ts-node": "^10.9.2",
|
|
29
|
-
"tsx": "
|
|
30
|
-
"typescript": "^5.
|
|
31
|
-
"vitest": "^3.
|
|
31
|
+
"tsx": "4.19.4",
|
|
32
|
+
"typescript": "^5.9.2",
|
|
33
|
+
"vitest": "^3.2.4"
|
|
32
34
|
}
|
|
33
35
|
}
|