zod-sqlite 1.0.4 → 1.0.5
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 +27 -24
- package/dist/index.d.mts +205 -210
- package/dist/index.mjs +4 -283
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>Zod-SQLite</h1>
|
|
3
|
-
<p
|
|
3
|
+
<p>Generate type-safe SQLite table schemas from Zod validation schemas. Define your database structure once using Zod, and automatically generate both SQL CREATE TABLE statements and runtime validation schemas with full TypeScript type inference.</p>
|
|
4
4
|
<p>
|
|
5
|
-
<a href="https://github.com/favorodera/zod-sqlite/blob/main/LICENSE"><img src="https://img.shields.io/github/license/favorodera/zod-sqlite.svg?style=plastic&label=License&color=blue" alt="License"></a>
|
|
6
5
|
<a href="https://github.com/favorodera/zod-sqlite/stargazers"><img src="https://img.shields.io/github/stars/favorodera/zod-sqlite.svg?style=plastic&label=Stars&color=blue" alt="GitHub Stars"></a>
|
|
7
|
-
<a href="https://npmx.dev/package/zod-sqlite"><img src="https://img.shields.io/npm/
|
|
6
|
+
<a href="https://npmx.dev/package/zod-sqlite"><img src="https://img.shields.io/npm/dm/zod-sqlite.svg?style=plastic&label=NPM%20Downloads&color=blue" alt="NPM Downloads"></a>
|
|
8
7
|
<a href="https://npmx.dev/package/zod-sqlite"><img src="https://img.shields.io/npm/v/zod-sqlite.svg?style=plastic&label=Version&color=blue" alt="NPM Version"></a>
|
|
9
8
|
</p>
|
|
10
9
|
</div>
|
|
@@ -76,13 +75,15 @@ const users = createTable({
|
|
|
76
75
|
|
|
77
76
|
// Use the generated SQL
|
|
78
77
|
console.log(users.table)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
/*
|
|
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
|
+
*/
|
|
86
87
|
|
|
87
88
|
console.log(users.indexes[0])
|
|
88
89
|
// CREATE UNIQUE INDEX idx_users_email ON users (email);
|
|
@@ -170,10 +171,10 @@ Configuration object for table creation.
|
|
|
170
171
|
|
|
171
172
|
```typescript
|
|
172
173
|
interface TableConfig {
|
|
173
|
-
columns:
|
|
174
|
-
indexes?:
|
|
174
|
+
columns: ColumnConfig[] // Array of column definitions
|
|
175
|
+
indexes?: IndexConfig[] // Optional index configurations
|
|
175
176
|
name: string // Table name
|
|
176
|
-
primaryKeys:
|
|
177
|
+
primaryKeys: string[] // Column names forming primary key
|
|
177
178
|
}
|
|
178
179
|
```
|
|
179
180
|
|
|
@@ -195,19 +196,19 @@ interface ColumnConfig {
|
|
|
195
196
|
Foreign key constraint configuration.
|
|
196
197
|
|
|
197
198
|
```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
199
|
type ForeignKeyAction
|
|
206
200
|
= | 'CASCADE'
|
|
207
201
|
| 'NO ACTION'
|
|
208
202
|
| 'RESTRICT'
|
|
209
203
|
| 'SET DEFAULT'
|
|
210
|
-
| 'SET NULL'
|
|
204
|
+
| 'SET NULL'
|
|
205
|
+
|
|
206
|
+
interface ForeignKeyReference {
|
|
207
|
+
column: string // Referenced column name
|
|
208
|
+
onDelete?: ForeignKeyAction // Action on parent deletion
|
|
209
|
+
onUpdate?: ForeignKeyAction // Action on parent update
|
|
210
|
+
table: string // Referenced table name
|
|
211
|
+
}
|
|
211
212
|
```
|
|
212
213
|
|
|
213
214
|
### IndexConfig
|
|
@@ -216,7 +217,7 @@ Index configuration for query optimization.
|
|
|
216
217
|
|
|
217
218
|
```typescript
|
|
218
219
|
interface IndexConfig {
|
|
219
|
-
columns:
|
|
220
|
+
columns: string[] // Indexed column names
|
|
220
221
|
name: string // Index name
|
|
221
222
|
unique?: boolean // Whether this is a unique index
|
|
222
223
|
where?: string // Optional WHERE clause for partial index
|
|
@@ -949,8 +950,10 @@ const tags = JSON.parse(result.tags) // ['tech', 'news']
|
|
|
949
950
|
|
|
950
951
|
**Recommendation:** For better queryability, consider separate tables for array data:
|
|
951
952
|
```typescript
|
|
952
|
-
|
|
953
|
-
|
|
953
|
+
/*
|
|
954
|
+
* Instead of storing tags as JSON array
|
|
955
|
+
* Use a junction table: post_tags (post_id, tag_id)
|
|
956
|
+
*/
|
|
954
957
|
```
|
|
955
958
|
|
|
956
959
|
### Date Handling
|
package/dist/index.d.mts
CHANGED
|
@@ -1,22 +1,134 @@
|
|
|
1
1
|
import * as zod from "zod/v4/core";
|
|
2
|
-
//#region src/types.d.ts
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
3
|
+
* Database column configuration.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* type conversions as needed during operations.
|
|
9
|
-
* @see https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
|
10
|
-
*/
|
|
11
|
-
type SQLiteType = 'BLOB' | 'INTEGER' | 'NULL' | 'REAL' | 'TEXT';
|
|
12
|
-
/**
|
|
13
|
-
* SQLite supported column types.
|
|
5
|
+
* Defines a single column in a table, including its name, type (via Zod schema),
|
|
6
|
+
* validation rules, and optional constraints like uniqueness and foreign keys.
|
|
14
7
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
8
|
+
* **Column definition workflow:**
|
|
9
|
+
* 1. Zod schema is analyzed to determine SQLite type (TEXT, INTEGER, etc.)
|
|
10
|
+
* 2. Zod schema provides TypeScript type safety and runtime validation
|
|
11
|
+
* 3. Constraints (unique, foreign key) are applied at the database level
|
|
12
|
+
* @template TName - Literal string type for the column name
|
|
13
|
+
* @template TSchema - Zod type for the column schema
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // Simple text column
|
|
17
|
+
* {
|
|
18
|
+
* name: 'email',
|
|
19
|
+
* schema: z.email()
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* // Unique column with validation
|
|
23
|
+
* {
|
|
24
|
+
* name: 'username',
|
|
25
|
+
* schema: z.string().min(3).max(20),
|
|
26
|
+
* unique: true
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* // Foreign key column
|
|
30
|
+
* {
|
|
31
|
+
* name: 'author_id',
|
|
32
|
+
* schema: z.int(),
|
|
33
|
+
* references: {
|
|
34
|
+
* table: 'users',
|
|
35
|
+
* column: 'id',
|
|
36
|
+
* onDelete: 'CASCADE'
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* // Optional column with default
|
|
41
|
+
* {
|
|
42
|
+
* name: 'status',
|
|
43
|
+
* schema: z.enum(['draft', 'published']).default('draft')
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
18
46
|
*/
|
|
19
|
-
|
|
47
|
+
export interface ColumnConfig<TName extends string = string, TSchema extends zod.$ZodType = zod.$ZodType> {
|
|
48
|
+
/**
|
|
49
|
+
* Name of the column.
|
|
50
|
+
*
|
|
51
|
+
* Used in CREATE TABLE statement and all SQL queries.
|
|
52
|
+
* Should use snake_case by convention.
|
|
53
|
+
* @example 'user_id'
|
|
54
|
+
* @example 'created_at'
|
|
55
|
+
*/
|
|
56
|
+
name: TName;
|
|
57
|
+
/**
|
|
58
|
+
* Foreign key reference configuration.
|
|
59
|
+
*
|
|
60
|
+
* Creates a constraint linking this column to a column in another table.
|
|
61
|
+
* Ensures referential integrity by preventing orphaned records.
|
|
62
|
+
*
|
|
63
|
+
* **Requirements:**
|
|
64
|
+
* - Referenced column should typically be a PRIMARY KEY or UNIQUE
|
|
65
|
+
* - Types must be compatible between the two columns
|
|
66
|
+
* - Foreign keys must be enabled: `PRAGMA foreign_keys = ON`
|
|
67
|
+
*
|
|
68
|
+
* **Composite foreign keys** (multiple columns referencing multiple columns)
|
|
69
|
+
* cannot be defined here - they must be defined at the table level as a
|
|
70
|
+
* separate constraint.
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* {
|
|
74
|
+
* name: 'user_id',
|
|
75
|
+
* schema: z.int(),
|
|
76
|
+
* references: {
|
|
77
|
+
* table: 'users',
|
|
78
|
+
* column: 'id',
|
|
79
|
+
* onDelete: 'CASCADE'
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
references?: ForeignKeyReference;
|
|
85
|
+
/**
|
|
86
|
+
* Zod schema defining the column's type, validation, and constraints.
|
|
87
|
+
*
|
|
88
|
+
* The Zod schema serves dual purposes:
|
|
89
|
+
* 1. **Database level**: Mapped to SQLite type (TEXT, INTEGER, REAL, BLOB, NULL)
|
|
90
|
+
* 2. **Application level**: Provides TypeScript types and runtime validation
|
|
91
|
+
*
|
|
92
|
+
* **Zod to SQLite type mapping:**
|
|
93
|
+
* - `z.string()`, `z.enum()`, `z.literal()`, `z.date()` → TEXT
|
|
94
|
+
* - `z.number()` → REAL (or INTEGER if `.int()` is used)
|
|
95
|
+
* - `z.bigint()`, `z.boolean()` → INTEGER
|
|
96
|
+
* - `z.custom()` for file/blob → BLOB
|
|
97
|
+
* - `z.null()`, `z.undefined()` → NULL
|
|
98
|
+
*
|
|
99
|
+
* **Zod features mapped to SQL:**
|
|
100
|
+
* - `.optional()`, `.nullable()` → column allows NULL
|
|
101
|
+
* - `.default(value)` → DEFAULT clause in SQL
|
|
102
|
+
* - `.min()`, `.max()`, `.email()`, etc. → runtime validation only
|
|
103
|
+
* @example z.string() - TEXT column
|
|
104
|
+
* @example z.int() - INTEGER column
|
|
105
|
+
* @example z.string().nullable() - TEXT column that allows NULL
|
|
106
|
+
* @example z.boolean().default(false) - INTEGER with DEFAULT 0
|
|
107
|
+
*/
|
|
108
|
+
schema: TSchema;
|
|
109
|
+
/**
|
|
110
|
+
* Whether column values must be unique across all rows.
|
|
111
|
+
*
|
|
112
|
+
* When true, adds a UNIQUE constraint to the column. Any attempt to
|
|
113
|
+
* INSERT or UPDATE a duplicate value will fail.
|
|
114
|
+
*
|
|
115
|
+
* **Note**: This is different from PRIMARY KEY:
|
|
116
|
+
* - UNIQUE columns can be NULL (unless also NOT NULL)
|
|
117
|
+
* - A table can have multiple UNIQUE columns
|
|
118
|
+
* - PRIMARY KEY implies UNIQUE + NOT NULL
|
|
119
|
+
* @default false
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* // Unique email address
|
|
123
|
+
* {
|
|
124
|
+
* name: 'email',
|
|
125
|
+
* schema: z.email(),
|
|
126
|
+
* unique: true
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
unique?: boolean;
|
|
131
|
+
}
|
|
20
132
|
/**
|
|
21
133
|
* Foreign key constraint actions for ON DELETE and ON UPDATE clauses.
|
|
22
134
|
*
|
|
@@ -50,7 +162,7 @@ type SQLiteSupportType = 'BIGINT' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'FLOAT' |
|
|
|
50
162
|
* }
|
|
51
163
|
* ```
|
|
52
164
|
*/
|
|
53
|
-
type ForeignKeyAction = 'CASCADE' | 'NO ACTION' | 'RESTRICT' | 'SET DEFAULT' | 'SET NULL';
|
|
165
|
+
export type ForeignKeyAction = 'CASCADE' | 'NO ACTION' | 'RESTRICT' | 'SET DEFAULT' | 'SET NULL';
|
|
54
166
|
/**
|
|
55
167
|
* Foreign key reference configuration.
|
|
56
168
|
*
|
|
@@ -80,13 +192,7 @@ type ForeignKeyAction = 'CASCADE' | 'NO ACTION' | 'RESTRICT' | 'SET DEFAULT' | '
|
|
|
80
192
|
* }
|
|
81
193
|
* ```
|
|
82
194
|
*/
|
|
83
|
-
interface ForeignKeyReference {
|
|
84
|
-
/**
|
|
85
|
-
* Name of the referenced table.
|
|
86
|
-
*
|
|
87
|
-
* This is the parent table that contains the column being referenced.
|
|
88
|
-
*/
|
|
89
|
-
table: string;
|
|
195
|
+
export interface ForeignKeyReference {
|
|
90
196
|
/**
|
|
91
197
|
* Name of the referenced column in the parent table.
|
|
92
198
|
*
|
|
@@ -109,6 +215,12 @@ interface ForeignKeyReference {
|
|
|
109
215
|
* @default 'NO ACTION'
|
|
110
216
|
*/
|
|
111
217
|
onUpdate?: ForeignKeyAction;
|
|
218
|
+
/**
|
|
219
|
+
* Name of the referenced table.
|
|
220
|
+
*
|
|
221
|
+
* This is the parent table that contains the column being referenced.
|
|
222
|
+
*/
|
|
223
|
+
table: string;
|
|
112
224
|
}
|
|
113
225
|
/**
|
|
114
226
|
* Database index configuration.
|
|
@@ -152,7 +264,16 @@ interface ForeignKeyReference {
|
|
|
152
264
|
* }
|
|
153
265
|
* ```
|
|
154
266
|
*/
|
|
155
|
-
interface IndexConfig<TColumns extends
|
|
267
|
+
export interface IndexConfig<TColumns extends readonly ColumnConfig<string, zod.$ZodType>[] = readonly ColumnConfig<string, zod.$ZodType>[]> {
|
|
268
|
+
/**
|
|
269
|
+
* Column names to include in the index.
|
|
270
|
+
*
|
|
271
|
+
* For composite indexes, the order matters: queries that filter on the
|
|
272
|
+
* first columns benefit most from the index.
|
|
273
|
+
* @example ['email'] - single column
|
|
274
|
+
* @example ['last_name', 'first_name'] - composite index
|
|
275
|
+
*/
|
|
276
|
+
columns: readonly TColumns[number]['name'][];
|
|
156
277
|
/**
|
|
157
278
|
* Unique name for the index.
|
|
158
279
|
*
|
|
@@ -162,15 +283,6 @@ interface IndexConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$Z
|
|
|
162
283
|
* @example 'idx_posts_author_created'
|
|
163
284
|
*/
|
|
164
285
|
name: string;
|
|
165
|
-
/**
|
|
166
|
-
* Column names to include in the index.
|
|
167
|
-
*
|
|
168
|
-
* For composite indexes, the order matters: queries that filter on the
|
|
169
|
-
* first columns benefit most from the index.
|
|
170
|
-
* @example ['email'] - single column
|
|
171
|
-
* @example ['last_name', 'first_name'] - composite index
|
|
172
|
-
*/
|
|
173
|
-
columns: ReadonlyArray<TColumns[number]['name']>;
|
|
174
286
|
/**
|
|
175
287
|
* Whether this is a unique index.
|
|
176
288
|
*
|
|
@@ -195,155 +307,27 @@ interface IndexConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$Z
|
|
|
195
307
|
where?: string;
|
|
196
308
|
}
|
|
197
309
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* Defines a single column in a table, including its name, type (via Zod schema),
|
|
201
|
-
* validation rules, and optional constraints like uniqueness and foreign keys.
|
|
202
|
-
*
|
|
203
|
-
* **Column definition workflow:**
|
|
204
|
-
* 1. Zod schema is analyzed to determine SQLite type (TEXT, INTEGER, etc.)
|
|
205
|
-
* 2. Zod schema provides TypeScript type safety and runtime validation
|
|
206
|
-
* 3. Constraints (unique, foreign key) are applied at the database level
|
|
207
|
-
* @template TName - Literal string type for the column name
|
|
208
|
-
* @template TSchema - Zod type for the column schema
|
|
209
|
-
* @example
|
|
210
|
-
* ```ts
|
|
211
|
-
* // Simple text column
|
|
212
|
-
* {
|
|
213
|
-
* name: 'email',
|
|
214
|
-
* schema: z.email()
|
|
215
|
-
* }
|
|
216
|
-
*
|
|
217
|
-
* // Unique column with validation
|
|
218
|
-
* {
|
|
219
|
-
* name: 'username',
|
|
220
|
-
* schema: z.string().min(3).max(20),
|
|
221
|
-
* unique: true
|
|
222
|
-
* }
|
|
310
|
+
* SQLite supported column types.
|
|
223
311
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
231
|
-
* onDelete: 'CASCADE'
|
|
232
|
-
* }
|
|
233
|
-
* }
|
|
312
|
+
* These types are supported by SQLite but not as native storage classes.
|
|
313
|
+
* They are often used as aliases for other types or for specific purposes.
|
|
314
|
+
* @see https://www.sqlite.org/datatype3.html#type_affinity
|
|
315
|
+
*/
|
|
316
|
+
export type SQLiteSupportType = 'BIGINT' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'FLOAT' | 'NUMERIC';
|
|
317
|
+
/**
|
|
318
|
+
* SQLite native column types.
|
|
234
319
|
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* }
|
|
240
|
-
* ```
|
|
320
|
+
* SQLite uses a dynamic type system with these five storage classes.
|
|
321
|
+
* Column values are stored in one of these formats, and SQLite performs
|
|
322
|
+
* type conversions as needed during operations.
|
|
323
|
+
* @see https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
|
|
241
324
|
*/
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Name of the column.
|
|
245
|
-
*
|
|
246
|
-
* Used in CREATE TABLE statement and all SQL queries.
|
|
247
|
-
* Should use snake_case by convention.
|
|
248
|
-
* @example 'user_id'
|
|
249
|
-
* @example 'created_at'
|
|
250
|
-
*/
|
|
251
|
-
name: TName;
|
|
252
|
-
/**
|
|
253
|
-
* Zod schema defining the column's type, validation, and constraints.
|
|
254
|
-
*
|
|
255
|
-
* The Zod schema serves dual purposes:
|
|
256
|
-
* 1. **Database level**: Mapped to SQLite type (TEXT, INTEGER, REAL, BLOB, NULL)
|
|
257
|
-
* 2. **Application level**: Provides TypeScript types and runtime validation
|
|
258
|
-
*
|
|
259
|
-
* **Zod to SQLite type mapping:**
|
|
260
|
-
* - `z.string()`, `z.enum()`, `z.literal()`, `z.date()` → TEXT
|
|
261
|
-
* - `z.number()` → REAL (or INTEGER if `.int()` is used)
|
|
262
|
-
* - `z.bigint()`, `z.boolean()` → INTEGER
|
|
263
|
-
* - `z.custom()` for file/blob → BLOB
|
|
264
|
-
* - `z.null()`, `z.undefined()` → NULL
|
|
265
|
-
*
|
|
266
|
-
* **Zod features mapped to SQL:**
|
|
267
|
-
* - `.optional()`, `.nullable()` → column allows NULL
|
|
268
|
-
* - `.default(value)` → DEFAULT clause in SQL
|
|
269
|
-
* - `.min()`, `.max()`, `.email()`, etc. → runtime validation only
|
|
270
|
-
* @example z.string() - TEXT column
|
|
271
|
-
* @example z.int() - INTEGER column
|
|
272
|
-
* @example z.string().nullable() - TEXT column that allows NULL
|
|
273
|
-
* @example z.boolean().default(false) - INTEGER with DEFAULT 0
|
|
274
|
-
*/
|
|
275
|
-
schema: TSchema;
|
|
276
|
-
/**
|
|
277
|
-
* Whether column values must be unique across all rows.
|
|
278
|
-
*
|
|
279
|
-
* When true, adds a UNIQUE constraint to the column. Any attempt to
|
|
280
|
-
* INSERT or UPDATE a duplicate value will fail.
|
|
281
|
-
*
|
|
282
|
-
* **Note**: This is different from PRIMARY KEY:
|
|
283
|
-
* - UNIQUE columns can be NULL (unless also NOT NULL)
|
|
284
|
-
* - A table can have multiple UNIQUE columns
|
|
285
|
-
* - PRIMARY KEY implies UNIQUE + NOT NULL
|
|
286
|
-
* @default false
|
|
287
|
-
* @example
|
|
288
|
-
* ```ts
|
|
289
|
-
* // Unique email address
|
|
290
|
-
* {
|
|
291
|
-
* name: 'email',
|
|
292
|
-
* schema: z.email(),
|
|
293
|
-
* unique: true
|
|
294
|
-
* }
|
|
295
|
-
* ```
|
|
296
|
-
*/
|
|
297
|
-
unique?: boolean;
|
|
298
|
-
/**
|
|
299
|
-
* Foreign key reference configuration.
|
|
300
|
-
*
|
|
301
|
-
* Creates a constraint linking this column to a column in another table.
|
|
302
|
-
* Ensures referential integrity by preventing orphaned records.
|
|
303
|
-
*
|
|
304
|
-
* **Requirements:**
|
|
305
|
-
* - Referenced column should typically be a PRIMARY KEY or UNIQUE
|
|
306
|
-
* - Types must be compatible between the two columns
|
|
307
|
-
* - Foreign keys must be enabled: `PRAGMA foreign_keys = ON`
|
|
308
|
-
*
|
|
309
|
-
* **Composite foreign keys** (multiple columns referencing multiple columns)
|
|
310
|
-
* cannot be defined here - they must be defined at the table level as a
|
|
311
|
-
* separate constraint.
|
|
312
|
-
* @example
|
|
313
|
-
* ```ts
|
|
314
|
-
* {
|
|
315
|
-
* name: 'user_id',
|
|
316
|
-
* schema: z.int(),
|
|
317
|
-
* references: {
|
|
318
|
-
* table: 'users',
|
|
319
|
-
* column: 'id',
|
|
320
|
-
* onDelete: 'CASCADE'
|
|
321
|
-
* }
|
|
322
|
-
* }
|
|
323
|
-
* ```
|
|
324
|
-
*/
|
|
325
|
-
references?: ForeignKeyReference;
|
|
326
|
-
}
|
|
325
|
+
export type SQLiteType = 'BLOB' | 'INTEGER' | 'NULL' | 'REAL' | 'TEXT';
|
|
327
326
|
/**
|
|
328
327
|
* Complete database table configuration.
|
|
329
328
|
* @template TColumns - An array of column configuration objects.
|
|
330
329
|
*/
|
|
331
|
-
interface TableConfig<TColumns extends
|
|
332
|
-
/**
|
|
333
|
-
* Name of the table.
|
|
334
|
-
*
|
|
335
|
-
* Used in CREATE TABLE statement and all SQL queries.
|
|
336
|
-
* Should use snake_case by convention (e.g., 'user_profiles', 'order_items').
|
|
337
|
-
*
|
|
338
|
-
* **Naming conventions:**
|
|
339
|
-
* - Use plural nouns for tables representing collections (e.g., 'users', 'posts')
|
|
340
|
-
* - Use singular nouns for junction tables (e.g., 'user_role', 'post_tag')
|
|
341
|
-
* - Avoid SQL reserved keywords (e.g., 'order' → 'orders' or 'user_order')
|
|
342
|
-
* @example 'users'
|
|
343
|
-
* @example 'blog_posts'
|
|
344
|
-
* @example 'user_sessions'
|
|
345
|
-
*/
|
|
346
|
-
name: string;
|
|
330
|
+
export interface TableConfig<TColumns extends readonly ColumnConfig<string, zod.$ZodType>[] = readonly ColumnConfig<string, zod.$ZodType>[]> {
|
|
347
331
|
/**
|
|
348
332
|
* Array of column configurations defining the table structure.
|
|
349
333
|
*
|
|
@@ -366,35 +350,6 @@ interface TableConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$Z
|
|
|
366
350
|
* ```
|
|
367
351
|
*/
|
|
368
352
|
columns: TColumns;
|
|
369
|
-
/**
|
|
370
|
-
* Column name(s) that form the primary key.
|
|
371
|
-
*
|
|
372
|
-
* The primary key uniquely identifies each row in the table and cannot
|
|
373
|
-
* contain NULL values. Always provided as an array, even for single-column
|
|
374
|
-
* primary keys.
|
|
375
|
-
*
|
|
376
|
-
* **Single column primary key:**
|
|
377
|
-
* - Use an array with one element (e.g., `['id']`)
|
|
378
|
-
* - Typically an auto-incrementing integer
|
|
379
|
-
* - Most common pattern for standard entity tables
|
|
380
|
-
*
|
|
381
|
-
* **Composite primary key:**
|
|
382
|
-
* - Use an array with multiple elements (e.g., `['user_id', 'post_id']`)
|
|
383
|
-
* - Common in junction tables for many-to-many relationships
|
|
384
|
-
* - Order matters: most specific/selective column should come first
|
|
385
|
-
* - The combination of all columns must be unique
|
|
386
|
-
*
|
|
387
|
-
* **Notes:**
|
|
388
|
-
* - Every table should have a primary key
|
|
389
|
-
* - Primary key columns are automatically indexed by SQLite
|
|
390
|
-
* - Primary key implies UNIQUE + NOT NULL constraints on all columns
|
|
391
|
-
* - Cannot be changed after table creation (requires table rebuild)
|
|
392
|
-
* @example ['id'] - single column primary key
|
|
393
|
-
* @example ['user_id', 'role_id'] - composite primary key for junction table
|
|
394
|
-
* @example ['tenant_id', 'record_id'] - composite key for multi-tenant data
|
|
395
|
-
* @example ['country_code', 'postal_code'] - composite key for location data
|
|
396
|
-
*/
|
|
397
|
-
primaryKeys: ReadonlyArray<TColumns[number]['name']>;
|
|
398
353
|
/**
|
|
399
354
|
* Optional array of index configurations for query optimization.
|
|
400
355
|
*
|
|
@@ -433,10 +388,52 @@ interface TableConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$Z
|
|
|
433
388
|
* ]
|
|
434
389
|
* ```
|
|
435
390
|
*/
|
|
436
|
-
indexes?:
|
|
391
|
+
indexes?: IndexConfig<TColumns>[];
|
|
392
|
+
/**
|
|
393
|
+
* Name of the table.
|
|
394
|
+
*
|
|
395
|
+
* Used in CREATE TABLE statement and all SQL queries.
|
|
396
|
+
* Should use snake_case by convention (e.g., 'user_profiles', 'order_items').
|
|
397
|
+
*
|
|
398
|
+
* **Naming conventions:**
|
|
399
|
+
* - Use plural nouns for tables representing collections (e.g., 'users', 'posts')
|
|
400
|
+
* - Use singular nouns for junction tables (e.g., 'user_role', 'post_tag')
|
|
401
|
+
* - Avoid SQL reserved keywords (e.g., 'order' → 'orders' or 'user_order')
|
|
402
|
+
* @example 'users'
|
|
403
|
+
* @example 'blog_posts'
|
|
404
|
+
* @example 'user_sessions'
|
|
405
|
+
*/
|
|
406
|
+
name: string;
|
|
407
|
+
/**
|
|
408
|
+
* Column name(s) that form the primary key.
|
|
409
|
+
*
|
|
410
|
+
* The primary key uniquely identifies each row in the table and cannot
|
|
411
|
+
* contain NULL values. Always provided as an array, even for single-column
|
|
412
|
+
* primary keys.
|
|
413
|
+
*
|
|
414
|
+
* **Single column primary key:**
|
|
415
|
+
* - Use an array with one element (e.g., `['id']`)
|
|
416
|
+
* - Typically an auto-incrementing integer
|
|
417
|
+
* - Most common pattern for standard entity tables
|
|
418
|
+
*
|
|
419
|
+
* **Composite primary key:**
|
|
420
|
+
* - Use an array with multiple elements (e.g., `['user_id', 'post_id']`)
|
|
421
|
+
* - Common in junction tables for many-to-many relationships
|
|
422
|
+
* - Order matters: most specific/selective column should come first
|
|
423
|
+
* - The combination of all columns must be unique
|
|
424
|
+
*
|
|
425
|
+
* **Notes:**
|
|
426
|
+
* - Every table should have a primary key
|
|
427
|
+
* - Primary key columns are automatically indexed by SQLite
|
|
428
|
+
* - Primary key implies UNIQUE + NOT NULL constraints on all columns
|
|
429
|
+
* - Cannot be changed after table creation (requires table rebuild)
|
|
430
|
+
* @example ['id'] - single column primary key
|
|
431
|
+
* @example ['user_id', 'role_id'] - composite primary key for junction table
|
|
432
|
+
* @example ['tenant_id', 'record_id'] - composite key for multi-tenant data
|
|
433
|
+
* @example ['country_code', 'postal_code'] - composite key for location data
|
|
434
|
+
*/
|
|
435
|
+
primaryKeys: readonly TColumns[number]['name'][];
|
|
437
436
|
}
|
|
438
|
-
//#endregion
|
|
439
|
-
//#region src/table.d.ts
|
|
440
437
|
/**
|
|
441
438
|
* Creates a table definition and Zod schema.
|
|
442
439
|
*
|
|
@@ -449,12 +446,10 @@ interface TableConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$Z
|
|
|
449
446
|
* @param config Table configuration object
|
|
450
447
|
* @returns Object containing SQL statements and Zod schema
|
|
451
448
|
*/
|
|
452
|
-
declare function createTable<const TColumns extends
|
|
449
|
+
export declare function createTable<const TColumns extends readonly ColumnConfig<any, any>[]>(config: TableConfig<TColumns>): {
|
|
453
450
|
indexes: string[];
|
|
454
451
|
schema: import("zod").ZodObject<{ [K in TColumns[number]["name"]]: Extract<TColumns[number], {
|
|
455
452
|
name: K;
|
|
456
453
|
}>["schema"]; }, import("zod/v4/core").$strip>;
|
|
457
454
|
table: string;
|
|
458
|
-
};
|
|
459
|
-
//#endregion
|
|
460
|
-
export { ColumnConfig, ForeignKeyAction, ForeignKeyReference, IndexConfig, SQLiteSupportType, SQLiteType, TableConfig, createTable };
|
|
455
|
+
};
|
package/dist/index.mjs
CHANGED
|
@@ -1,283 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* Supports:
|
|
7
|
-
* - `z.enum()`: CHECK(col IN ('a', 'b'))
|
|
8
|
-
* - `z.literal()`: CHECK(col = 'val')
|
|
9
|
-
* - `z.number().min/max()`: CHECK(col >= min AND col <= max)
|
|
10
|
-
* - `z.string().min/max()`: CHECK(length(col) >= min)
|
|
11
|
-
* @param name Column name
|
|
12
|
-
* @param schema Zod schema
|
|
13
|
-
* @returns SQL CHECK constraint string or null
|
|
14
|
-
*/
|
|
15
|
-
function formatCheckConstraint(name, schema) {
|
|
16
|
-
let currentSchema = schema;
|
|
17
|
-
while (true) {
|
|
18
|
-
const def = currentSchema._zod.def;
|
|
19
|
-
if (def.type === "nullable" || def.type === "optional" || def.type === "default") currentSchema = def.innerType;
|
|
20
|
-
else break;
|
|
21
|
-
}
|
|
22
|
-
const definition = currentSchema._zod.def;
|
|
23
|
-
switch (definition.type) {
|
|
24
|
-
case "enum": {
|
|
25
|
-
const entries = definition.entries;
|
|
26
|
-
return `CHECK(${name} IN (${Object.values(entries).map((value) => `'${String(value).replaceAll("'", "''")}'`).join(", ")}))`;
|
|
27
|
-
}
|
|
28
|
-
case "literal": {
|
|
29
|
-
const values = definition.values;
|
|
30
|
-
if (values.length === 1) {
|
|
31
|
-
const value = values[0];
|
|
32
|
-
if (typeof value === "string") return `CHECK(${name} = '${value.replaceAll("'", "''")}')`;
|
|
33
|
-
return `CHECK(${name} = ${value})`;
|
|
34
|
-
}
|
|
35
|
-
return `CHECK(${name} IN (${values.map((value) => {
|
|
36
|
-
if (typeof value === "string") return `'${value.replaceAll("'", "''")}'`;
|
|
37
|
-
return String(value);
|
|
38
|
-
}).join(", ")}))`;
|
|
39
|
-
}
|
|
40
|
-
case "number": {
|
|
41
|
-
const checks = definition.checks;
|
|
42
|
-
const constraints = [];
|
|
43
|
-
if (!checks) return;
|
|
44
|
-
for (const check of checks) {
|
|
45
|
-
const checkParameters = check._zod.def.check;
|
|
46
|
-
const checkValue = check._zod.def.value;
|
|
47
|
-
switch (checkParameters) {
|
|
48
|
-
case "greater_than":
|
|
49
|
-
constraints.push(`${name} >= ${checkValue}`);
|
|
50
|
-
break;
|
|
51
|
-
case "less_than": constraints.push(`${name} <= ${checkValue}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return constraints.length > 0 ? `CHECK(${constraints.join(" AND ")})` : void 0;
|
|
55
|
-
}
|
|
56
|
-
case "string": {
|
|
57
|
-
const checks = definition.checks;
|
|
58
|
-
const constraints = [];
|
|
59
|
-
if (!checks) return;
|
|
60
|
-
for (const check of checks) {
|
|
61
|
-
const checkParameters = check._zod.def.check;
|
|
62
|
-
const checkDefinition = check._zod.def;
|
|
63
|
-
switch (checkParameters) {
|
|
64
|
-
case "length_equals":
|
|
65
|
-
constraints.push(`length(${name}) = ${checkDefinition.length}`);
|
|
66
|
-
break;
|
|
67
|
-
case "max_length":
|
|
68
|
-
constraints.push(`length(${name}) <= ${checkDefinition.maximum}`);
|
|
69
|
-
break;
|
|
70
|
-
case "min_length": constraints.push(`length(${name}) >= ${checkDefinition.minimum}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return constraints.length > 0 ? `CHECK(${constraints.join(" AND ")})` : void 0;
|
|
74
|
-
}
|
|
75
|
-
case "union": {
|
|
76
|
-
const options = definition.options;
|
|
77
|
-
if (!options.every((option) => option._zod.def.type === "literal")) return;
|
|
78
|
-
const values = [];
|
|
79
|
-
for (const option of options) values.push(...option._zod.def.values);
|
|
80
|
-
return `CHECK(${name} IN (${values.map((value) => {
|
|
81
|
-
if (typeof value === "string") return `'${value.replaceAll("'", "''")}'`;
|
|
82
|
-
return String(value);
|
|
83
|
-
}).join(", ")}))`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/utils/format-default-value.ts
|
|
89
|
-
/**
|
|
90
|
-
* Formats a default value for SQL.
|
|
91
|
-
*
|
|
92
|
-
* Handles string quoting and type conversions for DEFAULT clauses.
|
|
93
|
-
* @param value The default value
|
|
94
|
-
* @param SQLiteType Target column type
|
|
95
|
-
* @returns Formatted SQL default string (e.g., "DEFAULT 'val'", "DEFAULT 42")
|
|
96
|
-
*/
|
|
97
|
-
function formatDefaultValue(value, SQLiteType) {
|
|
98
|
-
switch (SQLiteType) {
|
|
99
|
-
case "TEXT": return `DEFAULT '${String(value).replaceAll("'", "''")}'`;
|
|
100
|
-
case "INTEGER":
|
|
101
|
-
case "REAL": return `DEFAULT ${Number(value)}`;
|
|
102
|
-
case "BOOLEAN": return `DEFAULT ${value ? 1 : 0}`;
|
|
103
|
-
case "NULL": return "DEFAULT NULL";
|
|
104
|
-
default: return `DEFAULT '${value}'`;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
|
-
//#region src/utils/map-zod-type-to-sqlite.ts
|
|
109
|
-
/**
|
|
110
|
-
* Maps a Zod type to an SQLite column type.
|
|
111
|
-
*
|
|
112
|
-
* analyzes the Zod schema type and optionally its format (e.g., z.email())
|
|
113
|
-
* to determine the most appropriate SQLite storage class or supported type.
|
|
114
|
-
* @param zodType Zod type string (e.g., 'string', 'number')
|
|
115
|
-
* @param schema The actual Zod schema instance
|
|
116
|
-
* @returns SQLite column type
|
|
117
|
-
*/
|
|
118
|
-
function mapZodTypeToSQLite(zodType, schema) {
|
|
119
|
-
switch (zodType) {
|
|
120
|
-
case "string": switch (schema._zod.def.format) {
|
|
121
|
-
case "date": return "DATE";
|
|
122
|
-
case "datetime": return "DATETIME";
|
|
123
|
-
case "duration": return "INTEGER";
|
|
124
|
-
default: return "TEXT";
|
|
125
|
-
}
|
|
126
|
-
case "enum":
|
|
127
|
-
case "literal":
|
|
128
|
-
case "array":
|
|
129
|
-
case "template_literal":
|
|
130
|
-
case "object": return "TEXT";
|
|
131
|
-
case "date": return "DATE";
|
|
132
|
-
case "number": switch (schema._zod.def.format) {
|
|
133
|
-
case "safeint":
|
|
134
|
-
case "uint32":
|
|
135
|
-
case "int32": return "INTEGER";
|
|
136
|
-
case "float32":
|
|
137
|
-
case "float64": return "FLOAT";
|
|
138
|
-
default: return "REAL";
|
|
139
|
-
}
|
|
140
|
-
case "boolean": return "BOOLEAN";
|
|
141
|
-
case "bigint": return "BIGINT";
|
|
142
|
-
case "null":
|
|
143
|
-
case "undefined": return "NULL";
|
|
144
|
-
case "file": return "BLOB";
|
|
145
|
-
default: return "TEXT";
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
//#endregion
|
|
149
|
-
//#region src/utils/zod-to-sqlite.ts
|
|
150
|
-
/**
|
|
151
|
-
* Analyzes a Zod schema to extract SQLite metadata.
|
|
152
|
-
*
|
|
153
|
-
* Unwraps optional/nullable/default layers to find the underlying core type
|
|
154
|
-
* and extracts metadata like nullability and default values.
|
|
155
|
-
* @param schema Zod schema to analyze
|
|
156
|
-
* @returns Object containing SQLite type, nullability, default value, and inner schema
|
|
157
|
-
*/
|
|
158
|
-
function zodToSQLite(schema) {
|
|
159
|
-
let nullable = false;
|
|
160
|
-
let defaultValue;
|
|
161
|
-
let currentSchema = schema;
|
|
162
|
-
while (true) {
|
|
163
|
-
const definition = currentSchema._zod.def;
|
|
164
|
-
const zodType = definition.type;
|
|
165
|
-
if (zodType === "nullable" || zodType === "optional") {
|
|
166
|
-
nullable = true;
|
|
167
|
-
currentSchema = definition.innerType;
|
|
168
|
-
} else if (zodType === "default") {
|
|
169
|
-
defaultValue = typeof definition.defaultValue === "function" ? definition.defaultValue() : definition.defaultValue;
|
|
170
|
-
currentSchema = definition.innerType;
|
|
171
|
-
} else break;
|
|
172
|
-
}
|
|
173
|
-
const mainType = currentSchema._zod.def.type;
|
|
174
|
-
const SQLiteType = mapZodTypeToSQLite(mainType, currentSchema);
|
|
175
|
-
return {
|
|
176
|
-
defaultValue,
|
|
177
|
-
nullable,
|
|
178
|
-
schema,
|
|
179
|
-
SQLiteType
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
//#endregion
|
|
183
|
-
//#region src/column-definitions.ts
|
|
184
|
-
/**
|
|
185
|
-
* Builds the SQL definition for a single column.
|
|
186
|
-
*
|
|
187
|
-
* Combines name, type, and constraints into a valid SQL column string.
|
|
188
|
-
* @param column Column configuration object
|
|
189
|
-
* @returns Complete column definition string (e.g., "id INTEGER NOT NULL")
|
|
190
|
-
*/
|
|
191
|
-
function buildColumnDefinition(column) {
|
|
192
|
-
const { name, references, schema, unique } = column;
|
|
193
|
-
const { defaultValue, nullable, SQLiteType } = zodToSQLite(schema);
|
|
194
|
-
const parts = [name, SQLiteType];
|
|
195
|
-
if (!nullable) parts.push("NOT NULL");
|
|
196
|
-
if (defaultValue !== void 0) parts.push(formatDefaultValue(defaultValue, SQLiteType));
|
|
197
|
-
if (unique) parts.push("UNIQUE");
|
|
198
|
-
const checkConstraint = formatCheckConstraint(name, schema);
|
|
199
|
-
if (checkConstraint) parts.push(checkConstraint);
|
|
200
|
-
if (references) {
|
|
201
|
-
const { column: refColumn, onDelete, onUpdate, table } = references;
|
|
202
|
-
parts.push(`REFERENCES ${table}(${refColumn})`);
|
|
203
|
-
if (onDelete) parts.push(`ON DELETE ${onDelete}`);
|
|
204
|
-
if (onUpdate) parts.push(`ON UPDATE ${onUpdate}`);
|
|
205
|
-
}
|
|
206
|
-
return parts.join(" ");
|
|
207
|
-
}
|
|
208
|
-
//#endregion
|
|
209
|
-
//#region src/index-statements.ts
|
|
210
|
-
/**
|
|
211
|
-
* Generates SQL statements for table indexes.
|
|
212
|
-
*
|
|
213
|
-
* Creates standard, unique, and partial indexes based on configuration.
|
|
214
|
-
* @param tableName Name of the table
|
|
215
|
-
* @param indexes Array of index configurations
|
|
216
|
-
* @returns Array of CREATE INDEX SQL statements
|
|
217
|
-
*/
|
|
218
|
-
function buildIndexStatements(tableName, indexes) {
|
|
219
|
-
if (!indexes || indexes.length === 0) return [];
|
|
220
|
-
return indexes.map((index) => {
|
|
221
|
-
const uniqueClause = index.unique ? "UNIQUE " : "";
|
|
222
|
-
const whereClause = index.where ? ` WHERE ${index.where}` : "";
|
|
223
|
-
return `CREATE ${uniqueClause}INDEX ${index.name} ON ${tableName} (${index.columns.join(", ")})${whereClause};`;
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
//#endregion
|
|
227
|
-
//#region src/primary-key-constraint.ts
|
|
228
|
-
/**
|
|
229
|
-
* Builds the PRIMARY KEY constraint string.
|
|
230
|
-
*
|
|
231
|
-
* Handles both single-column and composite primary keys.
|
|
232
|
-
* @param primaryKeys Array of column names forming the primary key
|
|
233
|
-
* @returns SQL constraint string (e.g., "PRIMARY KEY (id, email)")
|
|
234
|
-
*/
|
|
235
|
-
function buildPrimaryKeyConstraint(primaryKeys) {
|
|
236
|
-
if (primaryKeys.length === 0) return "";
|
|
237
|
-
return `PRIMARY KEY (${primaryKeys.join(", ")})`;
|
|
238
|
-
}
|
|
239
|
-
//#endregion
|
|
240
|
-
//#region src/zod-schema.ts
|
|
241
|
-
/**
|
|
242
|
-
* Builds a Zod schema from column definitions.
|
|
243
|
-
*
|
|
244
|
-
* Creates a Zod object schema where keys are column names and values are
|
|
245
|
-
* the corresponding Zod schemas from the column configuration.
|
|
246
|
-
* @template TColumns - Array of column configurations
|
|
247
|
-
* @param columns Array of column configurations
|
|
248
|
-
* @returns Zod object schema
|
|
249
|
-
*/
|
|
250
|
-
function buildZodSchema(columns) {
|
|
251
|
-
const schemaShape = {};
|
|
252
|
-
for (const column of columns) schemaShape[column.name] = column.schema;
|
|
253
|
-
return z.object(schemaShape);
|
|
254
|
-
}
|
|
255
|
-
//#endregion
|
|
256
|
-
//#region src/table.ts
|
|
257
|
-
/**
|
|
258
|
-
* Creates a table definition and Zod schema.
|
|
259
|
-
*
|
|
260
|
-
* This function is the main entry point for defining a table. It takes a configuration object
|
|
261
|
-
* and returns:
|
|
262
|
-
* 1. `table`: SQL CREATE TABLE statement
|
|
263
|
-
* 2. `indexes`: Array of SQL CREATE INDEX statements
|
|
264
|
-
* 3. `schema`: Zod object schema matching the table structure
|
|
265
|
-
* @template TColumns - Array of column configurations
|
|
266
|
-
* @param config Table configuration object
|
|
267
|
-
* @returns Object containing SQL statements and Zod schema
|
|
268
|
-
*/
|
|
269
|
-
function createTable(config) {
|
|
270
|
-
const { columns, indexes, name, primaryKeys } = config;
|
|
271
|
-
const columnDefs = columns.map((column) => buildColumnDefinition(column));
|
|
272
|
-
const primaryKeyConstraint = buildPrimaryKeyConstraint(primaryKeys);
|
|
273
|
-
const createTableSQL = `CREATE TABLE ${name} (
|
|
274
|
-
${[...columnDefs, primaryKeyConstraint].filter(Boolean).join(",\n ")}
|
|
275
|
-
);`;
|
|
276
|
-
return {
|
|
277
|
-
indexes: buildIndexStatements(name, indexes),
|
|
278
|
-
schema: buildZodSchema(columns),
|
|
279
|
-
table: createTableSQL
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
//#endregion
|
|
283
|
-
export { createTable };
|
|
1
|
+
import{z as e}from"zod";function t(e,t){let n=t;for(;;){let e=n._zod.def;if(e.type===`nullable`||e.type===`optional`||e.type==="default")n=e.innerType;else break}let r=n._zod.def;switch(r.type){case`enum`:{let t=r.entries;return`CHECK(${e} IN (${Object.values(t).map(e=>`'${String(e).replaceAll(`'`,`''`)}'`).join(`, `)}))`}case`literal`:{let t=r.values;if(t.length===1){let n=t[0];return typeof n==`string`?`CHECK(${e} = '${n.replaceAll(`'`,`''`)}')`:`CHECK(${e} = ${n})`}return`CHECK(${e} IN (${t.map(e=>typeof e==`string`?`'${e.replaceAll(`'`,`''`)}'`:String(e)).join(`, `)}))`}case`number`:{let t=r.checks;if(!t)return;let n=[],i=(t,r)=>{switch(t){case`greater_than`:n.push(`${e} >= ${r}`);break;case`less_than`:n.push(`${e} <= ${r}`)}};for(let e of t){let t=e._zod.def.check,n=e._zod.def.value;i(t,n)}return n.length>0?`CHECK(${n.join(` AND `)})`:void 0}case`string`:{let t=r.checks;if(!t)return;let n=[],i=(t,r)=>{switch(t){case`length_equals`:n.push(`length(${e}) = ${r.length}`);break;case`max_length`:n.push(`length(${e}) <= ${r.maximum}`);break;case`min_length`:n.push(`length(${e}) >= ${r.minimum}`)}};for(let e of t){let t=e._zod.def.check,n=e._zod.def;i(t,n)}return n.length>0?`CHECK(${n.join(` AND `)})`:void 0}case`union`:{let t=r.options;if(!t.every(e=>e._zod.def.type===`literal`))return;let n=[];for(let e of t)n.push(...e._zod.def.values);return`CHECK(${e} IN (${n.map(e=>typeof e==`string`?`'${e.replaceAll(`'`,`''`)}'`:String(e)).join(`, `)}))`}}}function n(e,t){switch(t){case`TEXT`:return`DEFAULT '${String(e).replaceAll(`'`,`''`)}'`;case`INTEGER`:case`REAL`:return`DEFAULT ${Number(e)}`;case`BOOLEAN`:return`DEFAULT ${+!!e}`;case`NULL`:return`DEFAULT NULL`;default:return`DEFAULT '${e}'`}}function r(e,t){switch(e){case`string`:switch(t._zod.def.format){case`date`:return`DATE`;case`datetime`:return`DATETIME`;case`duration`:return`INTEGER`;default:return`TEXT`}case`enum`:case`literal`:case`array`:case`template_literal`:case`object`:return`TEXT`;case`date`:return`DATE`;case`number`:switch(t._zod.def.format){case`safeint`:case`uint32`:case`int32`:return`INTEGER`;case`float32`:case`float64`:return`FLOAT`;default:return`REAL`}case`boolean`:return`BOOLEAN`;case`bigint`:return`BIGINT`;case`null`:case`undefined`:return`NULL`;case`file`:return`BLOB`;default:return`TEXT`}}function i(e){let t=!1,n,i=e;for(;;){let e=i._zod.def,r=e.type;if(r===`nullable`||r===`optional`)t=!0,i=e.innerType;else if(r==="default")n=typeof e.defaultValue==`function`?e.defaultValue():e.defaultValue,i=e.innerType;else break}let a=i._zod.def.type,o=r(a,i);return{defaultValue:n,nullable:t,schema:e,SQLiteType:o}}function a(e){let{name:r,references:a,schema:o,unique:s}=e,{defaultValue:c,nullable:l,SQLiteType:u}=i(o),d=[r,u];l||d.push(`NOT NULL`),c!==void 0&&d.push(n(c,u)),s&&d.push(`UNIQUE`);let f=t(r,o);if(f&&d.push(f),a){let{column:e,onDelete:t,onUpdate:n,table:r}=a;d.push(`REFERENCES ${r}(${e})`),t&&d.push(`ON DELETE ${t}`),n&&d.push(`ON UPDATE ${n}`)}return d.join(` `)}function o(e,t){return!t||t.length===0?[]:t.map(t=>{let n=t.unique?`UNIQUE `:``,r=t.where?` WHERE ${t.where}`:``;return`CREATE ${n}INDEX ${t.name} ON ${e} (${t.columns.join(`, `)})${r};`})}function s(e){return e.length===0?``:`PRIMARY KEY (${e.join(`, `)})`}function c(t){let n={};for(let e of t)n[e.name]=e.schema;return e.object(n)}function l(e){let{columns:t,indexes:n,name:r,primaryKeys:i}=e,l=t.map(e=>a(e)),u=s(i),d=`CREATE TABLE ${r} (
|
|
2
|
+
${[...l,u].filter(Boolean).join(`,
|
|
3
|
+
`)}
|
|
4
|
+
);`;return{indexes:o(r,n),schema:c(t),table:d}}export{l as createTable};
|
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.5",
|
|
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>",
|
|
@@ -35,23 +35,22 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
39
|
-
"pnpm": ">=11.0.0"
|
|
38
|
+
"node": ">=24.0.0"
|
|
40
39
|
},
|
|
41
40
|
"dependencies": {
|
|
42
41
|
"zod": "^4.4.3"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
|
-
"@favorodera/eslint-config": "^
|
|
44
|
+
"@favorodera/eslint-config": "^3.0.0",
|
|
46
45
|
"@types/node": "^26.2.0",
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
"db0": "^0.3.4",
|
|
46
|
+
"@vitest/ui": "5.0.5",
|
|
47
|
+
"db0": "^0.4.0",
|
|
50
48
|
"eslint": "^10.8.1",
|
|
49
|
+
"pkg-pr-new": "^0.0.93",
|
|
51
50
|
"relizy": "^1.4.9",
|
|
52
|
-
"tsdown": "^0.
|
|
51
|
+
"tsdown": "^0.23.0",
|
|
53
52
|
"typescript": "^6.0.3",
|
|
54
|
-
"vitest": "^
|
|
53
|
+
"vitest": "^5.0.0"
|
|
55
54
|
},
|
|
56
55
|
"scripts": {
|
|
57
56
|
"build": "tsdown",
|
|
@@ -59,7 +58,8 @@
|
|
|
59
58
|
"test": "vitest run",
|
|
60
59
|
"test:watch": "vitest --watch --ui",
|
|
61
60
|
"typecheck": "tsc --noEmit",
|
|
62
|
-
"lint": "eslint . --fix",
|
|
61
|
+
"lint:fix": "eslint . --fix",
|
|
62
|
+
"lint": "eslint .",
|
|
63
63
|
"ready": "pnpm install && pnpm lint && pnpm typecheck && pnpm test && pnpm build"
|
|
64
64
|
}
|
|
65
65
|
}
|