zod-sqlite 1.0.3 → 1.0.4
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 +375 -375
- package/dist/index.d.mts +1 -2
- package/dist/index.mjs +2 -6
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -58,45 +58,45 @@ npm install zod-sqlite
|
|
|
58
58
|
Here's a simple example creating a users table:
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
61
|
-
import { z } from 'zod'
|
|
62
|
-
import { createTable } from 'zod-sqlite'
|
|
63
|
-
|
|
64
|
-
const users = createTable({
|
|
65
|
-
columns: [
|
|
66
|
-
{ name: 'id', schema: z.int() },
|
|
67
|
-
{ name: 'email', schema: z.email() },
|
|
68
|
-
{ name: 'username', schema: z.string().min(3)
|
|
69
|
-
.max(20) },
|
|
70
|
-
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
71
|
-
],
|
|
72
|
-
indexes: [{ columns: ['email'], name: 'idx_users_email', unique: true }],
|
|
73
|
-
name: 'users',
|
|
74
|
-
primaryKeys: ['id'],
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
// Use the generated SQL
|
|
78
|
-
console.log(users.table)
|
|
79
|
-
// CREATE TABLE users (
|
|
80
|
-
// id INTEGER NOT NULL,
|
|
81
|
-
// email TEXT NOT NULL,
|
|
82
|
-
// username TEXT NOT NULL CHECK(length(username) >= 3 AND length(username) <= 20),
|
|
83
|
-
// created_at DATE NOT NULL DEFAULT '2026-01-06T...',
|
|
84
|
-
// PRIMARY KEY (id)
|
|
85
|
-
// );
|
|
86
|
-
|
|
87
|
-
console.log(users.indexes[0])
|
|
88
|
-
// CREATE UNIQUE INDEX idx_users_email ON users (email);
|
|
89
|
-
|
|
90
|
-
// Validate data at runtime
|
|
91
|
-
const result = users.schema.safeParse({
|
|
92
|
-
created_at: new Date(),
|
|
93
|
-
email: 'user@example.com',
|
|
94
|
-
id: 1,
|
|
95
|
-
username: 'john',
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
// TypeScript type inference
|
|
99
|
-
type User = z.infer<typeof users.schema>
|
|
61
|
+
import { z } from 'zod'
|
|
62
|
+
import { createTable } from 'zod-sqlite'
|
|
63
|
+
|
|
64
|
+
const users = createTable({
|
|
65
|
+
columns: [
|
|
66
|
+
{ name: 'id', schema: z.int() },
|
|
67
|
+
{ name: 'email', schema: z.email() },
|
|
68
|
+
{ name: 'username', schema: z.string().min(3)
|
|
69
|
+
.max(20) },
|
|
70
|
+
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
71
|
+
],
|
|
72
|
+
indexes: [{ columns: ['email'], name: 'idx_users_email', unique: true }],
|
|
73
|
+
name: 'users',
|
|
74
|
+
primaryKeys: ['id'],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Use the generated SQL
|
|
78
|
+
console.log(users.table)
|
|
79
|
+
// CREATE TABLE users (
|
|
80
|
+
// id INTEGER NOT NULL,
|
|
81
|
+
// email TEXT NOT NULL,
|
|
82
|
+
// username TEXT NOT NULL CHECK(length(username) >= 3 AND length(username) <= 20),
|
|
83
|
+
// created_at DATE NOT NULL DEFAULT '2026-01-06T...',
|
|
84
|
+
// PRIMARY KEY (id)
|
|
85
|
+
// );
|
|
86
|
+
|
|
87
|
+
console.log(users.indexes[0])
|
|
88
|
+
// CREATE UNIQUE INDEX idx_users_email ON users (email);
|
|
89
|
+
|
|
90
|
+
// Validate data at runtime
|
|
91
|
+
const result = users.schema.safeParse({
|
|
92
|
+
created_at: new Date(),
|
|
93
|
+
email: 'user@example.com',
|
|
94
|
+
id: 1,
|
|
95
|
+
username: 'john',
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// TypeScript type inference
|
|
99
|
+
type User = z.infer<typeof users.schema>
|
|
100
100
|
// { id: number; email: string; username: string; created_at: Date }
|
|
101
101
|
```
|
|
102
102
|
|
|
@@ -114,7 +114,7 @@ Zod schemas serve as the source of truth for your database structure. Each colum
|
|
|
114
114
|
`createTable` is the primary entry point. It accepts a configuration object and returns three things:
|
|
115
115
|
|
|
116
116
|
```typescript
|
|
117
|
-
const result = createTable(config)
|
|
117
|
+
const result = createTable(config)
|
|
118
118
|
// Returns: { table: string, indexes: string[], schema: ZodObject }
|
|
119
119
|
```
|
|
120
120
|
|
|
@@ -142,7 +142,7 @@ Creates a table definition with SQL statements and validation schema.
|
|
|
142
142
|
|
|
143
143
|
**Returns:**
|
|
144
144
|
|
|
145
|
-
```
|
|
145
|
+
```plaintext
|
|
146
146
|
{
|
|
147
147
|
table: string // CREATE TABLE SQL statement
|
|
148
148
|
indexes: string[] // Array of CREATE INDEX statements
|
|
@@ -153,14 +153,14 @@ Creates a table definition with SQL statements and validation schema.
|
|
|
153
153
|
**Example:**
|
|
154
154
|
|
|
155
155
|
```typescript
|
|
156
|
-
const { indexes, schema, table } = createTable({
|
|
157
|
-
columns: [
|
|
158
|
-
{ name: 'id', schema: z.int() },
|
|
159
|
-
{ name: 'name', schema: z.string() },
|
|
160
|
-
{ name: 'price', schema: z.number().min(0) },
|
|
161
|
-
],
|
|
162
|
-
name: 'products',
|
|
163
|
-
primaryKeys: ['id'],
|
|
156
|
+
const { indexes, schema, table } = createTable({
|
|
157
|
+
columns: [
|
|
158
|
+
{ name: 'id', schema: z.int() },
|
|
159
|
+
{ name: 'name', schema: z.string() },
|
|
160
|
+
{ name: 'price', schema: z.number().min(0) },
|
|
161
|
+
],
|
|
162
|
+
name: 'products',
|
|
163
|
+
primaryKeys: ['id'],
|
|
164
164
|
})
|
|
165
165
|
```
|
|
166
166
|
|
|
@@ -169,11 +169,11 @@ const { indexes, schema, table } = createTable({
|
|
|
169
169
|
Configuration object for table creation.
|
|
170
170
|
|
|
171
171
|
```typescript
|
|
172
|
-
interface TableConfig {
|
|
173
|
-
columns: Array<ColumnConfig> // Array of column definitions
|
|
174
|
-
indexes?: Array<IndexConfig> // Optional index configurations
|
|
175
|
-
name: string // Table name
|
|
176
|
-
primaryKeys: Array<string> // Column names forming primary key
|
|
172
|
+
interface TableConfig {
|
|
173
|
+
columns: Array<ColumnConfig> // Array of column definitions
|
|
174
|
+
indexes?: Array<IndexConfig> // Optional index configurations
|
|
175
|
+
name: string // Table name
|
|
176
|
+
primaryKeys: Array<string> // Column names forming primary key
|
|
177
177
|
}
|
|
178
178
|
```
|
|
179
179
|
|
|
@@ -182,11 +182,11 @@ interface TableConfig {
|
|
|
182
182
|
Configuration for a single column.
|
|
183
183
|
|
|
184
184
|
```typescript
|
|
185
|
-
interface ColumnConfig {
|
|
186
|
-
name: string // Column name
|
|
187
|
-
references?: ForeignKeyReference // Foreign key configuration
|
|
188
|
-
schema: ZodType // Zod schema defining type and validation
|
|
189
|
-
unique?: boolean // Whether values must be unique
|
|
185
|
+
interface ColumnConfig {
|
|
186
|
+
name: string // Column name
|
|
187
|
+
references?: ForeignKeyReference // Foreign key configuration
|
|
188
|
+
schema: ZodType // Zod schema defining type and validation
|
|
189
|
+
unique?: boolean // Whether values must be unique
|
|
190
190
|
}
|
|
191
191
|
```
|
|
192
192
|
|
|
@@ -195,18 +195,18 @@ interface ColumnConfig {
|
|
|
195
195
|
Foreign key constraint configuration.
|
|
196
196
|
|
|
197
197
|
```typescript
|
|
198
|
-
interface ForeignKeyReference {
|
|
199
|
-
column: string // Referenced column name
|
|
200
|
-
onDelete?: ForeignKeyAction // Action on parent deletion
|
|
201
|
-
onUpdate?: ForeignKeyAction // Action on parent update
|
|
202
|
-
table: string // Referenced table name
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
type ForeignKeyAction
|
|
206
|
-
= | 'CASCADE'
|
|
207
|
-
| 'NO ACTION'
|
|
208
|
-
| 'RESTRICT'
|
|
209
|
-
| 'SET DEFAULT'
|
|
198
|
+
interface ForeignKeyReference {
|
|
199
|
+
column: string // Referenced column name
|
|
200
|
+
onDelete?: ForeignKeyAction // Action on parent deletion
|
|
201
|
+
onUpdate?: ForeignKeyAction // Action on parent update
|
|
202
|
+
table: string // Referenced table name
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
type ForeignKeyAction
|
|
206
|
+
= | 'CASCADE'
|
|
207
|
+
| 'NO ACTION'
|
|
208
|
+
| 'RESTRICT'
|
|
209
|
+
| 'SET DEFAULT'
|
|
210
210
|
| 'SET NULL'
|
|
211
211
|
```
|
|
212
212
|
|
|
@@ -215,11 +215,11 @@ type ForeignKeyAction
|
|
|
215
215
|
Index configuration for query optimization.
|
|
216
216
|
|
|
217
217
|
```typescript
|
|
218
|
-
interface IndexConfig {
|
|
219
|
-
columns: Array<string> // Indexed column names
|
|
220
|
-
name: string // Index name
|
|
221
|
-
unique?: boolean // Whether this is a unique index
|
|
222
|
-
where?: string // Optional WHERE clause for partial index
|
|
218
|
+
interface IndexConfig {
|
|
219
|
+
columns: Array<string> // Indexed column names
|
|
220
|
+
name: string // Index name
|
|
221
|
+
unique?: boolean // Whether this is a unique index
|
|
222
|
+
where?: string // Optional WHERE clause for partial index
|
|
223
223
|
}
|
|
224
224
|
```
|
|
225
225
|
|
|
@@ -271,9 +271,9 @@ These Zod wrappers are automatically unwrapped:
|
|
|
271
271
|
- `.default(value)` - Adds DEFAULT clause
|
|
272
272
|
|
|
273
273
|
```typescript
|
|
274
|
-
z.string().optional() // TEXT (nullable)
|
|
275
|
-
z.number().default(0) // REAL NOT NULL DEFAULT 0
|
|
276
|
-
z.string().nullable()
|
|
274
|
+
z.string().optional() // TEXT (nullable)
|
|
275
|
+
z.number().default(0) // REAL NOT NULL DEFAULT 0
|
|
276
|
+
z.string().nullable()
|
|
277
277
|
.default('n/a') // TEXT DEFAULT 'n/a'
|
|
278
278
|
```
|
|
279
279
|
|
|
@@ -281,14 +281,14 @@ z.string().nullable()
|
|
|
281
281
|
|
|
282
282
|
### Basic Columns
|
|
283
283
|
|
|
284
|
-
```
|
|
284
|
+
```plaintext
|
|
285
285
|
{ name: 'email', schema: z.email() }
|
|
286
286
|
// SQL: email TEXT NOT NULL
|
|
287
287
|
```
|
|
288
288
|
|
|
289
289
|
### Optional and Nullable Columns
|
|
290
290
|
|
|
291
|
-
```
|
|
291
|
+
```plaintext
|
|
292
292
|
{ name: 'bio', schema: z.string().optional() }
|
|
293
293
|
// SQL: bio TEXT
|
|
294
294
|
|
|
@@ -298,7 +298,7 @@ z.string().nullable()
|
|
|
298
298
|
|
|
299
299
|
### Columns with Default Values
|
|
300
300
|
|
|
301
|
-
```
|
|
301
|
+
```plaintext
|
|
302
302
|
{ name: 'status', schema: z.enum(['active', 'inactive']).default('active') }
|
|
303
303
|
// SQL: status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'inactive'))
|
|
304
304
|
|
|
@@ -308,7 +308,7 @@ z.string().nullable()
|
|
|
308
308
|
|
|
309
309
|
### Unique Columns
|
|
310
310
|
|
|
311
|
-
```
|
|
311
|
+
```plaintext
|
|
312
312
|
{ name: 'username', schema: z.string(), unique: true }
|
|
313
313
|
// SQL: username TEXT NOT NULL UNIQUE
|
|
314
314
|
```
|
|
@@ -319,7 +319,7 @@ SQL CHECK constraints are automatically generated from Zod validation rules.
|
|
|
319
319
|
|
|
320
320
|
### Enum Constraints
|
|
321
321
|
|
|
322
|
-
```
|
|
322
|
+
```plaintext
|
|
323
323
|
{
|
|
324
324
|
name: 'role',
|
|
325
325
|
schema: z.enum(['admin', 'user', 'guest'])
|
|
@@ -329,7 +329,7 @@ SQL CHECK constraints are automatically generated from Zod validation rules.
|
|
|
329
329
|
|
|
330
330
|
### Literal Constraints
|
|
331
331
|
|
|
332
|
-
```
|
|
332
|
+
```plaintext
|
|
333
333
|
{
|
|
334
334
|
name: 'type',
|
|
335
335
|
schema: z.literal('premium')
|
|
@@ -349,7 +349,7 @@ SQL CHECK constraints are automatically generated from Zod validation rules.
|
|
|
349
349
|
|
|
350
350
|
### Numeric Range Constraints
|
|
351
351
|
|
|
352
|
-
```
|
|
352
|
+
```plaintext
|
|
353
353
|
{
|
|
354
354
|
name: 'age',
|
|
355
355
|
schema: z.int().min(18).max(120)
|
|
@@ -365,7 +365,7 @@ SQL CHECK constraints are automatically generated from Zod validation rules.
|
|
|
365
365
|
|
|
366
366
|
### String Length Constraints
|
|
367
367
|
|
|
368
|
-
```
|
|
368
|
+
```plaintext
|
|
369
369
|
{
|
|
370
370
|
name: 'username',
|
|
371
371
|
schema: z.string().min(3).max(20)
|
|
@@ -386,14 +386,14 @@ SQL CHECK constraints are automatically generated from Zod validation rules.
|
|
|
386
386
|
The most common pattern for entity tables:
|
|
387
387
|
|
|
388
388
|
```typescript
|
|
389
|
-
createTable({
|
|
390
|
-
columns: [
|
|
391
|
-
{ name: 'id', schema: z.int() },
|
|
392
|
-
{ name: 'email', schema: z.string() },
|
|
393
|
-
],
|
|
394
|
-
name: 'users',
|
|
395
|
-
primaryKeys: ['id'],
|
|
396
|
-
})
|
|
389
|
+
createTable({
|
|
390
|
+
columns: [
|
|
391
|
+
{ name: 'id', schema: z.int() },
|
|
392
|
+
{ name: 'email', schema: z.string() },
|
|
393
|
+
],
|
|
394
|
+
name: 'users',
|
|
395
|
+
primaryKeys: ['id'],
|
|
396
|
+
})
|
|
397
397
|
// SQL: PRIMARY KEY (id)
|
|
398
398
|
```
|
|
399
399
|
|
|
@@ -402,35 +402,35 @@ createTable({
|
|
|
402
402
|
Used for junction tables and multi-tenant data:
|
|
403
403
|
|
|
404
404
|
```typescript
|
|
405
|
-
createTable({
|
|
406
|
-
columns: [
|
|
407
|
-
{ name: 'user_id', schema: z.int() },
|
|
408
|
-
{ name: 'role_id', schema: z.int() },
|
|
409
|
-
],
|
|
410
|
-
name: 'user_roles',
|
|
411
|
-
primaryKeys: [
|
|
412
|
-
'user_id',
|
|
413
|
-
'role_id',
|
|
414
|
-
],
|
|
415
|
-
})
|
|
405
|
+
createTable({
|
|
406
|
+
columns: [
|
|
407
|
+
{ name: 'user_id', schema: z.int() },
|
|
408
|
+
{ name: 'role_id', schema: z.int() },
|
|
409
|
+
],
|
|
410
|
+
name: 'user_roles',
|
|
411
|
+
primaryKeys: [
|
|
412
|
+
'user_id',
|
|
413
|
+
'role_id',
|
|
414
|
+
],
|
|
415
|
+
})
|
|
416
416
|
// SQL: PRIMARY KEY (user_id, role_id)
|
|
417
417
|
```
|
|
418
418
|
|
|
419
419
|
### Multi-Tenant Example
|
|
420
420
|
|
|
421
421
|
```typescript
|
|
422
|
-
createTable({
|
|
423
|
-
columns: [
|
|
424
|
-
{ name: 'tenant_id', schema: z.string() },
|
|
425
|
-
{ name: 'doc_id', schema: z.int() },
|
|
426
|
-
{ name: 'title', schema: z.string() },
|
|
427
|
-
],
|
|
428
|
-
name: 'documents',
|
|
429
|
-
primaryKeys: [
|
|
430
|
-
'tenant_id',
|
|
431
|
-
'doc_id',
|
|
432
|
-
],
|
|
433
|
-
})
|
|
422
|
+
createTable({
|
|
423
|
+
columns: [
|
|
424
|
+
{ name: 'tenant_id', schema: z.string() },
|
|
425
|
+
{ name: 'doc_id', schema: z.int() },
|
|
426
|
+
{ name: 'title', schema: z.string() },
|
|
427
|
+
],
|
|
428
|
+
name: 'documents',
|
|
429
|
+
primaryKeys: [
|
|
430
|
+
'tenant_id',
|
|
431
|
+
'doc_id',
|
|
432
|
+
],
|
|
433
|
+
})
|
|
434
434
|
// SQL: PRIMARY KEY (tenant_id, doc_id)
|
|
435
435
|
```
|
|
436
436
|
|
|
@@ -439,21 +439,21 @@ createTable({
|
|
|
439
439
|
### Basic Foreign Key
|
|
440
440
|
|
|
441
441
|
```typescript
|
|
442
|
-
createTable({
|
|
443
|
-
columns: [
|
|
444
|
-
{ name: 'id', schema: z.int() },
|
|
445
|
-
{
|
|
446
|
-
name: 'author_id',
|
|
447
|
-
references: {
|
|
448
|
-
column: 'id',
|
|
449
|
-
table: 'users',
|
|
450
|
-
},
|
|
451
|
-
schema: z.int(),
|
|
452
|
-
},
|
|
453
|
-
],
|
|
454
|
-
name: 'posts',
|
|
455
|
-
primaryKeys: ['id'],
|
|
456
|
-
})
|
|
442
|
+
createTable({
|
|
443
|
+
columns: [
|
|
444
|
+
{ name: 'id', schema: z.int() },
|
|
445
|
+
{
|
|
446
|
+
name: 'author_id',
|
|
447
|
+
references: {
|
|
448
|
+
column: 'id',
|
|
449
|
+
table: 'users',
|
|
450
|
+
},
|
|
451
|
+
schema: z.int(),
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
name: 'posts',
|
|
455
|
+
primaryKeys: ['id'],
|
|
456
|
+
})
|
|
457
457
|
// SQL: author_id INTEGER NOT NULL REFERENCES users(id)
|
|
458
458
|
```
|
|
459
459
|
|
|
@@ -461,7 +461,7 @@ createTable({
|
|
|
461
461
|
|
|
462
462
|
Automatically delete child records when parent is deleted:
|
|
463
463
|
|
|
464
|
-
```
|
|
464
|
+
```plaintext
|
|
465
465
|
{
|
|
466
466
|
name: 'user_id',
|
|
467
467
|
schema: z.int(),
|
|
@@ -478,7 +478,7 @@ Automatically delete child records when parent is deleted:
|
|
|
478
478
|
|
|
479
479
|
Prevent deletion of parent if children exist:
|
|
480
480
|
|
|
481
|
-
```
|
|
481
|
+
```plaintext
|
|
482
482
|
{
|
|
483
483
|
name: 'category_id',
|
|
484
484
|
schema: z.int(),
|
|
@@ -495,7 +495,7 @@ Prevent deletion of parent if children exist:
|
|
|
495
495
|
|
|
496
496
|
Set foreign key to NULL when parent is deleted:
|
|
497
497
|
|
|
498
|
-
```
|
|
498
|
+
```plaintext
|
|
499
499
|
{
|
|
500
500
|
name: 'manager_id',
|
|
501
501
|
schema: z.int().nullable(),
|
|
@@ -512,7 +512,7 @@ Set foreign key to NULL when parent is deleted:
|
|
|
512
512
|
|
|
513
513
|
Propagate updates to child records:
|
|
514
514
|
|
|
515
|
-
```
|
|
515
|
+
```plaintext
|
|
516
516
|
{
|
|
517
517
|
name: 'parent_id',
|
|
518
518
|
schema: z.int(),
|
|
@@ -530,7 +530,7 @@ Propagate updates to child records:
|
|
|
530
530
|
|
|
531
531
|
### Simple Index
|
|
532
532
|
|
|
533
|
-
```
|
|
533
|
+
```plaintext
|
|
534
534
|
indexes: [
|
|
535
535
|
{
|
|
536
536
|
columns: ['email'],
|
|
@@ -545,13 +545,13 @@ indexes: [
|
|
|
545
545
|
Enforce uniqueness at the database level:
|
|
546
546
|
|
|
547
547
|
```typescript
|
|
548
|
-
indexes: [
|
|
549
|
-
{
|
|
550
|
-
columns: ['username'],
|
|
551
|
-
name: 'idx_users_username',
|
|
552
|
-
unique: true,
|
|
553
|
-
},
|
|
554
|
-
]
|
|
548
|
+
indexes: [
|
|
549
|
+
{
|
|
550
|
+
columns: ['username'],
|
|
551
|
+
name: 'idx_users_username',
|
|
552
|
+
unique: true,
|
|
553
|
+
},
|
|
554
|
+
]
|
|
555
555
|
// SQL: CREATE UNIQUE INDEX idx_users_username ON users (username);
|
|
556
556
|
```
|
|
557
557
|
|
|
@@ -560,15 +560,15 @@ indexes: [
|
|
|
560
560
|
Index multiple columns together for multi-column queries:
|
|
561
561
|
|
|
562
562
|
```typescript
|
|
563
|
-
indexes: [
|
|
564
|
-
{
|
|
565
|
-
columns: [
|
|
566
|
-
'author_id',
|
|
567
|
-
'created_at',
|
|
568
|
-
],
|
|
569
|
-
name: 'idx_posts_author_date',
|
|
570
|
-
},
|
|
571
|
-
]
|
|
563
|
+
indexes: [
|
|
564
|
+
{
|
|
565
|
+
columns: [
|
|
566
|
+
'author_id',
|
|
567
|
+
'created_at',
|
|
568
|
+
],
|
|
569
|
+
name: 'idx_posts_author_date',
|
|
570
|
+
},
|
|
571
|
+
]
|
|
572
572
|
// SQL: CREATE INDEX idx_posts_author_date ON posts (author_id, created_at);
|
|
573
573
|
```
|
|
574
574
|
|
|
@@ -583,13 +583,13 @@ SELECT * FROM posts WHERE author_id = 123 AND created_at > '2024-01-01';
|
|
|
583
583
|
Index only rows matching a condition:
|
|
584
584
|
|
|
585
585
|
```typescript
|
|
586
|
-
indexes: [
|
|
587
|
-
{
|
|
588
|
-
columns: ['last_login'],
|
|
589
|
-
name: 'idx_active_users',
|
|
590
|
-
where: 'deleted_at IS NULL',
|
|
591
|
-
},
|
|
592
|
-
]
|
|
586
|
+
indexes: [
|
|
587
|
+
{
|
|
588
|
+
columns: ['last_login'],
|
|
589
|
+
name: 'idx_active_users',
|
|
590
|
+
where: 'deleted_at IS NULL',
|
|
591
|
+
},
|
|
592
|
+
]
|
|
593
593
|
// SQL: CREATE INDEX idx_active_users ON posts (last_login) WHERE deleted_at IS NULL;
|
|
594
594
|
```
|
|
595
595
|
|
|
@@ -603,160 +603,160 @@ Benefits:
|
|
|
603
603
|
### Complete Blog Example
|
|
604
604
|
|
|
605
605
|
```typescript
|
|
606
|
-
// Users table
|
|
607
|
-
const users = createTable({
|
|
608
|
-
columns: [
|
|
609
|
-
{ name: 'id', schema: z.int() },
|
|
610
|
-
{ name: 'email', schema: z.email(), unique: true },
|
|
611
|
-
{ name: 'username', schema: z.string().min(3)
|
|
612
|
-
.max(20), unique: true },
|
|
613
|
-
{ name: 'role', schema: z.enum([
|
|
614
|
-
'admin',
|
|
615
|
-
'author',
|
|
616
|
-
'reader',
|
|
617
|
-
]).default('reader') },
|
|
618
|
-
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
619
|
-
],
|
|
620
|
-
indexes: [
|
|
621
|
-
{ columns: ['email'], name: 'idx_users_email', unique: true },
|
|
622
|
-
{ columns: ['role'], name: 'idx_users_role' },
|
|
623
|
-
],
|
|
624
|
-
name: 'users',
|
|
625
|
-
primaryKeys: ['id'],
|
|
626
|
-
})
|
|
627
|
-
|
|
628
|
-
// Posts table with foreign key
|
|
629
|
-
const posts = createTable({
|
|
630
|
-
columns: [
|
|
631
|
-
{ name: 'id', schema: z.int() },
|
|
632
|
-
{ name: 'title', schema: z.string().min(1)
|
|
633
|
-
.max(200) },
|
|
634
|
-
{ name: 'content', schema: z.string() },
|
|
635
|
-
{ name: 'status', schema: z.enum([
|
|
636
|
-
'draft',
|
|
637
|
-
'published',
|
|
638
|
-
'archived',
|
|
639
|
-
]).default('draft') },
|
|
640
|
-
{
|
|
641
|
-
name: 'author_id',
|
|
642
|
-
references: {
|
|
643
|
-
column: 'id',
|
|
644
|
-
onDelete: 'CASCADE',
|
|
645
|
-
table: 'users',
|
|
646
|
-
},
|
|
647
|
-
schema: z.int(),
|
|
648
|
-
},
|
|
649
|
-
{ name: 'published_at', schema: z.date().nullable() },
|
|
650
|
-
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
651
|
-
],
|
|
652
|
-
indexes: [
|
|
653
|
-
{ columns: ['author_id'], name: 'idx_posts_author' },
|
|
654
|
-
{
|
|
655
|
-
columns: ['published_at'],
|
|
656
|
-
name: 'idx_posts_published',
|
|
657
|
-
where: 'status = \'published\'',
|
|
658
|
-
},
|
|
659
|
-
{ columns: [
|
|
660
|
-
'status',
|
|
661
|
-
'created_at',
|
|
662
|
-
], name: 'idx_posts_status_date' },
|
|
663
|
-
],
|
|
664
|
-
name: 'posts',
|
|
665
|
-
primaryKeys: ['id'],
|
|
666
|
-
})
|
|
667
|
-
|
|
668
|
-
// Junction table for tags (many-to-many)
|
|
669
|
-
const postTags = createTable({
|
|
670
|
-
columns: [
|
|
671
|
-
{
|
|
672
|
-
name: 'post_id',
|
|
673
|
-
references: {
|
|
674
|
-
column: 'id',
|
|
675
|
-
onDelete: 'CASCADE',
|
|
676
|
-
table: 'posts',
|
|
677
|
-
},
|
|
678
|
-
schema: z.int(),
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
name: 'tag_id',
|
|
682
|
-
references: {
|
|
683
|
-
column: 'id',
|
|
684
|
-
onDelete: 'CASCADE',
|
|
685
|
-
table: 'tags',
|
|
686
|
-
},
|
|
687
|
-
schema: z.int(),
|
|
688
|
-
},
|
|
689
|
-
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
690
|
-
],
|
|
691
|
-
name: 'post_tags',
|
|
692
|
-
primaryKeys: [
|
|
693
|
-
'post_id',
|
|
694
|
-
'tag_id',
|
|
695
|
-
],
|
|
606
|
+
// Users table
|
|
607
|
+
const users = createTable({
|
|
608
|
+
columns: [
|
|
609
|
+
{ name: 'id', schema: z.int() },
|
|
610
|
+
{ name: 'email', schema: z.email(), unique: true },
|
|
611
|
+
{ name: 'username', schema: z.string().min(3)
|
|
612
|
+
.max(20), unique: true },
|
|
613
|
+
{ name: 'role', schema: z.enum([
|
|
614
|
+
'admin',
|
|
615
|
+
'author',
|
|
616
|
+
'reader',
|
|
617
|
+
]).default('reader') },
|
|
618
|
+
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
619
|
+
],
|
|
620
|
+
indexes: [
|
|
621
|
+
{ columns: ['email'], name: 'idx_users_email', unique: true },
|
|
622
|
+
{ columns: ['role'], name: 'idx_users_role' },
|
|
623
|
+
],
|
|
624
|
+
name: 'users',
|
|
625
|
+
primaryKeys: ['id'],
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
// Posts table with foreign key
|
|
629
|
+
const posts = createTable({
|
|
630
|
+
columns: [
|
|
631
|
+
{ name: 'id', schema: z.int() },
|
|
632
|
+
{ name: 'title', schema: z.string().min(1)
|
|
633
|
+
.max(200) },
|
|
634
|
+
{ name: 'content', schema: z.string() },
|
|
635
|
+
{ name: 'status', schema: z.enum([
|
|
636
|
+
'draft',
|
|
637
|
+
'published',
|
|
638
|
+
'archived',
|
|
639
|
+
]).default('draft') },
|
|
640
|
+
{
|
|
641
|
+
name: 'author_id',
|
|
642
|
+
references: {
|
|
643
|
+
column: 'id',
|
|
644
|
+
onDelete: 'CASCADE',
|
|
645
|
+
table: 'users',
|
|
646
|
+
},
|
|
647
|
+
schema: z.int(),
|
|
648
|
+
},
|
|
649
|
+
{ name: 'published_at', schema: z.date().nullable() },
|
|
650
|
+
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
651
|
+
],
|
|
652
|
+
indexes: [
|
|
653
|
+
{ columns: ['author_id'], name: 'idx_posts_author' },
|
|
654
|
+
{
|
|
655
|
+
columns: ['published_at'],
|
|
656
|
+
name: 'idx_posts_published',
|
|
657
|
+
where: 'status = \'published\'',
|
|
658
|
+
},
|
|
659
|
+
{ columns: [
|
|
660
|
+
'status',
|
|
661
|
+
'created_at',
|
|
662
|
+
], name: 'idx_posts_status_date' },
|
|
663
|
+
],
|
|
664
|
+
name: 'posts',
|
|
665
|
+
primaryKeys: ['id'],
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
// Junction table for tags (many-to-many)
|
|
669
|
+
const postTags = createTable({
|
|
670
|
+
columns: [
|
|
671
|
+
{
|
|
672
|
+
name: 'post_id',
|
|
673
|
+
references: {
|
|
674
|
+
column: 'id',
|
|
675
|
+
onDelete: 'CASCADE',
|
|
676
|
+
table: 'posts',
|
|
677
|
+
},
|
|
678
|
+
schema: z.int(),
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
name: 'tag_id',
|
|
682
|
+
references: {
|
|
683
|
+
column: 'id',
|
|
684
|
+
onDelete: 'CASCADE',
|
|
685
|
+
table: 'tags',
|
|
686
|
+
},
|
|
687
|
+
schema: z.int(),
|
|
688
|
+
},
|
|
689
|
+
{ name: 'created_at', schema: z.date().default(new Date()) },
|
|
690
|
+
],
|
|
691
|
+
name: 'post_tags',
|
|
692
|
+
primaryKeys: [
|
|
693
|
+
'post_id',
|
|
694
|
+
'tag_id',
|
|
695
|
+
],
|
|
696
696
|
})
|
|
697
697
|
```
|
|
698
698
|
|
|
699
699
|
### E-commerce Example
|
|
700
700
|
|
|
701
701
|
```typescript
|
|
702
|
-
const products = createTable({
|
|
703
|
-
columns: [
|
|
704
|
-
{ name: 'id', schema: z.int() },
|
|
705
|
-
{ name: 'sku', schema: z.string().length(10), unique: true },
|
|
706
|
-
{ name: 'name', schema: z.string().min(1) },
|
|
707
|
-
{ name: 'description', schema: z.string().optional() },
|
|
708
|
-
{ name: 'price', schema: z.number().min(0) },
|
|
709
|
-
{ name: 'stock', schema: z.int().min(0)
|
|
710
|
-
.default(0) },
|
|
711
|
-
{ name: 'active', schema: z.boolean().default(true) },
|
|
712
|
-
{
|
|
713
|
-
name: 'category_id',
|
|
714
|
-
references: {
|
|
715
|
-
column: 'id',
|
|
716
|
-
onDelete: 'RESTRICT',
|
|
717
|
-
table: 'categories',
|
|
718
|
-
},
|
|
719
|
-
schema: z.int(),
|
|
720
|
-
},
|
|
721
|
-
],
|
|
722
|
-
indexes: [
|
|
723
|
-
{ columns: ['sku'], name: 'idx_products_sku', unique: true },
|
|
724
|
-
{ columns: ['category_id'], name: 'idx_products_category' },
|
|
725
|
-
{
|
|
726
|
-
columns: [
|
|
727
|
-
'price',
|
|
728
|
-
'stock',
|
|
729
|
-
],
|
|
730
|
-
name: 'idx_products_active',
|
|
731
|
-
where: 'active = 1',
|
|
732
|
-
},
|
|
733
|
-
],
|
|
734
|
-
name: 'products',
|
|
735
|
-
primaryKeys: ['id'],
|
|
702
|
+
const products = createTable({
|
|
703
|
+
columns: [
|
|
704
|
+
{ name: 'id', schema: z.int() },
|
|
705
|
+
{ name: 'sku', schema: z.string().length(10), unique: true },
|
|
706
|
+
{ name: 'name', schema: z.string().min(1) },
|
|
707
|
+
{ name: 'description', schema: z.string().optional() },
|
|
708
|
+
{ name: 'price', schema: z.number().min(0) },
|
|
709
|
+
{ name: 'stock', schema: z.int().min(0)
|
|
710
|
+
.default(0) },
|
|
711
|
+
{ name: 'active', schema: z.boolean().default(true) },
|
|
712
|
+
{
|
|
713
|
+
name: 'category_id',
|
|
714
|
+
references: {
|
|
715
|
+
column: 'id',
|
|
716
|
+
onDelete: 'RESTRICT',
|
|
717
|
+
table: 'categories',
|
|
718
|
+
},
|
|
719
|
+
schema: z.int(),
|
|
720
|
+
},
|
|
721
|
+
],
|
|
722
|
+
indexes: [
|
|
723
|
+
{ columns: ['sku'], name: 'idx_products_sku', unique: true },
|
|
724
|
+
{ columns: ['category_id'], name: 'idx_products_category' },
|
|
725
|
+
{
|
|
726
|
+
columns: [
|
|
727
|
+
'price',
|
|
728
|
+
'stock',
|
|
729
|
+
],
|
|
730
|
+
name: 'idx_products_active',
|
|
731
|
+
where: 'active = 1',
|
|
732
|
+
},
|
|
733
|
+
],
|
|
734
|
+
name: 'products',
|
|
735
|
+
primaryKeys: ['id'],
|
|
736
736
|
})
|
|
737
737
|
```
|
|
738
738
|
|
|
739
739
|
### Self-Referencing Table
|
|
740
740
|
|
|
741
741
|
```typescript
|
|
742
|
-
const employees = createTable({
|
|
743
|
-
columns: [
|
|
744
|
-
{ name: 'id', schema: z.int() },
|
|
745
|
-
{ name: 'name', schema: z.string() },
|
|
746
|
-
{ name: 'email', schema: z.email(), unique: true },
|
|
747
|
-
{
|
|
748
|
-
name: 'manager_id',
|
|
749
|
-
references: {
|
|
750
|
-
column: 'id',
|
|
751
|
-
onDelete: 'SET NULL',
|
|
752
|
-
table: 'employees',
|
|
753
|
-
},
|
|
754
|
-
schema: z.int().nullable(),
|
|
755
|
-
},
|
|
756
|
-
],
|
|
757
|
-
indexes: [{ columns: ['manager_id'], name: 'idx_employees_manager' }],
|
|
758
|
-
name: 'employees',
|
|
759
|
-
primaryKeys: ['id'],
|
|
742
|
+
const employees = createTable({
|
|
743
|
+
columns: [
|
|
744
|
+
{ name: 'id', schema: z.int() },
|
|
745
|
+
{ name: 'name', schema: z.string() },
|
|
746
|
+
{ name: 'email', schema: z.email(), unique: true },
|
|
747
|
+
{
|
|
748
|
+
name: 'manager_id',
|
|
749
|
+
references: {
|
|
750
|
+
column: 'id',
|
|
751
|
+
onDelete: 'SET NULL',
|
|
752
|
+
table: 'employees',
|
|
753
|
+
},
|
|
754
|
+
schema: z.int().nullable(),
|
|
755
|
+
},
|
|
756
|
+
],
|
|
757
|
+
indexes: [{ columns: ['manager_id'], name: 'idx_employees_manager' }],
|
|
758
|
+
name: 'employees',
|
|
759
|
+
primaryKeys: ['id'],
|
|
760
760
|
})
|
|
761
761
|
```
|
|
762
762
|
|
|
@@ -768,20 +768,20 @@ const employees = createTable({
|
|
|
768
768
|
|
|
769
769
|
2. **Use appropriate types**: Choose the most specific Zod type that matches your data:
|
|
770
770
|
```typescript
|
|
771
|
-
z.int() // For IDs and counts
|
|
772
|
-
z.number().min(0) // For prices and quantities
|
|
773
|
-
z.enum([
|
|
774
|
-
'a',
|
|
775
|
-
'b',
|
|
776
|
-
]) // For status fields
|
|
771
|
+
z.int() // For IDs and counts
|
|
772
|
+
z.number().min(0) // For prices and quantities
|
|
773
|
+
z.enum([
|
|
774
|
+
'a',
|
|
775
|
+
'b',
|
|
776
|
+
]) // For status fields
|
|
777
777
|
z.email() // For email addresses
|
|
778
778
|
```
|
|
779
779
|
|
|
780
780
|
3. **Add validation at the schema level**: Leverage Zod's validation to prevent invalid data:
|
|
781
781
|
```typescript
|
|
782
|
-
z.string().min(3)
|
|
783
|
-
.max(50) // Username length
|
|
784
|
-
z.number().min(0)
|
|
782
|
+
z.string().min(3)
|
|
783
|
+
.max(50) // Username length
|
|
784
|
+
z.number().min(0)
|
|
785
785
|
.max(5) // Rating scale
|
|
786
786
|
```
|
|
787
787
|
|
|
@@ -807,23 +807,23 @@ const employees = createTable({
|
|
|
807
807
|
|
|
808
808
|
3. **Use composite indexes wisely**: Order matters - most selective column first:
|
|
809
809
|
```typescript
|
|
810
|
-
indexes: [
|
|
811
|
-
// Good: Filters by tenant first (high selectivity)
|
|
812
|
-
{ columns: [
|
|
813
|
-
'tenant_id',
|
|
814
|
-
'user_id',
|
|
815
|
-
], name: 'idx_tenant_user' },
|
|
810
|
+
indexes: [
|
|
811
|
+
// Good: Filters by tenant first (high selectivity)
|
|
812
|
+
{ columns: [
|
|
813
|
+
'tenant_id',
|
|
814
|
+
'user_id',
|
|
815
|
+
], name: 'idx_tenant_user' },
|
|
816
816
|
]
|
|
817
817
|
```
|
|
818
818
|
|
|
819
819
|
4. **Consider partial indexes**: Reduce index size for filtered queries:
|
|
820
820
|
```typescript
|
|
821
|
-
indexes: [
|
|
822
|
-
{
|
|
823
|
-
columns: ['created_at'],
|
|
824
|
-
name: 'idx_active_records',
|
|
825
|
-
where: 'deleted_at IS NULL', // Only index active records
|
|
826
|
-
},
|
|
821
|
+
indexes: [
|
|
822
|
+
{
|
|
823
|
+
columns: ['created_at'],
|
|
824
|
+
name: 'idx_active_records',
|
|
825
|
+
where: 'deleted_at IS NULL', // Only index active records
|
|
826
|
+
},
|
|
827
827
|
]
|
|
828
828
|
```
|
|
829
829
|
|
|
@@ -847,15 +847,15 @@ const employees = createTable({
|
|
|
847
847
|
|
|
848
848
|
1. **Use type inference**: Let TypeScript infer types from your schema:
|
|
849
849
|
```typescript
|
|
850
|
-
const { schema } = createTable(config)
|
|
850
|
+
const { schema } = createTable(config)
|
|
851
851
|
type User = z.infer<typeof schema>
|
|
852
852
|
```
|
|
853
853
|
|
|
854
854
|
2. **Validate at boundaries**: Use the generated schema to validate external data:
|
|
855
855
|
```typescript
|
|
856
|
-
const result = users.schema.safeParse(inputData)
|
|
857
|
-
if (!result.success) {
|
|
858
|
-
console.error(result.error)
|
|
856
|
+
const result = users.schema.safeParse(inputData)
|
|
857
|
+
if (!result.success) {
|
|
858
|
+
console.error(result.error)
|
|
859
859
|
}
|
|
860
860
|
```
|
|
861
861
|
|
|
@@ -870,43 +870,43 @@ Single-column foreign keys are supported at the column level. Composite foreign
|
|
|
870
870
|
**Pattern for composite foreign keys:**
|
|
871
871
|
|
|
872
872
|
```typescript
|
|
873
|
-
// 1. Create parent table with composite primary key
|
|
874
|
-
const { table: ordersTable } = createTable({
|
|
875
|
-
columns: [
|
|
876
|
-
{ name: 'tenant_id', schema: z.string() },
|
|
877
|
-
{ name: 'order_id', schema: z.int() },
|
|
878
|
-
{ name: 'total', schema: z.number() },
|
|
879
|
-
],
|
|
880
|
-
name: 'orders',
|
|
881
|
-
primaryKeys: [
|
|
882
|
-
'tenant_id',
|
|
883
|
-
'order_id',
|
|
884
|
-
],
|
|
885
|
-
})
|
|
886
|
-
|
|
887
|
-
// 2. Create child table with matching columns
|
|
888
|
-
const { table: itemsTable } = createTable({
|
|
889
|
-
columns: [
|
|
890
|
-
{ name: 'id', schema: z.int() },
|
|
891
|
-
{ name: 'tenant_id', schema: z.string() },
|
|
892
|
-
{ name: 'order_id', schema: z.int() },
|
|
893
|
-
{ name: 'product', schema: z.string() },
|
|
894
|
-
],
|
|
895
|
-
name: 'order_items',
|
|
896
|
-
primaryKeys: ['id'],
|
|
897
|
-
})
|
|
898
|
-
|
|
899
|
-
// 3. Execute table creation
|
|
900
|
-
db.exec(ordersTable)
|
|
901
|
-
db.exec(itemsTable)
|
|
902
|
-
|
|
903
|
-
// 4. Add composite foreign key constraint
|
|
904
|
-
db.exec(`
|
|
905
|
-
ALTER TABLE order_items
|
|
906
|
-
ADD CONSTRAINT fk_order
|
|
907
|
-
FOREIGN KEY (tenant_id, order_id)
|
|
908
|
-
REFERENCES orders(tenant_id, order_id)
|
|
909
|
-
ON DELETE CASCADE
|
|
873
|
+
// 1. Create parent table with composite primary key
|
|
874
|
+
const { table: ordersTable } = createTable({
|
|
875
|
+
columns: [
|
|
876
|
+
{ name: 'tenant_id', schema: z.string() },
|
|
877
|
+
{ name: 'order_id', schema: z.int() },
|
|
878
|
+
{ name: 'total', schema: z.number() },
|
|
879
|
+
],
|
|
880
|
+
name: 'orders',
|
|
881
|
+
primaryKeys: [
|
|
882
|
+
'tenant_id',
|
|
883
|
+
'order_id',
|
|
884
|
+
],
|
|
885
|
+
})
|
|
886
|
+
|
|
887
|
+
// 2. Create child table with matching columns
|
|
888
|
+
const { table: itemsTable } = createTable({
|
|
889
|
+
columns: [
|
|
890
|
+
{ name: 'id', schema: z.int() },
|
|
891
|
+
{ name: 'tenant_id', schema: z.string() },
|
|
892
|
+
{ name: 'order_id', schema: z.int() },
|
|
893
|
+
{ name: 'product', schema: z.string() },
|
|
894
|
+
],
|
|
895
|
+
name: 'order_items',
|
|
896
|
+
primaryKeys: ['id'],
|
|
897
|
+
})
|
|
898
|
+
|
|
899
|
+
// 3. Execute table creation
|
|
900
|
+
db.exec(ordersTable)
|
|
901
|
+
db.exec(itemsTable)
|
|
902
|
+
|
|
903
|
+
// 4. Add composite foreign key constraint
|
|
904
|
+
db.exec(`
|
|
905
|
+
ALTER TABLE order_items
|
|
906
|
+
ADD CONSTRAINT fk_order
|
|
907
|
+
FOREIGN KEY (tenant_id, order_id)
|
|
908
|
+
REFERENCES orders(tenant_id, order_id)
|
|
909
|
+
ON DELETE CASCADE
|
|
910
910
|
`)
|
|
911
911
|
```
|
|
912
912
|
|
|
@@ -919,7 +919,7 @@ Only specific Zod validations generate CHECK constraints:
|
|
|
919
919
|
|
|
920
920
|
Custom refinements and complex validations work at the application level but don't generate SQL constraints:
|
|
921
921
|
|
|
922
|
-
```
|
|
922
|
+
```plaintext
|
|
923
923
|
{
|
|
924
924
|
name: 'email',
|
|
925
925
|
schema: z.string().refine(val => val.includes('@'), 'Must contain @')
|
|
@@ -935,7 +935,7 @@ Custom refinements and complex validations work at the application level but don
|
|
|
935
935
|
|
|
936
936
|
Arrays and objects are stored as TEXT with JSON serialization. You must handle serialization manually:
|
|
937
937
|
|
|
938
|
-
```
|
|
938
|
+
```plaintext
|
|
939
939
|
{ name: 'tags', schema: z.array(z.string()) }
|
|
940
940
|
// SQL: tags TEXT NOT NULL
|
|
941
941
|
|
|
@@ -949,7 +949,7 @@ const tags = JSON.parse(result.tags) // ['tech', 'news']
|
|
|
949
949
|
|
|
950
950
|
**Recommendation:** For better queryability, consider separate tables for array data:
|
|
951
951
|
```typescript
|
|
952
|
-
// Instead of storing tags as JSON array
|
|
952
|
+
// Instead of storing tags as JSON array
|
|
953
953
|
// Use a junction table: post_tags (post_id, tag_id)
|
|
954
954
|
```
|
|
955
955
|
|
|
@@ -957,7 +957,7 @@ const tags = JSON.parse(result.tags) // ['tech', 'news']
|
|
|
957
957
|
|
|
958
958
|
Dates are stored as TEXT in ISO 8601 format. SQLite doesn't have a native DATE type:
|
|
959
959
|
|
|
960
|
-
```
|
|
960
|
+
```plaintext
|
|
961
961
|
{ name: 'created_at', schema: z.date() }
|
|
962
962
|
// SQL: created_at DATE NOT NULL
|
|
963
963
|
// Stored as TEXT: '2026-01-07T12:30:00.000Z'
|
|
@@ -1002,12 +1002,12 @@ This tool is designed specifically for SQLite. Some features may not translate t
|
|
|
1002
1002
|
**Critical:** SQLite does not enforce foreign key constraints by default. You must enable them:
|
|
1003
1003
|
|
|
1004
1004
|
```typescript
|
|
1005
|
-
// Example using db0
|
|
1006
|
-
const db = createDatabase(sqlite({
|
|
1007
|
-
name: './db.sqlite',
|
|
1008
|
-
}))
|
|
1009
|
-
|
|
1010
|
-
// Enable for each database connection
|
|
1005
|
+
// Example using db0
|
|
1006
|
+
const db = createDatabase(sqlite({
|
|
1007
|
+
name: './db.sqlite',
|
|
1008
|
+
}))
|
|
1009
|
+
|
|
1010
|
+
// Enable for each database connection
|
|
1011
1011
|
db.exec('PRAGMA foreign_keys = ON')
|
|
1012
1012
|
```
|
|
1013
1013
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as zod from "zod/v4/core";
|
|
2
|
-
|
|
3
2
|
//#region src/types.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* SQLite native column types.
|
|
@@ -454,7 +453,7 @@ declare function createTable<const TColumns extends ReadonlyArray<ColumnConfig<a
|
|
|
454
453
|
indexes: string[];
|
|
455
454
|
schema: import("zod").ZodObject<{ [K in TColumns[number]["name"]]: Extract<TColumns[number], {
|
|
456
455
|
name: K;
|
|
457
|
-
}>["schema"] }, import("zod/v4/core").$strip>;
|
|
456
|
+
}>["schema"]; }, import("zod/v4/core").$strip>;
|
|
458
457
|
table: string;
|
|
459
458
|
};
|
|
460
459
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -48,9 +48,7 @@ function formatCheckConstraint(name, schema) {
|
|
|
48
48
|
case "greater_than":
|
|
49
49
|
constraints.push(`${name} >= ${checkValue}`);
|
|
50
50
|
break;
|
|
51
|
-
case "less_than":
|
|
52
|
-
constraints.push(`${name} <= ${checkValue}`);
|
|
53
|
-
break;
|
|
51
|
+
case "less_than": constraints.push(`${name} <= ${checkValue}`);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
return constraints.length > 0 ? `CHECK(${constraints.join(" AND ")})` : void 0;
|
|
@@ -69,9 +67,7 @@ function formatCheckConstraint(name, schema) {
|
|
|
69
67
|
case "max_length":
|
|
70
68
|
constraints.push(`length(${name}) <= ${checkDefinition.maximum}`);
|
|
71
69
|
break;
|
|
72
|
-
case "min_length":
|
|
73
|
-
constraints.push(`length(${name}) >= ${checkDefinition.minimum}`);
|
|
74
|
-
break;
|
|
70
|
+
case "min_length": constraints.push(`length(${name}) >= ${checkDefinition.minimum}`);
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
73
|
return constraints.length > 0 ? `CHECK(${constraints.join(" AND ")})` : void 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-sqlite",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Generate type-safe SQLite table schemas from Zod validation schemas.",
|
|
7
7
|
"author": "Favour Emeka <favorodera@gmail.com>",
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
"zod": "^4.4.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@favorodera/eslint-config": "^1.0.
|
|
46
|
-
"@types/node": "^
|
|
47
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
48
|
-
"@vitest/ui": "4.1.
|
|
45
|
+
"@favorodera/eslint-config": "^1.0.4",
|
|
46
|
+
"@types/node": "^26.2.0",
|
|
47
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
48
|
+
"@vitest/ui": "4.1.10",
|
|
49
49
|
"db0": "^0.3.4",
|
|
50
|
-
"eslint": "^10.
|
|
50
|
+
"eslint": "^10.8.1",
|
|
51
51
|
"relizy": "^1.4.9",
|
|
52
|
-
"tsdown": "^0.22.
|
|
52
|
+
"tsdown": "^0.22.14",
|
|
53
53
|
"typescript": "^6.0.3",
|
|
54
|
-
"vitest": "^4.1.
|
|
54
|
+
"vitest": "^4.1.10"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsdown",
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"test:watch": "vitest --watch --ui",
|
|
61
61
|
"typecheck": "tsc --noEmit",
|
|
62
62
|
"lint": "eslint . --fix",
|
|
63
|
-
"ready": "pnpm install && pnpm
|
|
63
|
+
"ready": "pnpm install && pnpm lint && pnpm typecheck && pnpm test && pnpm build"
|
|
64
64
|
}
|
|
65
65
|
}
|