mutano 3.0.0 → 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 +116 -732
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,182 +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
|
-
- **NEW: Database Views Support** - Extract and generate types for database views (read-only)
|
|
9
|
-
- Supports camelCase conversion
|
|
10
|
-
- Handles nullable, default, auto-increment and enum fields
|
|
11
|
-
- Supports custom type overrides via configuration or database comments
|
|
12
|
-
- Intelligently handles field nullability based on operation type (table, insertable, updateable, selectable)
|
|
13
|
-
- All fields in updateable schemas are automatically made optional
|
|
14
|
-
- Views are treated as read-only entities (no insertable/updateable schemas generated)
|
|
5
|
+
**Supports:** MySQL, PostgreSQL, SQLite, Prisma • **Features:** Views, Magic Comments, Type Overrides, Multiple Outputs
|
|
15
6
|
|
|
16
7
|
## Installation
|
|
17
8
|
|
|
18
|
-
Install `mutano` with npm
|
|
19
|
-
|
|
20
9
|
```bash
|
|
21
10
|
npm install mutano
|
|
22
11
|
```
|
|
23
12
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
Create user table:
|
|
27
|
-
|
|
28
|
-
```sql
|
|
29
|
-
CREATE TABLE `user` (
|
|
30
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
31
|
-
`name` varchar(255) NOT NULL COMMENT '@zod(z.string().min(10).max(255))', -- this will override the Zod type
|
|
32
|
-
`username` varchar(255) NOT NULL,
|
|
33
|
-
`password` varchar(255) NOT NULL,
|
|
34
|
-
`profile_picture` varchar(255) DEFAULT NULL,
|
|
35
|
-
`metadata` json NOT NULL COMMENT '@ts(Record<string, unknown>) @kysely(Record<string, string>)', -- this will override the TypeScript and Kysely type
|
|
36
|
-
`role` enum('admin','user') NOT NULL,
|
|
37
|
-
PRIMARY KEY (`id`)
|
|
38
|
-
);
|
|
39
|
-
```
|
|
40
|
-
Use the mutano API:
|
|
41
|
-
|
|
42
|
-
### MySQL Example with Zod Schemas
|
|
13
|
+
## Quick Start
|
|
43
14
|
|
|
44
15
|
```typescript
|
|
45
16
|
import { generate } from 'mutano'
|
|
46
17
|
|
|
18
|
+
// Basic usage
|
|
47
19
|
await generate({
|
|
48
20
|
origin: {
|
|
49
|
-
type: 'mysql',
|
|
50
|
-
host: '127.0.0.1',
|
|
51
|
-
port: 3306,
|
|
52
|
-
user: 'root',
|
|
53
|
-
password: 'secret',
|
|
54
|
-
database: 'myapp',
|
|
55
|
-
overrideTypes: {
|
|
56
|
-
json: 'z.record(z.string())'
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
destinations: [{
|
|
60
|
-
type: 'zod',
|
|
61
|
-
useDateType: true,
|
|
62
|
-
useTrim: false,
|
|
63
|
-
nullish: false, // When true, nullable fields use nullish() instead of nullable()
|
|
64
|
-
folder: './generated',
|
|
65
|
-
suffix: 'schema'
|
|
66
|
-
}]
|
|
67
|
-
})
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### MySQL Example with TypeScript Type Aliases (Instead of Interfaces)
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { generate } from 'mutano'
|
|
74
|
-
|
|
75
|
-
await generate({
|
|
76
|
-
origin: {
|
|
77
|
-
type: 'mysql',
|
|
78
|
-
host: '127.0.0.1',
|
|
79
|
-
port: 3306,
|
|
80
|
-
user: 'root',
|
|
81
|
-
password: 'secret',
|
|
82
|
-
database: 'myapp',
|
|
83
|
-
overrideTypes: {
|
|
84
|
-
json: 'z.record(z.string())'
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
destinations: [{
|
|
88
|
-
type: 'ts',
|
|
89
|
-
modelType: 'type', // Generate TypeScript type aliases instead of interfaces
|
|
90
|
-
folder: './types',
|
|
91
|
-
suffix: 'types'
|
|
92
|
-
}]
|
|
93
|
-
})
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### MySQL Example with Custom Header for TypeScript
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
import { generate } from 'mutano'
|
|
100
|
-
|
|
101
|
-
await generate({
|
|
102
|
-
origin: {
|
|
103
|
-
type: 'mysql',
|
|
104
|
-
host: '127.0.0.1',
|
|
105
|
-
port: 3306,
|
|
106
|
-
user: 'root',
|
|
107
|
-
password: 'secret',
|
|
108
|
-
database: 'myapp',
|
|
109
|
-
overrideTypes: {
|
|
110
|
-
json: 'z.record(z.string())'
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
destinations: [{
|
|
114
|
-
type: 'ts',
|
|
115
|
-
header: "import type { CustomType } from './types';\nimport type { BaseModel } from './models';"
|
|
116
|
-
}]
|
|
117
|
-
})
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### MySQL Example with Custom Header for Zod
|
|
121
|
-
|
|
122
|
-
```typescript
|
|
123
|
-
import { generate } from 'mutano'
|
|
124
|
-
|
|
125
|
-
await generate({
|
|
126
|
-
origin: {
|
|
127
|
-
type: 'mysql',
|
|
128
|
-
host: '127.0.0.1',
|
|
129
|
-
port: 3306,
|
|
130
|
-
user: 'root',
|
|
131
|
-
password: 'secret',
|
|
132
|
-
database: 'myapp',
|
|
133
|
-
overrideTypes: {
|
|
134
|
-
json: 'z.record(z.string())'
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
destinations: [{
|
|
138
|
-
type: 'zod',
|
|
139
|
-
header: "import { z } from 'zod';\nimport { CustomValidator } from './validators';"
|
|
140
|
-
}]
|
|
141
|
-
})
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### MySQL Example with Kysely Type Definitions (Custom Schema Name)
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { generate } from 'mutano'
|
|
148
|
-
|
|
149
|
-
await generate({
|
|
150
|
-
origin: {
|
|
151
|
-
type: 'mysql',
|
|
152
|
-
host: '127.0.0.1',
|
|
153
|
-
port: 3306,
|
|
154
|
-
user: 'root',
|
|
155
|
-
password: 'secret',
|
|
156
|
-
database: 'myapp',
|
|
157
|
-
overrideTypes: {
|
|
158
|
-
json: 'z.record(z.string())'
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
destinations: [{
|
|
162
|
-
type: 'kysely',
|
|
163
|
-
schemaName: 'Database', // Default is 'DB'
|
|
164
|
-
header: "import { Generated, ColumnType } from 'kysely';\nimport { CustomTypes } from './types';",
|
|
165
|
-
folder: './db/types',
|
|
166
|
-
suffix: 'db'
|
|
167
|
-
}]
|
|
168
|
-
})
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Example with Dry Run Option
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
import { generate } from 'mutano'
|
|
175
|
-
|
|
176
|
-
// Generate without writing to disk
|
|
177
|
-
const output = await generate({
|
|
178
|
-
origin: {
|
|
179
|
-
type: 'mysql',
|
|
21
|
+
type: 'mysql', // or 'postgres', 'sqlite', 'prisma'
|
|
180
22
|
host: '127.0.0.1',
|
|
181
23
|
port: 3306,
|
|
182
24
|
user: 'root',
|
|
@@ -184,656 +26,198 @@ const output = await generate({
|
|
|
184
26
|
database: 'myapp'
|
|
185
27
|
},
|
|
186
28
|
destinations: [{
|
|
187
|
-
type: 'zod'
|
|
188
|
-
|
|
189
|
-
dryRun: true // Return content and don't write to files
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// Output is an object where keys are filenames and values are file content
|
|
193
|
-
console.log(Object.keys(output)) // ['user.ts', 'product.ts', ...]
|
|
194
|
-
|
|
195
|
-
// You can access the content for a specific file
|
|
196
|
-
console.log(output['user.ts'])
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### PostgreSQL Example
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
import { generate } from 'mutano'
|
|
203
|
-
|
|
204
|
-
await generate({
|
|
205
|
-
origin: {
|
|
206
|
-
type: 'postgres',
|
|
207
|
-
host: '127.0.0.1',
|
|
208
|
-
port: 5432,
|
|
209
|
-
user: 'postgres',
|
|
210
|
-
password: 'secret',
|
|
211
|
-
database: 'myapp',
|
|
212
|
-
schema: 'public', // optional, defaults to 'public'
|
|
213
|
-
overrideTypes: {
|
|
214
|
-
jsonb: 'z.record(z.string())'
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
destinations: [{
|
|
218
|
-
type: 'zod',
|
|
219
|
-
useDateType: true
|
|
220
|
-
}]
|
|
221
|
-
})
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### SQLite Example
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
import { generate } from 'mutano'
|
|
228
|
-
|
|
229
|
-
await generate({
|
|
230
|
-
origin: {
|
|
231
|
-
type: 'sqlite',
|
|
232
|
-
path: './myapp.db',
|
|
233
|
-
overrideTypes: {
|
|
234
|
-
json: 'z.record(z.string())'
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
destinations: [{
|
|
238
|
-
type: 'ts'
|
|
29
|
+
type: 'zod', // or 'ts', 'kysely'
|
|
30
|
+
folder: './generated'
|
|
239
31
|
}]
|
|
240
32
|
})
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### Example with Multiple Destinations
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
import { generate } from 'mutano'
|
|
247
33
|
|
|
34
|
+
// Multiple outputs
|
|
248
35
|
await generate({
|
|
249
|
-
origin: {
|
|
250
|
-
type: 'mysql',
|
|
251
|
-
host: '127.0.0.1',
|
|
252
|
-
port: 3306,
|
|
253
|
-
user: 'root',
|
|
254
|
-
password: 'secret',
|
|
255
|
-
database: 'myapp',
|
|
256
|
-
overrideTypes: {
|
|
257
|
-
json: 'z.record(z.string())'
|
|
258
|
-
}
|
|
259
|
-
},
|
|
36
|
+
origin: { /* ... */ },
|
|
260
37
|
destinations: [
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
folder: './generated/zod',
|
|
265
|
-
suffix: 'schema'
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
type: 'ts',
|
|
269
|
-
folder: './generated/types',
|
|
270
|
-
suffix: 'type'
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
type: 'kysely',
|
|
274
|
-
folder: './generated/kysely',
|
|
275
|
-
suffix: 'db'
|
|
276
|
-
}
|
|
38
|
+
{ type: 'zod', folder: './zod' },
|
|
39
|
+
{ type: 'ts', folder: './types' },
|
|
40
|
+
{ type: 'kysely', outFile: './db.ts' }
|
|
277
41
|
]
|
|
278
42
|
})
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
This will generate all three types of output files for each table in your database, placing them in separate folders with appropriate suffixes.
|
|
282
|
-
|
|
283
|
-
### Database Views Example
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
import { generate } from 'mutano'
|
|
287
43
|
|
|
44
|
+
// With views support
|
|
288
45
|
await generate({
|
|
289
|
-
origin: {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
password: 'secret',
|
|
295
|
-
database: 'myapp'
|
|
296
|
-
},
|
|
297
|
-
destinations: [{
|
|
298
|
-
type: 'zod',
|
|
299
|
-
folder: './generated/schemas',
|
|
300
|
-
suffix: 'schema'
|
|
301
|
-
}],
|
|
302
|
-
includeViews: true, // Enable views processing
|
|
303
|
-
views: ['user_profile_view', 'order_summary_view'], // Optional: specify which views to include
|
|
304
|
-
ignoreViews: ['temp_view'] // Optional: specify which views to ignore
|
|
305
|
-
})
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
**Database Views Features:**
|
|
309
|
-
- **Read-only**: Views generate only selectable schemas (no insertable/updateable)
|
|
310
|
-
- **All database types**: Supports MySQL, PostgreSQL, SQLite, and Prisma views
|
|
311
|
-
- **Filtering**: Use `views` and `ignoreViews` options to control which views are processed
|
|
312
|
-
- **Type safety**: Full TypeScript/Zod/Kysely type generation for view columns
|
|
313
|
-
- **Prisma integration**: Automatically detects `view` blocks in Prisma schema files
|
|
314
|
-
|
|
315
|
-
### Prisma Views Integration
|
|
316
|
-
|
|
317
|
-
Mutano automatically detects and processes `view` blocks in your Prisma schema:
|
|
318
|
-
|
|
319
|
-
```prisma
|
|
320
|
-
// schema.prisma
|
|
321
|
-
generator client {
|
|
322
|
-
provider = "prisma-client-js"
|
|
323
|
-
previewFeatures = ["views"]
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
model User {
|
|
327
|
-
id Int @id @default(autoincrement())
|
|
328
|
-
email String @unique
|
|
329
|
-
name String?
|
|
330
|
-
profile Profile?
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
model Profile {
|
|
334
|
-
id Int @id @default(autoincrement())
|
|
335
|
-
bio String
|
|
336
|
-
user User @relation(fields: [userId], references: [id])
|
|
337
|
-
userId Int @unique
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// This view will be automatically processed by Mutano
|
|
341
|
-
view UserInfo {
|
|
342
|
-
id Int
|
|
343
|
-
email String
|
|
344
|
-
name String?
|
|
345
|
-
bio String?
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
```typescript
|
|
350
|
-
// Generate types from Prisma schema with views
|
|
351
|
-
await generate({
|
|
352
|
-
origin: {
|
|
353
|
-
type: 'prisma',
|
|
354
|
-
path: './schema.prisma'
|
|
355
|
-
},
|
|
356
|
-
destinations: [{
|
|
357
|
-
type: 'zod',
|
|
358
|
-
useDateType: true
|
|
359
|
-
}],
|
|
360
|
-
includeViews: true
|
|
46
|
+
origin: { /* ... */ },
|
|
47
|
+
destinations: [{ type: 'zod' }],
|
|
48
|
+
includeViews: true,
|
|
49
|
+
views: ['user_profile_view'], // optional filter
|
|
50
|
+
ignoreViews: ['temp_view'] // optional exclude
|
|
361
51
|
})
|
|
362
52
|
```
|
|
363
53
|
|
|
364
|
-
|
|
54
|
+
## Database Support
|
|
365
55
|
|
|
366
|
-
|
|
56
|
+
| Database | Connection | Views | Magic Comments |
|
|
57
|
+
|----------|------------|-------|----------------|
|
|
58
|
+
| **MySQL** | Host/Port | ✅ | ✅ |
|
|
59
|
+
| **PostgreSQL** | Host/Port | ✅ | ✅ |
|
|
60
|
+
| **SQLite** | File Path | ✅ | ❌ |
|
|
61
|
+
| **Prisma** | Schema File | ✅ | ❌ |
|
|
367
62
|
|
|
368
|
-
|
|
369
|
-
```sql
|
|
370
|
-
CREATE VIEW user_profile_view AS
|
|
371
|
-
SELECT
|
|
372
|
-
u.id,
|
|
373
|
-
u.name,
|
|
374
|
-
u.email,
|
|
375
|
-
p.bio,
|
|
376
|
-
p.avatar_url
|
|
377
|
-
FROM users u
|
|
378
|
-
LEFT JOIN profiles p ON u.id = p.user_id;
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### Zod Schema Output Example with Custom Header
|
|
63
|
+
## Output Examples
|
|
382
64
|
|
|
65
|
+
**Zod Schema:**
|
|
383
66
|
```typescript
|
|
384
|
-
import { z } from 'zod';
|
|
385
|
-
import { CustomValidator } from './validators';
|
|
386
|
-
|
|
387
67
|
export const user = z.object({
|
|
388
68
|
id: z.number().nonnegative(),
|
|
389
|
-
name: z.string().min(
|
|
390
|
-
|
|
391
|
-
password: z.string(),
|
|
392
|
-
profile_picture: z.string().nullable(),
|
|
69
|
+
name: z.string().min(1),
|
|
70
|
+
email: z.string().email(),
|
|
393
71
|
role: z.enum(['admin', 'user']),
|
|
394
72
|
})
|
|
395
73
|
|
|
396
74
|
export const insertable_user = z.object({
|
|
397
|
-
name: z.string().min(
|
|
398
|
-
|
|
399
|
-
password: z.string(),
|
|
400
|
-
profile_picture: z.string().nullable(),
|
|
401
|
-
role: z.enum(['admin', 'user']),
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
export const updateable_user = z.object({
|
|
405
|
-
name: z.string().min(10).max(255).optional(),
|
|
406
|
-
username: z.string().optional(),
|
|
407
|
-
password: z.string().optional(),
|
|
408
|
-
profile_picture: z.string().nullable().optional(),
|
|
409
|
-
role: z.enum(['admin', 'user']).optional(),
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
export const selectable_user = z.object({
|
|
413
|
-
id: z.number().nonnegative(),
|
|
414
|
-
name: z.string(),
|
|
415
|
-
username: z.string(),
|
|
416
|
-
password: z.string(),
|
|
417
|
-
profile_picture: z.string().nullable(),
|
|
75
|
+
name: z.string().min(1),
|
|
76
|
+
email: z.string().email(),
|
|
418
77
|
role: z.enum(['admin', 'user']),
|
|
419
78
|
})
|
|
420
79
|
|
|
421
|
-
export type
|
|
422
|
-
export type InsertableUserType = z.infer<typeof insertable_user>
|
|
423
|
-
export type UpdateableUserType = z.infer<typeof updateable_user>
|
|
424
|
-
export type SelectableUserType = z.infer<typeof selectable_user>
|
|
80
|
+
export type UserType = z.infer<typeof user>
|
|
425
81
|
```
|
|
426
82
|
|
|
427
|
-
|
|
428
|
-
|
|
83
|
+
**TypeScript Interface:**
|
|
429
84
|
```typescript
|
|
430
|
-
import { CustomType } from './types';
|
|
431
|
-
import { BaseModel } from './models';
|
|
432
|
-
|
|
433
|
-
// TypeScript interfaces for user
|
|
434
|
-
|
|
435
85
|
export interface User {
|
|
436
86
|
id: number;
|
|
437
87
|
name: string;
|
|
438
|
-
|
|
439
|
-
password: string;
|
|
440
|
-
profile_picture: string | null;
|
|
441
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
88
|
+
email: string;
|
|
442
89
|
role: 'admin' | 'user';
|
|
443
90
|
}
|
|
444
91
|
|
|
445
92
|
export interface InsertableUser {
|
|
446
|
-
name: string | null; // Optional because it has a default value
|
|
447
|
-
username: string;
|
|
448
|
-
password: string;
|
|
449
|
-
profile_picture: string | null;
|
|
450
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
451
|
-
role: 'admin' | 'user';
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
export interface UpdateableUser {
|
|
455
|
-
name: string | null; // Optional for updates
|
|
456
|
-
username: string | null; // Optional for updates
|
|
457
|
-
password: string | null; // Optional for updates
|
|
458
|
-
profile_picture: string | null;
|
|
459
|
-
metadata: Record<string, unknown> | null; // Custom type from @ts comment, optional for updates
|
|
460
|
-
role: 'admin' | 'user' | null; // Optional for updates
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
export interface SelectableUser {
|
|
464
|
-
id: number;
|
|
465
93
|
name: string;
|
|
466
|
-
|
|
467
|
-
password: string;
|
|
468
|
-
profile_picture: string | null;
|
|
469
|
-
metadata: Record<string, unknown>; // Custom type from @ts comment
|
|
94
|
+
email: string;
|
|
470
95
|
role: 'admin' | 'user';
|
|
471
96
|
}
|
|
472
97
|
```
|
|
473
98
|
|
|
474
|
-
|
|
475
|
-
|
|
99
|
+
**Kysely Types:**
|
|
476
100
|
```typescript
|
|
477
|
-
import { Generated, ColumnType, Selectable, Insertable, Updateable } from 'kysely';
|
|
478
|
-
|
|
479
|
-
// JSON type definitions
|
|
480
|
-
export type Json = ColumnType<JsonValue, string, string>;
|
|
481
|
-
|
|
482
|
-
export type JsonArray = JsonValue[];
|
|
483
|
-
|
|
484
|
-
export type JsonObject = {
|
|
485
|
-
[x: string]: JsonValue | undefined;
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
export type JsonPrimitive = boolean | number | string | null;
|
|
489
|
-
|
|
490
|
-
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
|
|
491
|
-
|
|
492
|
-
// Kysely type definitions for user
|
|
493
|
-
|
|
494
|
-
// This interface defines the structure of the 'user' table
|
|
495
101
|
export interface UserTable {
|
|
496
102
|
id: Generated<number>;
|
|
497
103
|
name: string;
|
|
498
|
-
|
|
499
|
-
password: string;
|
|
500
|
-
profile_picture: string | null;
|
|
501
|
-
metadata: Record<string, unknown>; // Custom type from @kysely comment
|
|
104
|
+
email: string;
|
|
502
105
|
role: 'admin' | 'user';
|
|
503
106
|
}
|
|
504
107
|
|
|
505
|
-
// Define the database interface
|
|
506
|
-
export interface DB {
|
|
507
|
-
user: UserTable;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Use these types for inserting, selecting and updating the table
|
|
511
108
|
export type User = Selectable<UserTable>;
|
|
512
109
|
export type NewUser = Insertable<UserTable>;
|
|
513
110
|
export type UserUpdate = Updateable<UserTable>;
|
|
514
111
|
```
|
|
515
112
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
#### Zod Schema for Views (Read-only)
|
|
519
|
-
|
|
520
|
-
```typescript
|
|
521
|
-
import { z } from 'zod';
|
|
522
|
-
|
|
523
|
-
// View schema (read-only)
|
|
524
|
-
export const user_profile_view = z.object({
|
|
525
|
-
id: z.number().nonnegative(),
|
|
526
|
-
name: z.string(),
|
|
527
|
-
email: z.string(),
|
|
528
|
-
bio: z.string().nullable(),
|
|
529
|
-
avatar_url: z.string().nullable(),
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
export type UserProfileViewType = z.infer<typeof user_profile_view>
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
#### TypeScript Interface for Views (Read-only)
|
|
536
|
-
|
|
537
|
-
```typescript
|
|
538
|
-
// TypeScript interface for user_profile_view (view - read-only)
|
|
539
|
-
export interface UserProfileView {
|
|
540
|
-
id: number;
|
|
541
|
-
name: string;
|
|
542
|
-
email: string;
|
|
543
|
-
bio: string | null;
|
|
544
|
-
avatar_url: string | null;
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
#### Kysely Type Definitions for Views (Read-only)
|
|
113
|
+
## Configuration
|
|
549
114
|
|
|
115
|
+
### Origin Options
|
|
550
116
|
```typescript
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
// This interface defines the structure of the 'user_profile_view' view (read-only)
|
|
554
|
-
export interface UserProfileView {
|
|
555
|
-
id: number;
|
|
556
|
-
name: string;
|
|
557
|
-
email: string;
|
|
558
|
-
bio: string | null;
|
|
559
|
-
avatar_url: string | null;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Helper types for user_profile_view (view - read-only)
|
|
563
|
-
export type SelectableUserProfileView = Selectable<UserProfileView>;
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
## Config
|
|
567
|
-
|
|
568
|
-
```json
|
|
569
|
-
{
|
|
570
|
-
"origin": {
|
|
571
|
-
"type": "mysql",
|
|
572
|
-
"host": "127.0.0.1",
|
|
573
|
-
"port": 3306,
|
|
574
|
-
"user": "root",
|
|
575
|
-
"password": "secret",
|
|
576
|
-
"database": "myapp",
|
|
577
|
-
"overrideTypes": {
|
|
578
|
-
"json": "z.record(z.string())"
|
|
579
|
-
},
|
|
580
|
-
"ssl": {
|
|
581
|
-
"ca": "path/to/ca.pem",
|
|
582
|
-
"cert": "path/to/cert.pem",
|
|
583
|
-
"key": "path/to/key.pem"
|
|
584
|
-
},
|
|
585
|
-
} | {
|
|
586
|
-
"type": "postgres",
|
|
587
|
-
"host": "127.0.0.1",
|
|
588
|
-
"port": 5432,
|
|
589
|
-
"user": "postgres",
|
|
590
|
-
"password": "secret",
|
|
591
|
-
"database": "myapp",
|
|
592
|
-
"schema": "public",
|
|
593
|
-
"overrideTypes": {
|
|
594
|
-
"jsonb": "z.record(z.string())"
|
|
595
|
-
},
|
|
596
|
-
"ssl": {
|
|
597
|
-
"ca": "path/to/ca.pem",
|
|
598
|
-
"cert": "path/to/cert.pem",
|
|
599
|
-
"key": "path/to/key.pem"
|
|
600
|
-
},
|
|
601
|
-
} | {
|
|
602
|
-
"type": "sqlite",
|
|
603
|
-
"path": "path/to/database.db",
|
|
604
|
-
"overrideTypes": {
|
|
605
|
-
"json": "z.record(z.string())"
|
|
606
|
-
}
|
|
607
|
-
} | {
|
|
608
|
-
"type": "prisma",
|
|
609
|
-
"path": "path/to/schema.prisma",
|
|
610
|
-
"overrideTypes": {
|
|
611
|
-
"Json": "z.record(z.string())"
|
|
612
|
-
}
|
|
613
|
-
},
|
|
614
|
-
"destinations": [
|
|
615
|
-
{
|
|
616
|
-
"type": "zod",
|
|
617
|
-
"useDateType": true,
|
|
618
|
-
"useTrim": false,
|
|
619
|
-
"nullish": false, // When true, nullable fields use nullish() instead of nullable()
|
|
620
|
-
"requiredString": false, // When true, adds min(1) validation to non-nullable string fields
|
|
621
|
-
"header": "import { z } from 'zod';\nimport { CustomValidator } from './validators';",
|
|
622
|
-
"folder": "@zod",
|
|
623
|
-
"suffix": "table"
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
"type": "ts",
|
|
627
|
-
"enumType": "union",
|
|
628
|
-
"modelType": "interface",
|
|
629
|
-
"header": "import { CustomType } from './types';\nimport { BaseModel } from './models';",
|
|
630
|
-
"folder": "types",
|
|
631
|
-
"suffix": "type"
|
|
632
|
-
},
|
|
633
|
-
{
|
|
634
|
-
"type": "kysely",
|
|
635
|
-
"schemaName": "Database",
|
|
636
|
-
"header": "import { Generated, ColumnType } from 'kysely';\nimport { CustomTypes } from './types';",
|
|
637
|
-
"outFile": "db.ts"
|
|
638
|
-
}
|
|
639
|
-
],
|
|
640
|
-
"tables": ["user", "log"],
|
|
641
|
-
"views": ["user_profile_view", "order_summary"],
|
|
642
|
-
"ignore": ["log", "/^temp/"],
|
|
643
|
-
"ignoreViews": ["temp_view", "/^debug_/"],
|
|
644
|
-
"includeViews": true,
|
|
645
|
-
"camelCase": false,
|
|
646
|
-
"silent": false,
|
|
647
|
-
"dryRun": false,
|
|
648
|
-
"magicComments": true
|
|
649
|
-
}
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
| Option | Description |
|
|
653
|
-
| ------ | ----------- |
|
|
654
|
-
| destinations | An array of destination configurations to generate multiple output formats from a single origin |
|
|
655
|
-
| destinations[].type | The type of output to generate: "zod", "ts", or "kysely" |
|
|
656
|
-
| destinations[].useDateType | (Zod only) Use a specialized Zod type for date-like fields instead of string |
|
|
657
|
-
| destinations[].useTrim | (Zod only) Use `z.string().trim()` instead of `z.string()` |
|
|
658
|
-
| destinations[].nullish | (Zod only) Use `nullish()` instead of `nullable()` for nullable fields. In updateable schemas, fields that were already nullable will become nullish |
|
|
659
|
-
| destinations[].version | (Zod only) Zod version to use. Defaults to 3. Set to 4 to use Zod v4 |
|
|
660
|
-
| destinations[].requiredString | (Zod only) Add `min(1)` for non-nullable string fields |
|
|
661
|
-
| destinations[].enumType | (TypeScript only) How to represent enum types: "union" (default) or "enum" |
|
|
662
|
-
| destinations[].modelType | (TypeScript only) How to represent models: "interface" (default) or "type" |
|
|
663
|
-
| destinations[].schemaName | (Kysely only) Name of the database interface (default: "DB") |
|
|
664
|
-
| destinations[].header | Custom header to include at the beginning of generated files (e.g., custom imports) |
|
|
665
|
-
| destinations[].folder | Specify the output directory for the generated files |
|
|
666
|
-
| destinations[].suffix | Suffix to the name of a generated file (eg: `user.table.ts`) |
|
|
667
|
-
| destinations[].outFile | (Kysely only) Specify the output file for the generated content. All tables will be written to this file |
|
|
668
|
-
| tables | Filter the tables to include only those specified |
|
|
669
|
-
| views | Filter the views to include only those specified (requires `includeViews: true`) |
|
|
670
|
-
| ignore | Filter the tables to exclude those specified. If a table name begins and ends with "/", it will be processed as a regular expression |
|
|
671
|
-
| ignoreViews | Filter the views to exclude those specified. If a view name begins and ends with "/", it will be processed as a regular expression |
|
|
672
|
-
| includeViews | When true, database views will be processed and included in the output. Views are read-only (no insertable/updateable schemas) |
|
|
673
|
-
| camelCase | Convert all table names and their properties to camelcase. (eg: `profile_picture` becomes `profilePicture`) |
|
|
674
|
-
| silent | Don't log anything to the console |
|
|
675
|
-
| dryRun | When true, doesn't write files to disk but returns an object with filenames as keys and generated content as values |
|
|
676
|
-
| magicComments | Use @zod and @ts comments to override types (unsupported by SQLite) |
|
|
677
|
-
|
|
678
|
-
## overrideTypes
|
|
679
|
-
|
|
680
|
-
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:
|
|
681
|
-
|
|
682
|
-
### MySQL overrideTypes
|
|
683
|
-
|
|
684
|
-
```json
|
|
117
|
+
// MySQL/PostgreSQL
|
|
685
118
|
{
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
### PostgreSQL overrideTypes
|
|
702
|
-
|
|
703
|
-
```json
|
|
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>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// SQLite
|
|
704
131
|
{
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
"port": 5432,
|
|
709
|
-
"user": "postgres",
|
|
710
|
-
"password": "secret",
|
|
711
|
-
"database": "myapp",
|
|
712
|
-
"schema": "public",
|
|
713
|
-
"overrideTypes": {
|
|
714
|
-
"jsonb": "z.record(z.string())",
|
|
715
|
-
"uuid": "z.string().uuid()"
|
|
716
|
-
}
|
|
717
|
-
}
|
|
132
|
+
type: 'sqlite',
|
|
133
|
+
path: string,
|
|
134
|
+
overrideTypes?: Record<string, string>
|
|
718
135
|
}
|
|
719
|
-
```
|
|
720
136
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
```json
|
|
137
|
+
// Prisma
|
|
724
138
|
{
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
"overrideTypes": {
|
|
729
|
-
"json": "z.record(z.string())",
|
|
730
|
-
"text": "z.string().max(1000)"
|
|
731
|
-
}
|
|
732
|
-
}
|
|
139
|
+
type: 'prisma',
|
|
140
|
+
path: string,
|
|
141
|
+
overrideTypes?: Record<string, string>
|
|
733
142
|
}
|
|
734
143
|
```
|
|
735
144
|
|
|
736
|
-
###
|
|
737
|
-
|
|
738
|
-
```json
|
|
145
|
+
### Destination Options
|
|
146
|
+
```typescript
|
|
739
147
|
{
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
"String": "z.string().min(1)"
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
```
|
|
148
|
+
type: 'zod' | 'ts' | 'kysely',
|
|
149
|
+
folder?: string,
|
|
150
|
+
suffix?: string,
|
|
151
|
+
outFile?: string, // Kysely only
|
|
152
|
+
header?: string, // Custom imports
|
|
750
153
|
|
|
751
|
-
|
|
154
|
+
// Zod specific
|
|
155
|
+
useDateType?: boolean,
|
|
156
|
+
useTrim?: boolean,
|
|
157
|
+
nullish?: boolean,
|
|
158
|
+
requiredString?: boolean,
|
|
159
|
+
version?: 3 | 4,
|
|
752
160
|
|
|
753
|
-
|
|
161
|
+
// TypeScript specific
|
|
162
|
+
enumType?: 'union' | 'enum',
|
|
163
|
+
modelType?: 'interface' | 'type',
|
|
754
164
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
CREATE TABLE `user` (
|
|
759
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
760
|
-
`name` varchar(255) NOT NULL COMMENT '@zod(z.string().min(10).max(255))',
|
|
761
|
-
`email` varchar(255) NOT NULL COMMENT '@zod(z.string().email())',
|
|
762
|
-
PRIMARY KEY (`id`)
|
|
763
|
-
);
|
|
165
|
+
// Kysely specific
|
|
166
|
+
schemaName?: string // Default: 'DB'
|
|
167
|
+
}
|
|
764
168
|
```
|
|
765
169
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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 |
|
|
775
181
|
|
|
776
|
-
|
|
182
|
+
## Magic Comments
|
|
777
183
|
|
|
778
|
-
|
|
184
|
+
Override types for specific columns using database comments (MySQL/PostgreSQL only):
|
|
779
185
|
|
|
780
186
|
```sql
|
|
781
187
|
CREATE TABLE `user` (
|
|
782
|
-
`id` int(11)
|
|
783
|
-
`
|
|
784
|
-
`
|
|
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)',
|
|
785
192
|
PRIMARY KEY (`id`)
|
|
786
193
|
);
|
|
787
194
|
```
|
|
788
195
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
id: number;
|
|
794
|
-
metadata: Record<string, unknown>;
|
|
795
|
-
settings: UserSettings;
|
|
796
|
-
}
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### @kysely Comments
|
|
196
|
+
**Supported Comments:**
|
|
197
|
+
- `@zod(...)` - Override Zod schema
|
|
198
|
+
- `@ts(...)` - Override TypeScript type
|
|
199
|
+
- `@kysely(...)` - Override Kysely type
|
|
800
200
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
```sql
|
|
804
|
-
CREATE TABLE `user` (
|
|
805
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
806
|
-
`metadata` json NOT NULL COMMENT '@kysely(Record<string, string>)',
|
|
807
|
-
PRIMARY KEY (`id`)
|
|
808
|
-
);
|
|
809
|
-
```
|
|
201
|
+
## Type Overrides
|
|
810
202
|
|
|
811
|
-
|
|
203
|
+
Override default types globally in your origin config:
|
|
812
204
|
|
|
813
205
|
```typescript
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
+
}
|
|
817
216
|
}
|
|
818
217
|
```
|
|
819
218
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
CREATE TABLE `product` (
|
|
826
|
-
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
|
827
|
-
`variants` json NOT NULL COMMENT '@ts(Array<{ id: string; price: number; stock: number }>)',
|
|
828
|
-
PRIMARY KEY (`id`)
|
|
829
|
-
);
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
This will generate:
|
|
833
|
-
|
|
834
|
-
```typescript
|
|
835
|
-
export interface Product {
|
|
836
|
-
id: number;
|
|
837
|
-
variants: Array<{ id: string; price: number; stock: number }>;
|
|
838
|
-
}
|
|
839
|
-
```
|
|
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": "3.0.
|
|
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",
|