@ix-xs/node-comfort 1.2.0 → 2.0.0

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +136 -549
  3. package/index.js +80 -35
  4. package/package.json +160 -33
  5. package/src/Arr.js +887 -419
  6. package/src/Async.js +585 -0
  7. package/src/Cache.js +329 -0
  8. package/src/Checker.js +971 -539
  9. package/src/Cli.js +1034 -0
  10. package/src/Color.js +415 -0
  11. package/src/Crypto.js +707 -0
  12. package/src/Emitter.js +289 -0
  13. package/src/Env.js +555 -0
  14. package/src/FS.js +1483 -894
  15. package/src/Func.js +670 -353
  16. package/src/Http.js +538 -0
  17. package/src/Id.js +384 -201
  18. package/src/Logger.js +739 -378
  19. package/src/Num.js +844 -361
  20. package/src/Obj.js +855 -372
  21. package/src/SQLite.js +1308 -1005
  22. package/src/Schema.js +1586 -0
  23. package/src/Str.js +983 -436
  24. package/src/Sys.js +511 -0
  25. package/src/Time.js +1260 -422
  26. package/src/Utils.js +321 -325
  27. package/src/errors.js +292 -0
  28. package/types/index.d.ts +693 -526
  29. package/types/src/Arr.d.ts +455 -145
  30. package/types/src/Arr.d.ts.map +1 -1
  31. package/types/src/Async.d.ts +321 -0
  32. package/types/src/Async.d.ts.map +1 -0
  33. package/types/src/Cache.d.ts +195 -0
  34. package/types/src/Cache.d.ts.map +1 -0
  35. package/types/src/Checker.d.ts +501 -235
  36. package/types/src/Checker.d.ts.map +1 -1
  37. package/types/src/Cli.d.ts +582 -0
  38. package/types/src/Cli.d.ts.map +1 -0
  39. package/types/src/Color.d.ts +294 -0
  40. package/types/src/Color.d.ts.map +1 -0
  41. package/types/src/Crypto.d.ts +534 -0
  42. package/types/src/Crypto.d.ts.map +1 -0
  43. package/types/src/Emitter.d.ts +170 -0
  44. package/types/src/Emitter.d.ts.map +1 -0
  45. package/types/src/Env.d.ts +382 -0
  46. package/types/src/Env.d.ts.map +1 -0
  47. package/types/src/FS.d.ts +686 -238
  48. package/types/src/FS.d.ts.map +1 -1
  49. package/types/src/Func.d.ts +380 -135
  50. package/types/src/Func.d.ts.map +1 -1
  51. package/types/src/Http.d.ts +374 -0
  52. package/types/src/Http.d.ts.map +1 -0
  53. package/types/src/Id.d.ts +149 -68
  54. package/types/src/Id.d.ts.map +1 -1
  55. package/types/src/Logger.d.ts +363 -46
  56. package/types/src/Logger.d.ts.map +1 -1
  57. package/types/src/Num.d.ts +473 -137
  58. package/types/src/Num.d.ts.map +1 -1
  59. package/types/src/Obj.d.ts +390 -104
  60. package/types/src/Obj.d.ts.map +1 -1
  61. package/types/src/SQLite.d.ts +646 -309
  62. package/types/src/SQLite.d.ts.map +1 -1
  63. package/types/src/Schema.d.ts +940 -0
  64. package/types/src/Schema.d.ts.map +1 -0
  65. package/types/src/Str.d.ts +569 -166
  66. package/types/src/Str.d.ts.map +1 -1
  67. package/types/src/Sys.d.ts +269 -0
  68. package/types/src/Sys.d.ts.map +1 -0
  69. package/types/src/Time.d.ts +663 -204
  70. package/types/src/Time.d.ts.map +1 -1
  71. package/types/src/Utils.d.ts +154 -102
  72. package/types/src/Utils.d.ts.map +1 -1
  73. package/types/src/errors.d.ts +292 -0
  74. package/types/src/errors.d.ts.map +1 -0
  75. package/types/index.d.ts.map +0 -1
@@ -1,396 +1,733 @@
1
1
  export = SQLite;
2
- declare class SQLite {
2
+ /**
3
+ * A SQLite database connection.
4
+ *
5
+ * @template [R=Record<string, any>] The row type read methods return by default. Each call can override it.
6
+ */
7
+ declare class SQLite<R = Record<string, any>> {
3
8
  /**
4
- * Creates a new SQLite database connection.
9
+ * Opens a database, creating the file and its folders if needed. Use
10
+ * `":memory:"` for a throwaway in-memory database.
5
11
  *
6
- * The database file and its parent folders are created if necessary.
7
- * WAL journal mode and foreign key support are enabled automatically.
8
- *
9
- * Pass `":memory:"` to create a transient in-memory database.
12
+ * @example
13
+ * const db = new nc.SQLite(); // ./db.sqlite
14
+ * const app = new nc.SQLite("data/app.sqlite", { dates: true });
15
+ * const mem = new nc.SQLite(":memory:");
10
16
  *
11
- * @param {string} [path="db.sqlite"] - Path to the SQLite database file.
17
+ * @param {string} [path="db.sqlite"] Relative to the working directory.
18
+ * @param {SQLiteOptions} [options]
19
+ * @throws {SQLiteError} If the file can't be opened.
20
+ */
21
+ constructor(path?: string, options?: SQLiteOptions);
22
+ /**
23
+ * Runs SQL that doesn't return rows. Without parameters, you can pass
24
+ * several statements at once.
12
25
  *
13
26
  * @example
14
- * const db = new SQLite();
27
+ * db.exec("UPDATE users SET active = ? WHERE last_login < ?", [false, cutoff]);
28
+ * db.exec("DELETE FROM sessions WHERE user_id = $id", { $id: 42 });
29
+ * db.exec(`
30
+ * CREATE TABLE tags (id INTEGER PRIMARY KEY, label TEXT UNIQUE);
31
+ * CREATE INDEX idx_tags_label ON tags (label);
32
+ * `);
33
+ *
34
+ * @param {string} sql
35
+ * @param {unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null} [params] An array, named parameters like `{ $id: 1 }`, or a single value.
36
+ * @returns {{ changes: number, lastInsertRowid: number | bigint }}
37
+ * @throws {SQLiteError}
38
+ */
39
+ exec(sql: string, params?: unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null): {
40
+ changes: number;
41
+ lastInsertRowid: number | bigint;
42
+ };
43
+ /**
44
+ * Runs a query and returns the first row. When it reads a single table,
45
+ * JSON and boolean columns are converted.
15
46
  *
16
47
  * @example
17
- * const db = new SQLite("data/my-app.sqlite");
48
+ * const user = db.queryOne("SELECT * FROM users WHERE email = ?", [email]);
18
49
  *
19
- * @example
20
- * const db = new SQLite(":memory:");
50
+ * @template [T=R]
51
+ * @param {string} sql
52
+ * @param {unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null} [params]
53
+ * @returns {T | undefined}
54
+ * @throws {SQLiteError}
21
55
  */
22
- constructor(path?: string);
56
+ queryOne<T = R>(sql: string, params?: unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null): T | undefined;
23
57
  /**
24
- * Executes a raw SQL statement using `run()`.
58
+ * Runs a query and returns every row.
25
59
  *
26
- * Use this for custom SQL that is not covered by the helper methods.
27
- * This method is intended for statements such as:
28
- * - `INSERT`
29
- * - `UPDATE`
30
- * - `DELETE`
31
- * - `CREATE INDEX`
32
- * - PRAGMA writes
60
+ * @example
61
+ * const stats = db.queryAll("SELECT role, COUNT(*) AS n FROM users GROUP BY role");
33
62
  *
34
- * @param {string} sql - SQL statement.
35
- * @param {any[]|any} [params=[]] - Parameters to bind.
36
- * @returns {Promise<{ changes: number, lastInsertRowid: bigint|number } | { error: string }>}
63
+ * @template [T=R]
64
+ * @param {string} sql
65
+ * @param {unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null} [params]
66
+ * @returns {T[]}
67
+ * @throws {SQLiteError}
37
68
  */
38
- exec(sql: string, params?: any[] | any): Promise<{
39
- changes: number;
40
- lastInsertRowid: bigint | number;
41
- } | {
42
- error: string;
43
- }>;
44
- /**
45
- * Executes a raw SQL query and returns the first row.
46
- *
47
- * @param {string} sql - SQL query.
48
- * @param {any[]|any} [params=[]] - Parameters to bind.
49
- * @returns {Promise<object|undefined|{ error: string }>}
50
- */
51
- queryOne(sql: string, params?: any[] | any): Promise<object | undefined | {
52
- error: string;
53
- }>;
54
- /**
55
- * Executes a raw SQL query and returns all rows.
56
- *
57
- * @param {string} sql - SQL query.
58
- * @param {any[]|any} [params=[]] - Parameters to bind.
59
- * @returns {Promise<object[]|{ error: string }>}
60
- */
61
- queryAll(sql: string, params?: any[] | any): Promise<object[] | {
62
- error: string;
63
- }>;
64
- /**
65
- * Creates a table and stores its schema in memory.
66
- *
67
- * This method supports both the new `columns` property and the legacy
68
- * `options` property for backward compatibility.
69
- *
70
- * @param {{
71
- * name: string,
72
- * columns?: Record<string, {
73
- * type: "INTEGER"|"TEXT"|"REAL"|"BLOB"|"NUMERIC",
74
- * primaryKey?: boolean,
75
- * autoincrement?: boolean,
76
- * notNull?: boolean,
77
- * unique?: boolean,
78
- * defaultValue?: any,
79
- * values?: any[],
80
- * references?: { table: string, column: string },
81
- * onDelete?: string,
82
- * onUpdate?: string
83
- * }>,
84
- * options?: Record<string, any>,
85
- * constraints?: Array<
86
- * | { type: "unique", columns: string[] }
87
- * | { type: "primaryKey", columns: string[] }
88
- * | { type: "check", expression: string }
89
- * | {
90
- * type: "foreignKey",
91
- * columns: string[],
92
- * references: { table: string, columns: string[] },
93
- * onDelete?: string,
94
- * onUpdate?: string
95
- * }
96
- * >,
97
- * indexes?: Array<{
98
- * name?: string,
99
- * columns: string[],
100
- * unique?: boolean
101
- * }>
102
- * }} table - Table definition.
103
- * @returns {Promise<{ ok: true } | { error: string }>}
69
+ queryAll<T = R>(sql: string, params?: unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null): T[];
70
+ /**
71
+ * Goes through the rows of a query one at a time, without loading them all.
72
+ * For big exports.
104
73
  *
105
74
  * @example
106
- * await db.createTable({
107
- * name: "Post",
75
+ * for (const row of db.iterate("SELECT * FROM events WHERE day = ?", [day])) {
76
+ * stream.write(JSON.stringify(row) + "\n");
77
+ * }
78
+ *
79
+ * @template [T=R]
80
+ * @param {string} sql
81
+ * @param {unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null} [params]
82
+ * @returns {Generator<T, void, undefined>}
83
+ * @throws {SQLiteError}
84
+ */
85
+ iterate<T = R>(sql: string, params?: unknown[] | Record<string, unknown> | string | number | bigint | boolean | Date | null): Generator<T, void, undefined>;
86
+ /**
87
+ * Creates a table with its constraints and indexes, unless it already
88
+ * exists. To change an existing table, use `migrate()`.
89
+ *
90
+ * @example
91
+ * db.createTable({
92
+ * name: "posts",
108
93
  * columns: {
109
94
  * id: { type: "INTEGER", primaryKey: true, autoincrement: true },
110
- * userId: {
111
- * type: "INTEGER",
112
- * notNull: true,
113
- * references: { table: "User", column: "id" },
114
- * onDelete: "CASCADE"
115
- * },
116
- * title: { type: "TEXT", notNull: true }
95
+ * user_id: { type: "INTEGER", notNull: true, references: { table: "users", column: "id" }, onDelete: "CASCADE" },
96
+ * status: { type: "TEXT", values: ["draft", "published"], defaultValue: "draft" },
97
+ * tags: { type: "JSON", defaultValue: [] },
117
98
  * },
118
- * indexes: [{ columns: ["userId"] }]
99
+ * indexes: [{ columns: ["user_id", "status"] }],
119
100
  * });
101
+ *
102
+ * @param {TableDefinition} table
103
+ * @returns {{ ok: true }}
104
+ * @throws {SQLiteError} If the definition is invalid.
120
105
  */
121
- createTable(table: {
122
- name: string;
123
- columns?: Record<string, {
124
- type: "INTEGER" | "TEXT" | "REAL" | "BLOB" | "NUMERIC";
125
- primaryKey?: boolean;
126
- autoincrement?: boolean;
127
- notNull?: boolean;
128
- unique?: boolean;
129
- defaultValue?: any;
130
- values?: any[];
131
- references?: {
132
- table: string;
133
- column: string;
134
- };
135
- onDelete?: string;
136
- onUpdate?: string;
137
- }>;
138
- options?: Record<string, any>;
139
- constraints?: Array<{
140
- type: "unique";
141
- columns: string[];
142
- } | {
143
- type: "primaryKey";
144
- columns: string[];
145
- } | {
146
- type: "check";
147
- expression: string;
148
- } | {
149
- type: "foreignKey";
150
- columns: string[];
151
- references: {
152
- table: string;
153
- columns: string[];
154
- };
155
- onDelete?: string;
156
- onUpdate?: string;
157
- }>;
158
- indexes?: Array<{
159
- name?: string;
160
- columns: string[];
161
- unique?: boolean;
162
- }>;
163
- }): Promise<{
106
+ createTable(table: TableDefinition): {
164
107
  ok: true;
165
- } | {
166
- error: string;
167
- }>;
108
+ };
168
109
  /**
169
- * Creates an index on an existing table.
170
- *
171
- * Composite indexes are supported by passing multiple columns.
172
- *
173
- * @param {string} tableName - Table name.
174
- * @param {{
175
- * name?: string,
176
- * columns: string[],
177
- * unique?: boolean
178
- * }} index - Index definition.
179
- * @returns {Promise<{ ok: true } | { error: string }>}
110
+ * Creates an index, unless it already exists.
180
111
  *
181
112
  * @example
182
- * await db.createIndex("UserAuthMethod", {
183
- * columns: ["provider", "providerUserId"],
184
- * unique: true
185
- * });
113
+ * db.createIndex("users", { columns: ["email"], unique: true });
114
+ * db.createIndex("posts", { columns: ["slug"], unique: true, where: "deleted_at IS NULL" });
115
+ *
116
+ * @param {string} tableName
117
+ * @param {IndexDefinition} index
118
+ * @returns {{ ok: true }}
119
+ * @throws {SQLiteError}
186
120
  */
187
- createIndex(tableName: string, index: {
188
- name?: string;
189
- columns: string[];
190
- unique?: boolean;
191
- }): Promise<{
121
+ createIndex(tableName: string, index: IndexDefinition): {
192
122
  ok: true;
193
- } | {
194
- error: string;
195
- }>;
123
+ };
196
124
  /**
197
- * Drops a table and removes its model definition.
125
+ * Drops a table and everything in it.
198
126
  *
199
- * @param {string} tableName - Table name.
200
- * @returns {Promise<{ ok: true } | { error: string }>}
127
+ * @param {string} tableName
128
+ * @returns {{ ok: true }}
129
+ * @throws {SQLiteError} If the table doesn't exist.
201
130
  */
202
- deleteTable(tableName: string): Promise<{
131
+ deleteTable(tableName: string): {
203
132
  ok: true;
204
- } | {
205
- error: string;
206
- }>;
133
+ };
207
134
  /**
208
- * Deletes all rows from a table.
135
+ * Deletes every row, keeping the table.
209
136
  *
210
- * @param {string} tableName - Table name.
211
- * @returns {Promise<{ ok: true } | { error: string }>}
137
+ * @param {string} tableName
138
+ * @returns {{ ok: true }}
139
+ * @throws {SQLiteError} If the table doesn't exist.
212
140
  */
213
- clearTable(tableName: string): Promise<{
141
+ clearTable(tableName: string): {
214
142
  ok: true;
215
- } | {
216
- error: string;
217
- }>;
143
+ };
144
+ /**
145
+ * Does the table exist?
146
+ *
147
+ * @param {string} tableName
148
+ * @returns {boolean}
149
+ */
150
+ hasTable(tableName: string): boolean;
218
151
  /**
219
- * Returns a single row matching a plain-object WHERE clause.
152
+ * The tables in the database, sorted.
220
153
  *
221
- * @param {string} tableName - Table name.
222
- * @param {Record<string, any>} [where={}] - Conditions.
223
- * @returns {Promise<object|undefined|{ error: string }>}
154
+ * @returns {string[]}
224
155
  */
225
- get(tableName: string, where?: Record<string, any>): Promise<object | undefined | {
226
- error: string;
227
- }>;
156
+ tables(): string[];
228
157
  /**
229
- * Returns multiple rows from a table.
158
+ * The columns of a table.
230
159
  *
231
- * Supports:
232
- * - exact-match filters via `where`
233
- * - custom ordering
234
- * - pagination via `limit` and `offset`
160
+ * @example
161
+ * db.columns("users");
162
+ * // [{ name: "id", type: "INTEGER", notNull: false, defaultValue: null, primaryKey: true }, ...]
235
163
  *
236
- * @param {string} tableName - Table name.
237
- * @param {Record<string, any>} [where={}] - Exact-match conditions.
238
- * @param {{
239
- * limit?: number,
240
- * offset?: number,
241
- * orderBy?: string,
242
- * direction?: "ASC"|"DESC"
243
- * }} [options={}] - Query options.
244
- * @returns {Promise<object[] | { error: string }>}
164
+ * @param {string} tableName
165
+ * @returns {ColumnInfo[]}
166
+ * @throws {SQLiteError} If the table doesn't exist.
167
+ */
168
+ columns(tableName: string): ColumnInfo[];
169
+ /**
170
+ * The first row that matches.
245
171
  *
246
172
  * @example
247
- * const posts = await db.getAll("Post", { userId: 1 }, {
173
+ * const user = db.get("users", { email: "ada@example.com" });
174
+ * const latest = db.get("posts", { status: "published" }, { orderBy: "created_at", direction: "DESC" });
175
+ *
176
+ * @template [T=R]
177
+ * @param {string} tableName
178
+ * @param {Where<R>} [where]
179
+ * @param {Pick<QueryOptions, "columns" | "orderBy" | "direction">} [options]
180
+ * @returns {T | undefined}
181
+ * @throws {SQLiteError}
182
+ */
183
+ get<T = R>(tableName: string, where?: Where<R>, options?: Pick<QueryOptions, "columns" | "orderBy" | "direction">): T | undefined;
184
+ /**
185
+ * The rows that match, with sorting and pagination.
186
+ *
187
+ * @example
188
+ * db.getAll("users");
189
+ * db.getAll("users", { role: ["admin", "editor"], age: { gte: 18 } });
190
+ * db.getAll("posts", { $or: [{ pinned: true }, { views: { gt: 1000 } }] }, {
191
+ * orderBy: [["views", "DESC"], "title"],
248
192
  * limit: 20,
249
- * offset: 0,
250
- * orderBy: "createdAt",
251
- * direction: "DESC"
193
+ * offset: 40,
252
194
  * });
195
+ *
196
+ * @template [T=R]
197
+ * @param {string} tableName
198
+ * @param {Where<R>} [where]
199
+ * @param {QueryOptions} [options]
200
+ * @returns {T[]}
201
+ * @throws {SQLiteError}
253
202
  */
254
- getAll(tableName: string, where?: Record<string, any>, options?: {
255
- limit?: number;
256
- offset?: number;
257
- orderBy?: string;
258
- direction?: "ASC" | "DESC";
259
- }): Promise<object[] | {
260
- error: string;
261
- }>;
203
+ getAll<T = R>(tableName: string, where?: Where<R>, options?: QueryOptions): T[];
262
204
  /**
263
- * Inserts a new row into a table.
205
+ * Counts the rows that match.
264
206
  *
265
- * This method performs a plain `INSERT`.
266
- * If a unique constraint is violated, SQLite will throw an error.
207
+ * @example
208
+ * db.count("users", { active: true });
267
209
  *
268
- * @param {string} tableName - Table name.
269
- * @param {Record<string, any>} data - Row to insert.
270
- * @returns {Promise<{ ok: true, lastInsertRowid: bigint|number, changes: number } | { error: string }>}
210
+ * @param {string} tableName
211
+ * @param {Where<R>} [where]
212
+ * @returns {number}
213
+ * @throws {SQLiteError}
271
214
  */
272
- insert(tableName: string, data: Record<string, any>): Promise<{
273
- ok: true;
274
- lastInsertRowid: bigint | number;
275
- changes: number;
276
- } | {
277
- error: string;
278
- }>;
215
+ count(tableName: string, where?: Where<R>): number;
279
216
  /**
280
- * Updates one or more rows matching a WHERE clause.
217
+ * Does any row match?
281
218
  *
282
- * This method performs a plain `UPDATE`.
219
+ * @example
220
+ * if (db.exists("users", { email })) throw new Error("Email already registered");
283
221
  *
284
- * @param {string} tableName - Table name.
285
- * @param {Record<string, any>} data - Fields to update.
286
- * @param {Record<string, any>} where - Conditions used to select rows.
287
- * @returns {Promise<{ ok: true, changes: number } | { error: string }>}
222
+ * @param {string} tableName
223
+ * @param {Where<R>} [where]
224
+ * @returns {boolean}
225
+ * @throws {SQLiteError}
288
226
  */
289
- update(tableName: string, data: Record<string, any>, where: Record<string, any>): Promise<{
290
- ok: true;
291
- changes: number;
292
- } | {
293
- error: string;
294
- }>;
227
+ exists(tableName: string, where?: Where<R>): boolean;
295
228
  /**
296
- * Inserts or updates a row using SQLite `ON CONFLICT DO UPDATE`.
229
+ * Inserts a row. Objects and arrays are stored as JSON, booleans as 1 and
230
+ * 0, dates as ISO strings. `undefined` values are skipped, so column
231
+ * defaults apply.
297
232
  *
298
- * This is safer than `INSERT OR REPLACE` because it does not delete
299
- * and recreate the row.
233
+ * @example
234
+ * const { lastInsertRowid } = db.insert("users", { email: "ada@example.com", settings: { theme: "dark" } });
300
235
  *
301
- * @param {string} tableName - Table name.
302
- * @param {Record<string, any>} data - Row data.
303
- * @param {string[]} conflict - Columns that define the conflict target.
304
- * @returns {Promise<{ ok: true, changes: number, lastInsertRowid: bigint|number } | { error: string }>}
236
+ * @param {string} tableName
237
+ * @param {Partial<R> & Record<string, unknown>} data
238
+ * @returns {InsertResult}
239
+ * @throws {SQLiteError} On a constraint failure; `sqliteCode` tells you which, like `"SQLITE_CONSTRAINT_UNIQUE"`.
240
+ */
241
+ insert(tableName: string, data: Partial<R> & Record<string, unknown>): InsertResult;
242
+ /**
243
+ * Inserts many rows in one transaction: all or nothing, and much faster
244
+ * than one by one.
305
245
  *
306
246
  * @example
307
- * await db.upsert("UserAuthMethod", {
308
- * userId: 1,
309
- * provider: "google",
310
- * providerUserId: "123"
311
- * }, ["provider", "providerUserId"]);
247
+ * db.insertMany("products", rowsFromCsv);
248
+ *
249
+ * @param {string} tableName
250
+ * @param {Array<Partial<R> & Record<string, unknown>>} rows
251
+ * @returns {ChangeResult}
252
+ * @throws {SQLiteError} If a row fails; nothing is inserted then.
312
253
  */
313
- upsert(tableName: string, data: Record<string, any>, conflict?: string[]): Promise<{
314
- ok: true;
315
- changes: number;
316
- lastInsertRowid: bigint | number;
317
- } | {
318
- error: string;
319
- }>;
254
+ insertMany(tableName: string, rows: Array<Partial<R> & Record<string, unknown>>): ChangeResult;
320
255
  /**
321
- * Backward-compatible helper that inserts or updates a row.
256
+ * Updates the rows that match. The filter is required, so a forgotten
257
+ * condition can't rewrite the whole table.
322
258
  *
323
- * Behavior:
324
- * - If `where` is provided, the existing row is loaded and merged with `data`,
325
- * then updated if found or inserted if not found.
326
- * - If `where` is not provided but `data.id` exists, lookup is performed on `id`.
327
- * - If no existing row is found, a new row is inserted.
328
- *
329
- * This method is intentionally simple, but for production-grade control,
330
- * prefer using `insert()`, `update()`, or `upsert()`.
259
+ * @example
260
+ * db.update("users", { active: false }, { last_login: { lt: "2025-01-01" } });
331
261
  *
332
- * @param {string} tableName - Table name.
333
- * @param {Record<string, any>} data - Row data.
334
- * @param {Record<string, any>|null} [where=null] - Lookup condition.
335
- * @returns {Promise<{ ok: true } | { error: string }>}
262
+ * @param {string} tableName
263
+ * @param {Partial<R> & Record<string, unknown>} data `undefined` values are skipped.
264
+ * @param {Where<R>} where
265
+ * @returns {ChangeResult}
266
+ * @throws {SQLiteError}
336
267
  */
337
- set(tableName: string, data: Record<string, any>, where?: Record<string, any> | null): Promise<{
338
- ok: true;
339
- } | {
340
- error: string;
341
- }>;
268
+ update(tableName: string, data: Partial<R> & Record<string, unknown>, where: Where<R>): ChangeResult;
342
269
  /**
343
- * Deletes rows matching a WHERE clause.
270
+ * Inserts a row, or updates it in place if it clashes with an existing one
271
+ * on the `conflict` columns.
344
272
  *
345
- * @param {string} tableName - Table name.
346
- * @param {Record<string, any>} where - Conditions.
347
- * @returns {Promise<{ ok: true, changes: number } | { error: string }>}
273
+ * @example
274
+ * db.upsert("settings", { user_id: 1, key: "theme", value: "dark" }, ["user_id", "key"]);
275
+ *
276
+ * @param {string} tableName
277
+ * @param {Partial<R> & Record<string, unknown>} data
278
+ * @param {string[]} conflict The unique columns that identify the row.
279
+ * @returns {InsertResult}
280
+ * @throws {SQLiteError} If `conflict` doesn't match a unique constraint.
348
281
  */
349
- delete(tableName: string, where: Record<string, any>): Promise<{
282
+ upsert(tableName: string, data: Partial<R> & Record<string, unknown>, conflict: string[]): InsertResult;
283
+ /**
284
+ * Updates the row matching `where` (or `{ id: data.id }`), or inserts it.
285
+ * Kept from 1.x; `upsert()` does the same in one atomic statement.
286
+ *
287
+ * @param {string} tableName
288
+ * @param {Partial<R> & Record<string, unknown>} data
289
+ * @param {Where<R> | null} [where]
290
+ * @returns {{ ok: true }}
291
+ * @throws {SQLiteError}
292
+ */
293
+ set(tableName: string, data: Partial<R> & Record<string, unknown>, where?: Where<R> | null): {
350
294
  ok: true;
351
- changes: number;
352
- } | {
353
- error: string;
354
- }>;
295
+ };
355
296
  /**
356
- * Counts rows matching a WHERE clause.
297
+ * Deletes the rows that match. The filter is required; `clearTable()`
298
+ * deletes everything on purpose.
357
299
  *
358
- * @param {string} tableName - Table name.
359
- * @param {Record<string, any>} [where={}] - Conditions.
360
- * @returns {Promise<number | { error: string }>}
300
+ * @example
301
+ * db.delete("sessions", { expires_at: { lt: new Date() } });
302
+ *
303
+ * @param {string} tableName
304
+ * @param {Where<R>} where
305
+ * @returns {ChangeResult}
306
+ * @throws {SQLiteError}
361
307
  */
362
- count(tableName: string, where?: Record<string, any>): Promise<number | {
363
- error: string;
364
- }>;
308
+ delete(tableName: string, where: Where<R>): ChangeResult;
365
309
  /**
366
- * Runs multiple operations inside a transaction.
310
+ * Shortcuts bound to one table, so you don't repeat its name.
311
+ *
312
+ * @example
313
+ * const users = db.table("users"); // in TypeScript: db.table<User>("users")
314
+ * users.insert({ email: "ada@example.com" });
315
+ * users.count({ active: true });
367
316
  *
368
- * If the callback throws, the transaction is rolled back.
369
- * If the callback completes successfully, the transaction is committed.
317
+ * @template [T=R]
318
+ * @param {string} tableName The table can be created later.
319
+ * @returns {TableHandle<T>}
320
+ */
321
+ table<T = R>(tableName: string): TableHandle<T>;
322
+ /**
323
+ * Runs `callback` in a transaction: all its changes are saved together, or
324
+ * none are if it throws. The error is then thrown again. Transactions can
325
+ * nest; an inner failure can be caught without losing the outer work.
370
326
  *
371
- * The callback may be synchronous or asynchronous.
327
+ * @example
328
+ * db.transaction(() => {
329
+ * db.update("accounts", { balance: from.balance - amount }, { id: from.id });
330
+ * db.update("accounts", { balance: to.balance + amount }, { id: to.id });
331
+ * });
372
332
  *
373
333
  * @template T
374
- * @param {() => T|Promise<T>} callback - Function to execute inside the transaction.
375
- * @returns {Promise<T | { error: string }>}
334
+ * @param {() => T} callback Can be async; the commit then waits for it.
335
+ * @returns {T}
336
+ * @throws Whatever `callback` threw, after rolling back.
337
+ */
338
+ transaction<T>(callback: () => T): T;
339
+ /** Whether a transaction is open. */
340
+ get inTransaction(): boolean;
341
+ /**
342
+ * Applies migrations in order, each one once. The current version is kept
343
+ * in the database, and each migration runs in its own transaction. Only
344
+ * ever add migrations at the end of the list.
376
345
  *
377
346
  * @example
378
- * const result = await db.transaction(async () => {
379
- * await db.insert("User", { email: "a@b.com", createdAt: Date.now() });
380
- * await db.insert("Profile", { userId: 1, nickname: "John" });
381
- * return { ok: true };
382
- * });
347
+ * db.migrate([
348
+ * "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE)",
349
+ * "ALTER TABLE users ADD COLUMN name TEXT",
350
+ * (db) => db.update("users", { name: "unknown" }, { name: null }),
351
+ * ]);
352
+ *
353
+ * @param {Migration[]} migrations All of them, from the first.
354
+ * @returns {{ from: number, to: number }} The version before and after.
355
+ * @throws {SQLiteError} If a migration fails. It's rolled back; earlier ones stay.
383
356
  */
384
- transaction<T>(callback: () => T | Promise<T>): Promise<T | {
385
- error: string;
386
- }>;
357
+ migrate(migrations: Migration[]): {
358
+ from: number;
359
+ to: number;
360
+ };
361
+ /** The migration version, stored in `PRAGMA user_version`. */
362
+ get version(): number;
387
363
  /**
388
- * Closes the underlying SQLite connection.
364
+ * Copies the database to a new file. Safe while the app is running.
389
365
  *
390
366
  * @example
391
- * db.close();
367
+ * db.backup(`backups/app-${nc.time.format(new Date(), "YYYY-MM-DD")}.sqlite`);
368
+ *
369
+ * @param {string} path Must not exist yet.
370
+ * @returns {string} The absolute path of the copy.
371
+ * @throws {SQLiteError} If the file exists or can't be written.
372
+ */
373
+ backup(path: string): string;
374
+ /**
375
+ * Compacts the database file to reclaim unused space.
376
+ *
377
+ * @returns {void}
378
+ */
379
+ vacuum(): void;
380
+ /**
381
+ * Makes a JavaScript function callable from SQL.
382
+ *
383
+ * @example
384
+ * db.fn("slugify", (text) => nc.str.slugify(String(text)));
385
+ * db.queryAll("SELECT slugify(title) AS slug FROM posts");
386
+ *
387
+ * @param {string} name
388
+ * @param {(...args: any[]) => null | number | bigint | string | Uint8Array} implementation
389
+ * @param {{ deterministic?: boolean, varargs?: boolean }} [options] `deterministic` (on by default) lets indexes use it; `varargs` accepts any number of arguments.
390
+ * @returns {this}
391
+ */
392
+ fn(name: string, implementation: (...args: any[]) => null | number | bigint | string | Uint8Array, options?: {
393
+ deterministic?: boolean;
394
+ varargs?: boolean;
395
+ }): this;
396
+ /** The absolute path of the database file, or `":memory:"`. */
397
+ get path(): string;
398
+ /** Whether the connection is open. */
399
+ get isOpen(): boolean;
400
+ /**
401
+ * The underlying `node:sqlite` database, for anything not covered here.
402
+ * @returns {import("node:sqlite").DatabaseSync}
403
+ */
404
+ get native(): import("node:sqlite").DatabaseSync;
405
+ /**
406
+ * Closes the connection. Calling it twice is fine.
407
+ *
408
+ * @returns {void}
392
409
  */
393
410
  close(): void;
411
+ /**
412
+ * Closes the connection at the end of a `using` block.
413
+ * @returns {void}
414
+ */
415
+ [Symbol.dispose](): void;
394
416
  #private;
395
417
  }
418
+ declare namespace SQLite {
419
+ export { ColumnType, ForeignKeyAction, ColumnDefinition, TableConstraint, IndexDefinition, TableDefinition, WhereOperators, Where, WhereValue, SortDirection, QueryOptions, SQLiteOptions, InsertResult, ChangeResult, ColumnInfo, Migration, TableHandle };
420
+ }
421
+ /**
422
+ * A column type. `JSON` and `BOOLEAN` columns give you back objects and
423
+ * `true`/`false`.
424
+ */
425
+ type ColumnType = "INTEGER" | "TEXT" | "REAL" | "BLOB" | "NUMERIC" | "JSON" | "BOOLEAN" | "DATETIME" | (string & {});
426
+ /**
427
+ * What happens to a row when the row it references changes.
428
+ */
429
+ type ForeignKeyAction = "CASCADE" | "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION";
430
+ /**
431
+ * A column.
432
+ */
433
+ type ColumnDefinition = {
434
+ /**
435
+ * Like `"INTEGER"`, `"TEXT"`, `"JSON"` or `"VARCHAR(255)"`.
436
+ */
437
+ type: ColumnType;
438
+ primaryKey?: boolean | undefined;
439
+ /**
440
+ * With an `INTEGER` primary key: ids are never reused.
441
+ */
442
+ autoincrement?: boolean | undefined;
443
+ notNull?: boolean | undefined;
444
+ unique?: boolean | undefined;
445
+ /**
446
+ * Objects work for JSON columns, booleans for BOOLEAN ones.
447
+ */
448
+ defaultValue?: unknown;
449
+ /**
450
+ * Allowed values.
451
+ */
452
+ values?: unknown[] | undefined;
453
+ /**
454
+ * A `CHECK` expression, like `"price >= 0"`.
455
+ */
456
+ check?: string | undefined;
457
+ /**
458
+ * Foreign key target.
459
+ */
460
+ references?: {
461
+ table: string;
462
+ column: string;
463
+ } | undefined;
464
+ /**
465
+ * When the referenced row is deleted.
466
+ */
467
+ onDelete?: ForeignKeyAction | undefined;
468
+ /**
469
+ * When the referenced key changes.
470
+ */
471
+ onUpdate?: ForeignKeyAction | undefined;
472
+ };
473
+ /**
474
+ * A constraint on several columns.
475
+ */
476
+ type TableConstraint = {
477
+ type: "unique";
478
+ columns: string[];
479
+ } | {
480
+ type: "primaryKey";
481
+ columns: string[];
482
+ } | {
483
+ type: "check";
484
+ expression: string;
485
+ } | {
486
+ type: "foreignKey";
487
+ columns: string[];
488
+ references: {
489
+ table: string;
490
+ columns: string[];
491
+ };
492
+ onDelete?: ForeignKeyAction;
493
+ onUpdate?: ForeignKeyAction;
494
+ };
495
+ /**
496
+ * An index.
497
+ */
498
+ type IndexDefinition = {
499
+ /**
500
+ * Several columns make a composite index.
501
+ */
502
+ columns: string[];
503
+ unique?: boolean | undefined;
504
+ /**
505
+ * Defaults to `idx_<table>_<columns>`.
506
+ */
507
+ name?: string | undefined;
508
+ /**
509
+ * Makes it a partial index, like `"deleted_at IS NULL"`.
510
+ */
511
+ where?: string | undefined;
512
+ };
513
+ /**
514
+ * A table for `createTable()`.
515
+ */
516
+ type TableDefinition = {
517
+ name: string;
518
+ columns?: Record<string, ColumnDefinition> | undefined;
519
+ /**
520
+ * Old name for `columns`, still accepted.
521
+ */
522
+ options?: Record<string, ColumnDefinition> | undefined;
523
+ constraints?: TableConstraint[] | undefined;
524
+ indexes?: IndexDefinition[] | undefined;
525
+ };
526
+ /**
527
+ * Operators for a column in a filter.
528
+ */
529
+ type WhereOperators = {
530
+ /**
531
+ * Equal. `null` means `IS NULL`.
532
+ */
533
+ eq?: unknown;
534
+ /**
535
+ * Not equal. Rows where the column is `NULL` match too.
536
+ */
537
+ ne?: unknown;
538
+ /**
539
+ * Greater than.
540
+ */
541
+ gt?: string | number | bigint | Date | undefined;
542
+ /**
543
+ * Greater than or equal.
544
+ */
545
+ gte?: string | number | bigint | Date | undefined;
546
+ /**
547
+ * Less than.
548
+ */
549
+ lt?: string | number | bigint | Date | undefined;
550
+ /**
551
+ * Less than or equal.
552
+ */
553
+ lte?: string | number | bigint | Date | undefined;
554
+ /**
555
+ * One of these values.
556
+ */
557
+ in?: unknown[] | undefined;
558
+ /**
559
+ * None of these values.
560
+ */
561
+ notIn?: unknown[] | undefined;
562
+ /**
563
+ * A `LIKE` pattern: `%` any text, `_` one character.
564
+ */
565
+ like?: string | undefined;
566
+ notLike?: string | undefined;
567
+ /**
568
+ * A case-sensitive pattern: `*` any text, `?` one character.
569
+ */
570
+ glob?: string | undefined;
571
+ /**
572
+ * From `min` to `max`, both included.
573
+ */
574
+ between?: [unknown, unknown] | undefined;
575
+ /**
576
+ * `true` for `IS NULL`, `false` for `IS NOT NULL`.
577
+ */
578
+ isNull?: boolean | undefined;
579
+ };
580
+ /**
581
+ * A filter. Each key is a column: a value means "equals", `null` means
582
+ * `IS NULL`, an array means "one of", and an object uses operators. Keys
583
+ * combine with AND; use `$or` and `$and` for the rest.
584
+ */
585
+ type Where<R = Record<string, any>> = { [K in keyof R]?: WhereValue; } & {
586
+ $or?: Where<R>[];
587
+ $and?: Where<R>[];
588
+ };
589
+ /**
590
+ * The condition for one column: a value, `null`, an array, or operators.
591
+ */
592
+ type WhereValue = null | string | number | bigint | boolean | Date | Uint8Array | readonly unknown[] | WhereOperators;
593
+ /**
594
+ * Sort direction.
595
+ */
596
+ type SortDirection = "ASC" | "DESC" | "asc" | "desc";
597
+ /**
598
+ * Options for `getAll()`.
599
+ */
600
+ type QueryOptions = {
601
+ /**
602
+ * Like `"created_at"` or `[["age", "DESC"], "name"]`.
603
+ */
604
+ orderBy?: string | (string | [string, SortDirection])[] | undefined;
605
+ /**
606
+ * For a single `orderBy` column. Defaults to `"ASC"`.
607
+ */
608
+ direction?: SortDirection | undefined;
609
+ limit?: number | undefined;
610
+ /**
611
+ * Rows to skip, for pagination.
612
+ */
613
+ offset?: number | undefined;
614
+ /**
615
+ * Columns to return. Defaults to all.
616
+ */
617
+ columns?: string[] | undefined;
618
+ };
619
+ /**
620
+ * Options for `new SQLite()`.
621
+ */
622
+ type SQLiteOptions = {
623
+ /**
624
+ * Parse `JSON` columns. Defaults to `true`.
625
+ */
626
+ json?: boolean | undefined;
627
+ /**
628
+ * Return `BOOLEAN` columns as `true`/`false`. Defaults to `true`.
629
+ */
630
+ booleans?: boolean | undefined;
631
+ /**
632
+ * Return `DATE`, `DATETIME` and `TIMESTAMP` columns as `Date`s.
633
+ */
634
+ dates?: boolean | undefined;
635
+ /**
636
+ * Write-ahead logging: faster, and reads don't wait for writes. Defaults to `true`.
637
+ */
638
+ wal?: boolean | undefined;
639
+ /**
640
+ * Enforce foreign keys. Defaults to `true`.
641
+ */
642
+ foreignKeys?: boolean | undefined;
643
+ /**
644
+ * How long to wait for a lock held by another connection, in ms. Defaults to `5000`.
645
+ */
646
+ busyTimeout?: number | undefined;
647
+ readOnly?: boolean | undefined;
648
+ };
649
+ /**
650
+ * What an insert returns.
651
+ */
652
+ type InsertResult = {
653
+ ok: true;
654
+ changes: number;
655
+ lastInsertRowid: number | bigint;
656
+ };
657
+ /**
658
+ * What an update or delete returns.
659
+ */
660
+ type ChangeResult = {
661
+ ok: true;
662
+ changes: number;
663
+ };
664
+ /**
665
+ * A column, as returned by `columns()`.
666
+ */
667
+ type ColumnInfo = {
668
+ name: string;
669
+ /**
670
+ * `""` when there's none.
671
+ */
672
+ type: string;
673
+ notNull: boolean;
674
+ /**
675
+ * As SQL text, or `null`.
676
+ */
677
+ defaultValue: unknown;
678
+ primaryKey: boolean;
679
+ };
680
+ /**
681
+ * A migration: SQL (several statements are fine), or a function that gets
682
+ * the database.
683
+ */
684
+ type Migration = string | ((db: SQLite<any>) => void);
685
+ /**
686
+ * Shortcuts bound to one table, from `table()`.
687
+ */
688
+ type TableHandle<R = Record<string, any>> = {
689
+ name: string;
690
+ /**
691
+ * The first matching row.
692
+ */
693
+ get: (where?: Where<R>, options?: Pick<QueryOptions, "columns" | "orderBy" | "direction">) => R | undefined;
694
+ /**
695
+ * All matching rows.
696
+ */
697
+ getAll: (where?: Where<R>, options?: QueryOptions) => R[];
698
+ /**
699
+ * Inserts a row.
700
+ */
701
+ insert: (data: Partial<R>) => InsertResult;
702
+ /**
703
+ * Inserts rows in one transaction.
704
+ */
705
+ insertMany: (rows: Array<Partial<R>>) => ChangeResult;
706
+ /**
707
+ * Updates matching rows.
708
+ */
709
+ update: (data: Partial<R>, where: Where<R>) => ChangeResult;
710
+ /**
711
+ * Inserts, or updates on conflict.
712
+ */
713
+ upsert: (data: Partial<R>, conflict: string[]) => InsertResult;
714
+ /**
715
+ * Deletes matching rows.
716
+ */
717
+ delete: (where: Where<R>) => ChangeResult;
718
+ /**
719
+ * Counts matching rows.
720
+ */
721
+ count: (where?: Where<R>) => number;
722
+ /**
723
+ * Does any row match?
724
+ */
725
+ exists: (where?: Where<R>) => boolean;
726
+ /**
727
+ * Deletes every row.
728
+ */
729
+ clear: () => {
730
+ ok: true;
731
+ };
732
+ };
396
733
  //# sourceMappingURL=SQLite.d.ts.map