zod-sqlite 1.0.2 → 1.0.3

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.
@@ -0,0 +1,461 @@
1
+ import * as zod from "zod/v4/core";
2
+
3
+ //#region src/types.d.ts
4
+ /**
5
+ * SQLite native column types.
6
+ *
7
+ * SQLite uses a dynamic type system with these five storage classes.
8
+ * Column values are stored in one of these formats, and SQLite performs
9
+ * type conversions as needed during operations.
10
+ * @see https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
11
+ */
12
+ type SQLiteType = 'BLOB' | 'INTEGER' | 'NULL' | 'REAL' | 'TEXT';
13
+ /**
14
+ * SQLite supported column types.
15
+ *
16
+ * These types are supported by SQLite but not as native storage classes.
17
+ * They are often used as aliases for other types or for specific purposes.
18
+ * @see https://www.sqlite.org/datatype3.html#type_affinity
19
+ */
20
+ type SQLiteSupportType = 'BIGINT' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'FLOAT' | 'NUMERIC';
21
+ /**
22
+ * Foreign key constraint actions for ON DELETE and ON UPDATE clauses.
23
+ *
24
+ * These actions determine how referential integrity is maintained when
25
+ * parent rows are modified or deleted. They control the cascading behavior
26
+ * of foreign key relationships.
27
+ *
28
+ * **Actions:**
29
+ * - `NO ACTION`: Deferred constraint check (performed after statement completes)
30
+ * - `RESTRICT`: Prevent the operation immediately (fails before any changes)
31
+ * - `SET NULL`: Automatically set foreign key columns to NULL
32
+ * - `SET DEFAULT`: Automatically set foreign key columns to their default values
33
+ * - `CASCADE`: Propagate the DELETE or UPDATE to all child rows
34
+ *
35
+ * **Default behavior**: If not specified, SQLite uses `NO ACTION`
36
+ * @see https://www.sqlite.org/foreignkeys.html#fk_actions
37
+ * @example
38
+ * ```ts
39
+ * // Delete a user and all their posts automatically
40
+ * references: {
41
+ * table: 'users',
42
+ * column: 'id',
43
+ * onDelete: 'CASCADE'
44
+ * }
45
+ *
46
+ * // Prevent deleting a category that has products
47
+ * references: {
48
+ * table: 'categories',
49
+ * column: 'id',
50
+ * onDelete: 'RESTRICT'
51
+ * }
52
+ * ```
53
+ */
54
+ type ForeignKeyAction = 'CASCADE' | 'NO ACTION' | 'RESTRICT' | 'SET DEFAULT' | 'SET NULL';
55
+ /**
56
+ * Foreign key reference configuration.
57
+ *
58
+ * Defines a relationship to another table by referencing one of its columns.
59
+ * This creates a constraint ensuring that values in this column must exist
60
+ * in the referenced table's column (referential integrity).
61
+ *
62
+ * **Important notes:**
63
+ * - The referenced column should typically be a PRIMARY KEY or have a UNIQUE constraint
64
+ * - Foreign key constraints are not enforced by default in SQLite (must enable with PRAGMA)
65
+ * - Composite foreign keys (multiple columns) are defined at the table level, not here
66
+ * @example
67
+ * ```ts
68
+ * // Simple foreign key to users table
69
+ * {
70
+ * table: 'users',
71
+ * column: 'id',
72
+ * onDelete: 'CASCADE'
73
+ * }
74
+ *
75
+ * // Foreign key with both actions specified
76
+ * {
77
+ * table: 'categories',
78
+ * column: 'id',
79
+ * onDelete: 'SET NULL',
80
+ * onUpdate: 'CASCADE'
81
+ * }
82
+ * ```
83
+ */
84
+ interface ForeignKeyReference {
85
+ /**
86
+ * Name of the referenced table.
87
+ *
88
+ * This is the parent table that contains the column being referenced.
89
+ */
90
+ table: string;
91
+ /**
92
+ * Name of the referenced column in the parent table.
93
+ *
94
+ * This column should typically be a PRIMARY KEY or have a UNIQUE constraint
95
+ * to ensure the relationship is well-defined.
96
+ */
97
+ column: string;
98
+ /**
99
+ * Action to take when the referenced row is deleted.
100
+ *
101
+ * Determines what happens to this row when the parent row is deleted.
102
+ * @default 'NO ACTION'
103
+ */
104
+ onDelete?: ForeignKeyAction;
105
+ /**
106
+ * Action to take when the referenced column value is updated.
107
+ *
108
+ * Determines what happens to this foreign key value when the parent
109
+ * column value changes.
110
+ * @default 'NO ACTION'
111
+ */
112
+ onUpdate?: ForeignKeyAction;
113
+ }
114
+ /**
115
+ * Database index configuration.
116
+ *
117
+ * Indexes improve query performance by creating a separate data structure
118
+ * that allows faster lookups on specific columns. They're essential for:
119
+ * - Columns frequently used in WHERE clauses
120
+ * - Columns used in JOIN conditions
121
+ * - Columns used in ORDER BY clauses
122
+ *
123
+ * **Trade-offs:**
124
+ * - **Pros**: Faster SELECT queries, faster JOIN operations
125
+ * - **Cons**: Slower INSERT/UPDATE/DELETE, increased storage size
126
+ *
127
+ * **Best practices:**
128
+ * - Index foreign key columns
129
+ * - Index columns frequently used in WHERE clauses
130
+ * - Consider composite indexes for multi-column queries
131
+ * - Use partial indexes (with WHERE clause) for filtered queries
132
+ * @template TColumns - An array of column configuration objects.
133
+ * @example
134
+ * ```ts
135
+ * // Simple index on email column
136
+ * {
137
+ * name: 'idx_users_email',
138
+ * columns: ['email'],
139
+ * unique: true
140
+ * }
141
+ *
142
+ * // Composite index for queries filtering by status and date
143
+ * {
144
+ * name: 'idx_orders_status_date',
145
+ * columns: ['status', 'created_at']
146
+ * }
147
+ *
148
+ * // Partial index for active users only
149
+ * {
150
+ * name: 'idx_active_users',
151
+ * columns: ['username'],
152
+ * where: 'status = "active"'
153
+ * }
154
+ * ```
155
+ */
156
+ interface IndexConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$ZodType>> = ReadonlyArray<ColumnConfig<string, zod.$ZodType>>> {
157
+ /**
158
+ * Unique name for the index.
159
+ *
160
+ * Used in the CREATE INDEX statement. Convention is to prefix with 'idx_'
161
+ * followed by table name and column names.
162
+ * @example 'idx_users_email'
163
+ * @example 'idx_posts_author_created'
164
+ */
165
+ name: string;
166
+ /**
167
+ * Column names to include in the index.
168
+ *
169
+ * For composite indexes, the order matters: queries that filter on the
170
+ * first columns benefit most from the index.
171
+ * @example ['email'] - single column
172
+ * @example ['last_name', 'first_name'] - composite index
173
+ */
174
+ columns: ReadonlyArray<TColumns[number]['name']>;
175
+ /**
176
+ * Whether this is a unique index.
177
+ *
178
+ * When true, enforces uniqueness constraint on the indexed columns.
179
+ * Duplicate values will cause INSERT/UPDATE to fail.
180
+ *
181
+ * **Note**: PRIMARY KEY columns automatically have a unique index.
182
+ * @default false
183
+ */
184
+ unique?: boolean;
185
+ /**
186
+ * Optional WHERE clause for a partial index.
187
+ *
188
+ * Only rows matching this condition are included in the index, reducing
189
+ * index size and improving performance for filtered queries.
190
+ *
191
+ * Useful when you frequently query a subset of rows.
192
+ * @example 'deleted_at IS NULL' - index only active records
193
+ * @example 'status = "published"' - index only published content
194
+ * @example 'price > 100' - index only expensive items
195
+ */
196
+ where?: string;
197
+ }
198
+ /**
199
+ * Database column configuration.
200
+ *
201
+ * Defines a single column in a table, including its name, type (via Zod schema),
202
+ * validation rules, and optional constraints like uniqueness and foreign keys.
203
+ *
204
+ * **Column definition workflow:**
205
+ * 1. Zod schema is analyzed to determine SQLite type (TEXT, INTEGER, etc.)
206
+ * 2. Zod schema provides TypeScript type safety and runtime validation
207
+ * 3. Constraints (unique, foreign key) are applied at the database level
208
+ * @template TName - Literal string type for the column name
209
+ * @template TSchema - Zod type for the column schema
210
+ * @example
211
+ * ```ts
212
+ * // Simple text column
213
+ * {
214
+ * name: 'email',
215
+ * schema: z.email()
216
+ * }
217
+ *
218
+ * // Unique column with validation
219
+ * {
220
+ * name: 'username',
221
+ * schema: z.string().min(3).max(20),
222
+ * unique: true
223
+ * }
224
+ *
225
+ * // Foreign key column
226
+ * {
227
+ * name: 'author_id',
228
+ * schema: z.int(),
229
+ * references: {
230
+ * table: 'users',
231
+ * column: 'id',
232
+ * onDelete: 'CASCADE'
233
+ * }
234
+ * }
235
+ *
236
+ * // Optional column with default
237
+ * {
238
+ * name: 'status',
239
+ * schema: z.enum(['draft', 'published']).default('draft')
240
+ * }
241
+ * ```
242
+ */
243
+ interface ColumnConfig<TName extends string = string, TSchema extends zod.$ZodType = zod.$ZodType> {
244
+ /**
245
+ * Name of the column.
246
+ *
247
+ * Used in CREATE TABLE statement and all SQL queries.
248
+ * Should use snake_case by convention.
249
+ * @example 'user_id'
250
+ * @example 'created_at'
251
+ */
252
+ name: TName;
253
+ /**
254
+ * Zod schema defining the column's type, validation, and constraints.
255
+ *
256
+ * The Zod schema serves dual purposes:
257
+ * 1. **Database level**: Mapped to SQLite type (TEXT, INTEGER, REAL, BLOB, NULL)
258
+ * 2. **Application level**: Provides TypeScript types and runtime validation
259
+ *
260
+ * **Zod to SQLite type mapping:**
261
+ * - `z.string()`, `z.enum()`, `z.literal()`, `z.date()` → TEXT
262
+ * - `z.number()` → REAL (or INTEGER if `.int()` is used)
263
+ * - `z.bigint()`, `z.boolean()` → INTEGER
264
+ * - `z.custom()` for file/blob → BLOB
265
+ * - `z.null()`, `z.undefined()` → NULL
266
+ *
267
+ * **Zod features mapped to SQL:**
268
+ * - `.optional()`, `.nullable()` → column allows NULL
269
+ * - `.default(value)` → DEFAULT clause in SQL
270
+ * - `.min()`, `.max()`, `.email()`, etc. → runtime validation only
271
+ * @example z.string() - TEXT column
272
+ * @example z.int() - INTEGER column
273
+ * @example z.string().nullable() - TEXT column that allows NULL
274
+ * @example z.boolean().default(false) - INTEGER with DEFAULT 0
275
+ */
276
+ schema: TSchema;
277
+ /**
278
+ * Whether column values must be unique across all rows.
279
+ *
280
+ * When true, adds a UNIQUE constraint to the column. Any attempt to
281
+ * INSERT or UPDATE a duplicate value will fail.
282
+ *
283
+ * **Note**: This is different from PRIMARY KEY:
284
+ * - UNIQUE columns can be NULL (unless also NOT NULL)
285
+ * - A table can have multiple UNIQUE columns
286
+ * - PRIMARY KEY implies UNIQUE + NOT NULL
287
+ * @default false
288
+ * @example
289
+ * ```ts
290
+ * // Unique email address
291
+ * {
292
+ * name: 'email',
293
+ * schema: z.email(),
294
+ * unique: true
295
+ * }
296
+ * ```
297
+ */
298
+ unique?: boolean;
299
+ /**
300
+ * Foreign key reference configuration.
301
+ *
302
+ * Creates a constraint linking this column to a column in another table.
303
+ * Ensures referential integrity by preventing orphaned records.
304
+ *
305
+ * **Requirements:**
306
+ * - Referenced column should typically be a PRIMARY KEY or UNIQUE
307
+ * - Types must be compatible between the two columns
308
+ * - Foreign keys must be enabled: `PRAGMA foreign_keys = ON`
309
+ *
310
+ * **Composite foreign keys** (multiple columns referencing multiple columns)
311
+ * cannot be defined here - they must be defined at the table level as a
312
+ * separate constraint.
313
+ * @example
314
+ * ```ts
315
+ * {
316
+ * name: 'user_id',
317
+ * schema: z.int(),
318
+ * references: {
319
+ * table: 'users',
320
+ * column: 'id',
321
+ * onDelete: 'CASCADE'
322
+ * }
323
+ * }
324
+ * ```
325
+ */
326
+ references?: ForeignKeyReference;
327
+ }
328
+ /**
329
+ * Complete database table configuration.
330
+ * @template TColumns - An array of column configuration objects.
331
+ */
332
+ interface TableConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$ZodType>> = ReadonlyArray<ColumnConfig<string, zod.$ZodType>>> {
333
+ /**
334
+ * Name of the table.
335
+ *
336
+ * Used in CREATE TABLE statement and all SQL queries.
337
+ * Should use snake_case by convention (e.g., 'user_profiles', 'order_items').
338
+ *
339
+ * **Naming conventions:**
340
+ * - Use plural nouns for tables representing collections (e.g., 'users', 'posts')
341
+ * - Use singular nouns for junction tables (e.g., 'user_role', 'post_tag')
342
+ * - Avoid SQL reserved keywords (e.g., 'order' → 'orders' or 'user_order')
343
+ * @example 'users'
344
+ * @example 'blog_posts'
345
+ * @example 'user_sessions'
346
+ */
347
+ name: string;
348
+ /**
349
+ * Array of column configurations defining the table structure.
350
+ *
351
+ * Each column specifies its name, type (via Zod schema), and optional
352
+ * constraints. The order of columns here determines their order in the
353
+ * CREATE TABLE statement.
354
+ *
355
+ * **Best practices:**
356
+ * - List primary key column(s) first
357
+ * - Group related columns together
358
+ * - Place frequently queried columns earlier
359
+ * - Put large TEXT/BLOB columns last
360
+ * @example
361
+ * ```ts
362
+ * [
363
+ * { name: 'id', schema: z.int() },
364
+ * { name: 'email', schema: z.email() },
365
+ * { name: 'created_at', schema: z.date().default(new Date()) }
366
+ * ]
367
+ * ```
368
+ */
369
+ columns: TColumns;
370
+ /**
371
+ * Column name(s) that form the primary key.
372
+ *
373
+ * The primary key uniquely identifies each row in the table and cannot
374
+ * contain NULL values. Always provided as an array, even for single-column
375
+ * primary keys.
376
+ *
377
+ * **Single column primary key:**
378
+ * - Use an array with one element (e.g., `['id']`)
379
+ * - Typically an auto-incrementing integer
380
+ * - Most common pattern for standard entity tables
381
+ *
382
+ * **Composite primary key:**
383
+ * - Use an array with multiple elements (e.g., `['user_id', 'post_id']`)
384
+ * - Common in junction tables for many-to-many relationships
385
+ * - Order matters: most specific/selective column should come first
386
+ * - The combination of all columns must be unique
387
+ *
388
+ * **Notes:**
389
+ * - Every table should have a primary key
390
+ * - Primary key columns are automatically indexed by SQLite
391
+ * - Primary key implies UNIQUE + NOT NULL constraints on all columns
392
+ * - Cannot be changed after table creation (requires table rebuild)
393
+ * @example ['id'] - single column primary key
394
+ * @example ['user_id', 'role_id'] - composite primary key for junction table
395
+ * @example ['tenant_id', 'record_id'] - composite key for multi-tenant data
396
+ * @example ['country_code', 'postal_code'] - composite key for location data
397
+ */
398
+ primaryKeys: ReadonlyArray<TColumns[number]['name']>;
399
+ /**
400
+ * Optional array of index configurations for query optimization.
401
+ *
402
+ * Indexes speed up SELECT queries but slow down INSERT/UPDATE/DELETE
403
+ * operations. Create indexes strategically based on your query patterns.
404
+ *
405
+ * **When to add indexes:**
406
+ * - Foreign key columns (for JOIN performance)
407
+ * - Columns frequently used in WHERE clauses
408
+ * - Columns used in ORDER BY clauses
409
+ * - Columns with high selectivity (many unique values)
410
+ *
411
+ * **When NOT to add indexes:**
412
+ * - Small tables (< 1000 rows) - full table scan is fast enough
413
+ * - Columns with low selectivity (few unique values, like boolean)
414
+ * - Tables with frequent writes and rare reads
415
+ * - Columns rarely used in queries
416
+ * @example
417
+ * ```ts
418
+ * [
419
+ * // Index foreign key for JOIN performance
420
+ * { name: 'idx_posts_author', columns: ['author_id'] },
421
+ *
422
+ * // Unique index for lookups
423
+ * { name: 'idx_users_email', columns: ['email'], unique: true },
424
+ *
425
+ * // Composite index for multi-column queries
426
+ * { name: 'idx_posts_status_date', columns: ['status', 'created_at'] },
427
+ *
428
+ * // Partial index for filtered queries
429
+ * {
430
+ * name: 'idx_active_users',
431
+ * columns: ['last_login'],
432
+ * where: 'deleted_at IS NULL'
433
+ * }
434
+ * ]
435
+ * ```
436
+ */
437
+ indexes?: Array<IndexConfig<TColumns>>;
438
+ }
439
+ //#endregion
440
+ //#region src/table.d.ts
441
+ /**
442
+ * Creates a table definition and Zod schema.
443
+ *
444
+ * This function is the main entry point for defining a table. It takes a configuration object
445
+ * and returns:
446
+ * 1. `table`: SQL CREATE TABLE statement
447
+ * 2. `indexes`: Array of SQL CREATE INDEX statements
448
+ * 3. `schema`: Zod object schema matching the table structure
449
+ * @template TColumns - Array of column configurations
450
+ * @param config Table configuration object
451
+ * @returns Object containing SQL statements and Zod schema
452
+ */
453
+ declare function createTable<const TColumns extends ReadonlyArray<ColumnConfig<any, any>>>(config: TableConfig<TColumns>): {
454
+ indexes: string[];
455
+ schema: import("zod").ZodObject<{ [K in TColumns[number]["name"]]: Extract<TColumns[number], {
456
+ name: K;
457
+ }>["schema"] }, import("zod/v4/core").$strip>;
458
+ table: string;
459
+ };
460
+ //#endregion
461
+ export { ColumnConfig, ForeignKeyAction, ForeignKeyReference, IndexConfig, SQLiteSupportType, SQLiteType, TableConfig, createTable };