zod-sqlite 1.0.2 → 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.
@@ -0,0 +1,460 @@
1
+ import * as zod from "zod/v4/core";
2
+ //#region src/types.d.ts
3
+ /**
4
+ * SQLite native column types.
5
+ *
6
+ * SQLite uses a dynamic type system with these five storage classes.
7
+ * Column values are stored in one of these formats, and SQLite performs
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.
14
+ *
15
+ * These types are supported by SQLite but not as native storage classes.
16
+ * They are often used as aliases for other types or for specific purposes.
17
+ * @see https://www.sqlite.org/datatype3.html#type_affinity
18
+ */
19
+ type SQLiteSupportType = 'BIGINT' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'FLOAT' | 'NUMERIC';
20
+ /**
21
+ * Foreign key constraint actions for ON DELETE and ON UPDATE clauses.
22
+ *
23
+ * These actions determine how referential integrity is maintained when
24
+ * parent rows are modified or deleted. They control the cascading behavior
25
+ * of foreign key relationships.
26
+ *
27
+ * **Actions:**
28
+ * - `NO ACTION`: Deferred constraint check (performed after statement completes)
29
+ * - `RESTRICT`: Prevent the operation immediately (fails before any changes)
30
+ * - `SET NULL`: Automatically set foreign key columns to NULL
31
+ * - `SET DEFAULT`: Automatically set foreign key columns to their default values
32
+ * - `CASCADE`: Propagate the DELETE or UPDATE to all child rows
33
+ *
34
+ * **Default behavior**: If not specified, SQLite uses `NO ACTION`
35
+ * @see https://www.sqlite.org/foreignkeys.html#fk_actions
36
+ * @example
37
+ * ```ts
38
+ * // Delete a user and all their posts automatically
39
+ * references: {
40
+ * table: 'users',
41
+ * column: 'id',
42
+ * onDelete: 'CASCADE'
43
+ * }
44
+ *
45
+ * // Prevent deleting a category that has products
46
+ * references: {
47
+ * table: 'categories',
48
+ * column: 'id',
49
+ * onDelete: 'RESTRICT'
50
+ * }
51
+ * ```
52
+ */
53
+ type ForeignKeyAction = 'CASCADE' | 'NO ACTION' | 'RESTRICT' | 'SET DEFAULT' | 'SET NULL';
54
+ /**
55
+ * Foreign key reference configuration.
56
+ *
57
+ * Defines a relationship to another table by referencing one of its columns.
58
+ * This creates a constraint ensuring that values in this column must exist
59
+ * in the referenced table's column (referential integrity).
60
+ *
61
+ * **Important notes:**
62
+ * - The referenced column should typically be a PRIMARY KEY or have a UNIQUE constraint
63
+ * - Foreign key constraints are not enforced by default in SQLite (must enable with PRAGMA)
64
+ * - Composite foreign keys (multiple columns) are defined at the table level, not here
65
+ * @example
66
+ * ```ts
67
+ * // Simple foreign key to users table
68
+ * {
69
+ * table: 'users',
70
+ * column: 'id',
71
+ * onDelete: 'CASCADE'
72
+ * }
73
+ *
74
+ * // Foreign key with both actions specified
75
+ * {
76
+ * table: 'categories',
77
+ * column: 'id',
78
+ * onDelete: 'SET NULL',
79
+ * onUpdate: 'CASCADE'
80
+ * }
81
+ * ```
82
+ */
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;
90
+ /**
91
+ * Name of the referenced column in the parent table.
92
+ *
93
+ * This column should typically be a PRIMARY KEY or have a UNIQUE constraint
94
+ * to ensure the relationship is well-defined.
95
+ */
96
+ column: string;
97
+ /**
98
+ * Action to take when the referenced row is deleted.
99
+ *
100
+ * Determines what happens to this row when the parent row is deleted.
101
+ * @default 'NO ACTION'
102
+ */
103
+ onDelete?: ForeignKeyAction;
104
+ /**
105
+ * Action to take when the referenced column value is updated.
106
+ *
107
+ * Determines what happens to this foreign key value when the parent
108
+ * column value changes.
109
+ * @default 'NO ACTION'
110
+ */
111
+ onUpdate?: ForeignKeyAction;
112
+ }
113
+ /**
114
+ * Database index configuration.
115
+ *
116
+ * Indexes improve query performance by creating a separate data structure
117
+ * that allows faster lookups on specific columns. They're essential for:
118
+ * - Columns frequently used in WHERE clauses
119
+ * - Columns used in JOIN conditions
120
+ * - Columns used in ORDER BY clauses
121
+ *
122
+ * **Trade-offs:**
123
+ * - **Pros**: Faster SELECT queries, faster JOIN operations
124
+ * - **Cons**: Slower INSERT/UPDATE/DELETE, increased storage size
125
+ *
126
+ * **Best practices:**
127
+ * - Index foreign key columns
128
+ * - Index columns frequently used in WHERE clauses
129
+ * - Consider composite indexes for multi-column queries
130
+ * - Use partial indexes (with WHERE clause) for filtered queries
131
+ * @template TColumns - An array of column configuration objects.
132
+ * @example
133
+ * ```ts
134
+ * // Simple index on email column
135
+ * {
136
+ * name: 'idx_users_email',
137
+ * columns: ['email'],
138
+ * unique: true
139
+ * }
140
+ *
141
+ * // Composite index for queries filtering by status and date
142
+ * {
143
+ * name: 'idx_orders_status_date',
144
+ * columns: ['status', 'created_at']
145
+ * }
146
+ *
147
+ * // Partial index for active users only
148
+ * {
149
+ * name: 'idx_active_users',
150
+ * columns: ['username'],
151
+ * where: 'status = "active"'
152
+ * }
153
+ * ```
154
+ */
155
+ interface IndexConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$ZodType>> = ReadonlyArray<ColumnConfig<string, zod.$ZodType>>> {
156
+ /**
157
+ * Unique name for the index.
158
+ *
159
+ * Used in the CREATE INDEX statement. Convention is to prefix with 'idx_'
160
+ * followed by table name and column names.
161
+ * @example 'idx_users_email'
162
+ * @example 'idx_posts_author_created'
163
+ */
164
+ 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
+ /**
175
+ * Whether this is a unique index.
176
+ *
177
+ * When true, enforces uniqueness constraint on the indexed columns.
178
+ * Duplicate values will cause INSERT/UPDATE to fail.
179
+ *
180
+ * **Note**: PRIMARY KEY columns automatically have a unique index.
181
+ * @default false
182
+ */
183
+ unique?: boolean;
184
+ /**
185
+ * Optional WHERE clause for a partial index.
186
+ *
187
+ * Only rows matching this condition are included in the index, reducing
188
+ * index size and improving performance for filtered queries.
189
+ *
190
+ * Useful when you frequently query a subset of rows.
191
+ * @example 'deleted_at IS NULL' - index only active records
192
+ * @example 'status = "published"' - index only published content
193
+ * @example 'price > 100' - index only expensive items
194
+ */
195
+ where?: string;
196
+ }
197
+ /**
198
+ * Database column configuration.
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
+ * }
223
+ *
224
+ * // Foreign key column
225
+ * {
226
+ * name: 'author_id',
227
+ * schema: z.int(),
228
+ * references: {
229
+ * table: 'users',
230
+ * column: 'id',
231
+ * onDelete: 'CASCADE'
232
+ * }
233
+ * }
234
+ *
235
+ * // Optional column with default
236
+ * {
237
+ * name: 'status',
238
+ * schema: z.enum(['draft', 'published']).default('draft')
239
+ * }
240
+ * ```
241
+ */
242
+ interface ColumnConfig<TName extends string = string, TSchema extends zod.$ZodType = zod.$ZodType> {
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
+ }
327
+ /**
328
+ * Complete database table configuration.
329
+ * @template TColumns - An array of column configuration objects.
330
+ */
331
+ interface TableConfig<TColumns extends ReadonlyArray<ColumnConfig<string, zod.$ZodType>> = ReadonlyArray<ColumnConfig<string, zod.$ZodType>>> {
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;
347
+ /**
348
+ * Array of column configurations defining the table structure.
349
+ *
350
+ * Each column specifies its name, type (via Zod schema), and optional
351
+ * constraints. The order of columns here determines their order in the
352
+ * CREATE TABLE statement.
353
+ *
354
+ * **Best practices:**
355
+ * - List primary key column(s) first
356
+ * - Group related columns together
357
+ * - Place frequently queried columns earlier
358
+ * - Put large TEXT/BLOB columns last
359
+ * @example
360
+ * ```ts
361
+ * [
362
+ * { name: 'id', schema: z.int() },
363
+ * { name: 'email', schema: z.email() },
364
+ * { name: 'created_at', schema: z.date().default(new Date()) }
365
+ * ]
366
+ * ```
367
+ */
368
+ 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
+ /**
399
+ * Optional array of index configurations for query optimization.
400
+ *
401
+ * Indexes speed up SELECT queries but slow down INSERT/UPDATE/DELETE
402
+ * operations. Create indexes strategically based on your query patterns.
403
+ *
404
+ * **When to add indexes:**
405
+ * - Foreign key columns (for JOIN performance)
406
+ * - Columns frequently used in WHERE clauses
407
+ * - Columns used in ORDER BY clauses
408
+ * - Columns with high selectivity (many unique values)
409
+ *
410
+ * **When NOT to add indexes:**
411
+ * - Small tables (< 1000 rows) - full table scan is fast enough
412
+ * - Columns with low selectivity (few unique values, like boolean)
413
+ * - Tables with frequent writes and rare reads
414
+ * - Columns rarely used in queries
415
+ * @example
416
+ * ```ts
417
+ * [
418
+ * // Index foreign key for JOIN performance
419
+ * { name: 'idx_posts_author', columns: ['author_id'] },
420
+ *
421
+ * // Unique index for lookups
422
+ * { name: 'idx_users_email', columns: ['email'], unique: true },
423
+ *
424
+ * // Composite index for multi-column queries
425
+ * { name: 'idx_posts_status_date', columns: ['status', 'created_at'] },
426
+ *
427
+ * // Partial index for filtered queries
428
+ * {
429
+ * name: 'idx_active_users',
430
+ * columns: ['last_login'],
431
+ * where: 'deleted_at IS NULL'
432
+ * }
433
+ * ]
434
+ * ```
435
+ */
436
+ indexes?: Array<IndexConfig<TColumns>>;
437
+ }
438
+ //#endregion
439
+ //#region src/table.d.ts
440
+ /**
441
+ * Creates a table definition and Zod schema.
442
+ *
443
+ * This function is the main entry point for defining a table. It takes a configuration object
444
+ * and returns:
445
+ * 1. `table`: SQL CREATE TABLE statement
446
+ * 2. `indexes`: Array of SQL CREATE INDEX statements
447
+ * 3. `schema`: Zod object schema matching the table structure
448
+ * @template TColumns - Array of column configurations
449
+ * @param config Table configuration object
450
+ * @returns Object containing SQL statements and Zod schema
451
+ */
452
+ declare function createTable<const TColumns extends ReadonlyArray<ColumnConfig<any, any>>>(config: TableConfig<TColumns>): {
453
+ indexes: string[];
454
+ schema: import("zod").ZodObject<{ [K in TColumns[number]["name"]]: Extract<TColumns[number], {
455
+ name: K;
456
+ }>["schema"]; }, import("zod/v4/core").$strip>;
457
+ table: string;
458
+ };
459
+ //#endregion
460
+ export { ColumnConfig, ForeignKeyAction, ForeignKeyReference, IndexConfig, SQLiteSupportType, SQLiteType, TableConfig, createTable };