utilitas 2001.1.136 → 2001.1.139

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/lib/dbio.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  import {
2
- assertSet, checkChance, ensureArray, ensureString, ignoreErrFunc,
2
+ assertSet, checkChance, ensureArray, ensureString,
3
3
  log as _log, need, throwError,
4
4
  } from './utilitas.mjs';
5
5
 
6
6
  import { isPrimary } from './callosum.mjs';
7
+ import { getTempPath } from './storage.mjs';
7
8
 
8
9
  const _NEED = ['mysql2', 'pg'];
9
10
  const [mysqlDefaultPort, postgresqlDefaultPort] = [3306, 5432];
10
- const [mysqlQuote, postgresqlQuote] = ['`', ''];
11
+ const [mysqlQuote, postgresqlQuote, sqliteQuote] = ['`', '', '"'];
11
12
  const IFSC = 'information_schema';
12
13
  const orders = { '+': 'ASC', ASC: 'ASC', '-': 'DESC', DESC: 'DESC', VECTOR: ' ' };
13
14
  const [INSERT, UPDATE, RETURNING] = ['INSERT', 'UPDATE', 'RETURNING'];
@@ -21,7 +22,7 @@ const log = content => _log(content, import.meta.url);
21
22
  const defaultKey = options => options && options.key ? options.key : fieldId;
22
23
  const queryOne = async (...args) => (await query(...args))[0];
23
24
  const assertForce = op => assert(op?.force, "Option 'force' is required.", 500);
24
- const [MYSQL, POSTGRESQL] = ['MYSQL', 'POSTGRESQL'];
25
+ const [MYSQL, POSTGRESQL, SQLITE] = ['MYSQL', 'POSTGRESQL', 'SQLITE'];
25
26
  const quote = name => `${bracket}${name}${bracket}`;
26
27
  const join = elements => elements.join(', ');
27
28
  const assembleDelete = table => `DELETE FROM ${quote(assertTable(table))}`;
@@ -32,12 +33,255 @@ const cleanSql = sql => sql.replace(/\s+/g, ' ');
32
33
 
33
34
  let provider, pool, actExecute, bracket, sqlShowTables, placeholder, sqlDesc,
34
35
  sqlShowIndexes, fieldCountResult, doublePlaceholder, pgvector,
35
- pgCheckedClients = new WeakSet();
36
+ pgCheckedClients = new WeakSet(), workerDb = null,
37
+ sqliteModule = null, workerThreadsModule = null;
38
+
39
+ const getSqliteModule = async () =>
40
+ sqliteModule || (sqliteModule = await need('node:sqlite', { raw: true }));
41
+
42
+ const getWorkerThreadsModule = async () => workerThreadsModule
43
+ || (workerThreadsModule = await need('node:worker_threads', { raw: true }));
44
+
45
+ const normalizeSqliteParams = (params) => {
46
+ if (params === undefined || params === null) { return []; }
47
+ if (Array.isArray(params)) { return params; }
48
+ if (typeof params === 'object') { return params; }
49
+ return [params];
50
+ };
51
+
52
+ const assembleKeyValueParams = value =>
53
+ provider === SQLITE && Array.isArray(value) ? value : [value];
54
+
55
+ const normalizeKeyValue = (key, value) => {
56
+ if (Object.isObject(key)) { key = Object.entries(key); }
57
+ return Array.isArray(key)
58
+ ? { key, value, params: key.map(x => x[1]) }
59
+ : { key, value, params: assembleKeyValueParams(value) };
60
+ };
61
+
62
+ const callSqliteStatement = (statement, method, params) => {
63
+ const normalized = normalizeSqliteParams(params);
64
+ return Array.isArray(normalized)
65
+ ? statement[method](...normalized)
66
+ : statement[method](normalized);
67
+ };
68
+
69
+ const requireWorkerDb = () => {
70
+ if (!workerDb) {
71
+ throw new Error('SQLite database has not been initialized.');
72
+ }
73
+ return workerDb;
74
+ };
75
+
76
+ const runSqliteWorkerOperation = (operation = {}) => {
77
+ const db = requireWorkerDb();
78
+ const type = String(operation.type || '').trim();
79
+ const sql = String(operation.sql || '');
80
+ if (type === 'exec') {
81
+ db.exec(sql);
82
+ return null;
83
+ }
84
+ if (type === 'run') {
85
+ const result = callSqliteStatement(
86
+ db.prepare(sql), type, operation.params
87
+ );
88
+ return {
89
+ affectedRows: Number(result?.changes || 0),
90
+ changes: Number(result?.changes || 0),
91
+ insertId: Number(result?.lastInsertRowid || 0),
92
+ lastInsertRowid: Number(result?.lastInsertRowid || 0),
93
+ };
94
+ }
95
+ if (type === 'get' || type === 'all') {
96
+ return callSqliteStatement(db.prepare(sql), type, operation.params);
97
+ }
98
+ throw new Error(`Unknown SQLite operation: ${type}`);
99
+ };
100
+
101
+ const getDefaultSqlitePath = () => getTempPath({
102
+ sub: `utilitas-dbio-${Date.now()}-${Math.random()
103
+ .toString(36).slice(2)}.sqlite`,
104
+ });
105
+
106
+ const handleSqliteWorkerCall = async (message = {}, context = {}) => {
107
+ const id = message.id;
108
+ const { path, parentPort } = context;
109
+ try {
110
+ const method = String(message.method || '').trim();
111
+ if (method === 'open') {
112
+ if (!workerDb) {
113
+ const { DatabaseSync } = await getSqliteModule();
114
+ workerDb = new DatabaseSync(path);
115
+ }
116
+ parentPort.postMessage({ id, ok: true, result: null });
117
+ return;
118
+ }
119
+ if (method === 'close') {
120
+ workerDb?.close();
121
+ workerDb = null;
122
+ parentPort.postMessage({ id, ok: true, result: null });
123
+ return;
124
+ }
125
+ if (method === 'transaction') {
126
+ const db = requireWorkerDb();
127
+ const operations = Array.isArray(message.operations)
128
+ ? message.operations : [];
129
+ db.exec('BEGIN');
130
+ try {
131
+ const result = operations.map(runSqliteWorkerOperation);
132
+ db.exec('COMMIT');
133
+ parentPort.postMessage({ id, ok: true, result });
134
+ } catch (error) {
135
+ db.exec('ROLLBACK');
136
+ throw error;
137
+ }
138
+ return;
139
+ }
140
+ const result = runSqliteWorkerOperation({
141
+ type: method,
142
+ sql: message.sql,
143
+ params: message.params,
144
+ });
145
+ parentPort.postMessage({ id, ok: true, result });
146
+ } catch (error) {
147
+ parentPort.postMessage({
148
+ id,
149
+ ok: false,
150
+ error: {
151
+ message: error?.message || String(error),
152
+ stack: error?.stack || '',
153
+ },
154
+ });
155
+ }
156
+ };
157
+
158
+ const startSqliteWorker = async (path) => {
159
+ const { parentPort } = await getWorkerThreadsModule();
160
+ parentPort.on('message', (message) => {
161
+ void handleSqliteWorkerCall(message, { path, parentPort });
162
+ });
163
+ };
164
+
165
+ class SQLiteWorkerPool {
166
+ constructor(path) {
167
+ this.path = path;
168
+ this.worker = null;
169
+ this.nextRequestId = 1;
170
+ this.pending = new Map();
171
+ this.openPromise = null;
172
+ this.closed = false;
173
+ }
174
+
175
+ async open() {
176
+ if (this.openPromise) { return await this.openPromise; }
177
+ this.openPromise = this.#call({ method: 'open' });
178
+ try {
179
+ await this.openPromise;
180
+ } finally {
181
+ this.openPromise = null;
182
+ }
183
+ }
184
+
185
+ async close() {
186
+ if (this.closed) { return; }
187
+ const worker = this.worker;
188
+ if (!worker) {
189
+ this.closed = true;
190
+ return;
191
+ }
192
+ try {
193
+ await this.#call({ method: 'close' });
194
+ } finally {
195
+ this.closed = true;
196
+ this.worker = null;
197
+ for (const { reject } of this.pending.values()) {
198
+ reject(new Error('SQLite worker closed'));
199
+ }
200
+ this.pending.clear();
201
+ await worker.terminate();
202
+ }
203
+ }
204
+
205
+ async exec(sql) { return await this.#call({ method: 'exec', sql }); }
206
+ async run(sql, params = []) {
207
+ return await this.#call({ method: 'run', sql, params });
208
+ }
209
+ async get(sql, params = []) {
210
+ return await this.#call({ method: 'get', sql, params });
211
+ }
212
+ async all(sql, params = []) {
213
+ return await this.#call({ method: 'all', sql, params });
214
+ }
215
+ async transaction(operations = []) {
216
+ return await this.#call({
217
+ method: 'transaction',
218
+ operations: Array.isArray(operations) ? operations : [],
219
+ });
220
+ }
221
+ query(...args) { return this.all(...args); }
222
+ execute(...args) { return this.run(...args); }
223
+ end() { return this.close(); }
224
+
225
+ async #ensureWorker() {
226
+ if (this.worker) { return; }
227
+ const { Worker } = await getWorkerThreadsModule();
228
+ const worker = new Worker(`
229
+ import ${JSON.stringify(new URL('./horizon.mjs', import.meta.url).href)};
230
+ import { startSqliteWorker } from ${JSON.stringify(import.meta.url)};
231
+ await startSqliteWorker(${JSON.stringify(this.path)});
232
+ `, {
233
+ eval: true,
234
+ execArgv: process.execArgv.filter(
235
+ arg => !arg.startsWith('--input-type')
236
+ ),
237
+ });
238
+ worker.on('message', (message) => {
239
+ const id = Number(message?.id || 0);
240
+ const pending = this.pending.get(id);
241
+ if (!pending) { return; }
242
+ this.pending.delete(id);
243
+ if (message.ok) {
244
+ pending.resolve(message.result);
245
+ return;
246
+ }
247
+ const error = new Error(
248
+ message?.error?.message || 'SQLite worker failed'
249
+ );
250
+ if (message?.error?.stack) { error.stack = message.error.stack; }
251
+ pending.reject(error);
252
+ });
253
+ worker.on('error', (error) => this.#rejectAll(error));
254
+ worker.on('exit', (code) => {
255
+ if (!this.closed && code !== 0) {
256
+ this.#rejectAll(new Error(`SQLite worker exited: ${code}`));
257
+ }
258
+ this.worker = null;
259
+ });
260
+ this.worker = worker;
261
+ }
262
+
263
+ async #call(payload = {}) {
264
+ if (this.closed) {
265
+ return Promise.reject(new Error('SQLite worker is closed'));
266
+ }
267
+ await this.#ensureWorker();
268
+ const id = this.nextRequestId++;
269
+ return new Promise((resolve, reject) => {
270
+ this.pending.set(id, { resolve, reject });
271
+ this.worker.postMessage({ ...payload, id });
272
+ });
273
+ }
274
+
275
+ #rejectAll(error) {
276
+ for (const { reject } of this.pending.values()) { reject(error); }
277
+ this.pending.clear();
278
+ }
279
+ }
36
280
 
37
281
  const init = async (options) => {
38
282
  if (options) {
39
283
  const _provider = ensureString(options?.provider, { case: 'UP' });
40
- let port, vector = '';
284
+ let [port, vector, uri] = [null, '', ''];
41
285
  switch (_provider) {
42
286
  case MYSQL:
43
287
  case 'MARIA':
@@ -56,6 +300,9 @@ const init = async (options) => {
56
300
  `SHOW INDEXES FROM ${doublePlaceholder}`,
57
301
  `DESC ${doublePlaceholder}`,
58
302
  ];
303
+ uri = `${_provider.toLowerCase()}://${options.user}@`
304
+ + `${options.host}:${options.port || port}/`
305
+ + `${options.database}`;
59
306
  break;
60
307
  case 'PG':
61
308
  case 'POSTGRE':
@@ -79,6 +326,30 @@ const init = async (options) => {
79
326
  await enableVector();
80
327
  vector = '(vector)';
81
328
  }
329
+ uri = `${_provider.toLowerCase()}://${options.user}@`
330
+ + `${options.host}:${options.port || port}/`
331
+ + `${options.database}`;
332
+ break;
333
+ case 'SQL':
334
+ case 'SQLITE3':
335
+ case SQLITE:
336
+ const path = options.path || getDefaultSqlitePath();
337
+ [
338
+ provider, pool, actExecute, bracket, placeholder,
339
+ fieldCountResult,
340
+ ] = [
341
+ SQLITE, new SQLiteWorkerPool(path), 'run',
342
+ sqliteQuote, '?', fieldCount,
343
+ ];
344
+ await pool.open();
345
+ doublePlaceholder = placeholder;
346
+ [sqlShowTables, sqlShowIndexes, sqlDesc] = [
347
+ 'SELECT name FROM sqlite_master WHERE type = \'table\' '
348
+ + 'AND name NOT LIKE \'sqlite_%\' ORDER BY name',
349
+ table => `PRAGMA index_list(${quote(table)})`,
350
+ table => `PRAGMA table_info(${quote(table)})`,
351
+ ];
352
+ uri = `sqlite://${path}`;
82
353
  break;
83
354
  default:
84
355
  assert(
@@ -87,17 +358,21 @@ const init = async (options) => {
87
358
  );
88
359
  }
89
360
  isPrimary && !options.silent && log(
90
- `Initialized: ${_provider.toLowerCase()}://${options.user}@`
91
- + `${options.host}:${options.port || port}/${options.database}`
92
- + `${vector}`
361
+ `Initialized: ${uri}${vector}`
93
362
  );
94
363
  }
95
364
  assert(pool, 'Database has not been initialized.');
96
365
  return pool;
97
366
  };
98
367
 
99
- const end = async (options) => {
100
- pool && pool.end();
368
+ const end = async () => {
369
+ if (pool) { await pool.end(); }
370
+ [
371
+ provider, pool, actExecute, bracket, sqlShowTables, placeholder,
372
+ sqlDesc, sqlShowIndexes, fieldCountResult, doublePlaceholder,
373
+ pgvector,
374
+ ] = [];
375
+ pgCheckedClients = new WeakSet();
101
376
  log('Terminated.');
102
377
  };
103
378
 
@@ -157,6 +432,7 @@ const handleResult = result => {
157
432
  switch (provider) {
158
433
  case MYSQL: return result[0];
159
434
  case POSTGRESQL: return result.rows;
435
+ case SQLITE: return result;
160
436
  }
161
437
  };
162
438
 
@@ -169,6 +445,7 @@ const execute = async (...args) => {
169
445
  switch (provider) {
170
446
  case MYSQL: return resp[0];
171
447
  case POSTGRESQL: return resp;
448
+ case SQLITE: return resp;
172
449
  }
173
450
  };
174
451
 
@@ -213,16 +490,22 @@ const rawAssembleKeyValue = (key, value, options) => {
213
490
  let _placeholder;
214
491
  switch (provider) {
215
492
  case MYSQL:
493
+ case SQLITE:
216
494
  _placeholder = placeholder;
217
495
  break;
218
496
  case POSTGRESQL:
219
- _placeholder = `$${options?.placeholderIndex || (~~i + 1)}`;
497
+ _placeholder = `$${options?.placeholderIndex
498
+ ? options.placeholderIndex + i : (~~i + 1)}`;
220
499
  }
221
500
  const val = options?.direct ? value : _placeholder;
222
501
  let express = `${getComparison(value) || '='} ${val}`;
223
502
  if (Array.isArray(value)) {
224
503
  assert(value.length, 'Invalid array value.', 500);
225
- express = `IN (${val})`;
504
+ if (provider === SQLITE) {
505
+ express = `IN (${value.map(() => placeholder).join(', ')})`;
506
+ } else {
507
+ express = `IN (${val})`;
508
+ }
226
509
  } else if (value === null) { express = `IS ${val}`; }
227
510
  conditions.push(`${quote(keys[i])} ${express}`);
228
511
  });
@@ -248,6 +531,7 @@ const assembleSet = (data, options) => {
248
531
  keys.push(k);
249
532
  switch (provider) {
250
533
  case MYSQL:
534
+ case SQLITE:
251
535
  vals.push('?');
252
536
  pairs.push(`${quote(k)} = ?`);
253
537
  break;
@@ -258,19 +542,36 @@ const assembleSet = (data, options) => {
258
542
  pushValue(values, item[k]);
259
543
  }
260
544
  if (options?.upsert) {
261
- for (let k in item) {
262
- switch (provider) {
263
- case MYSQL: dupSql.push(`${quote(k)} = ?`); break;
264
- case POSTGRESQL: dupSql.push(`${quote(k)} = $${++i}`); break;
265
- }
266
- pushValue(values, item[k]);
545
+ switch (provider) {
546
+ case MYSQL:
547
+ case POSTGRESQL:
548
+ for (let k in item) {
549
+ switch (provider) {
550
+ case MYSQL:
551
+ dupSql.push(`${quote(k)} = ?`);
552
+ break;
553
+ case POSTGRESQL:
554
+ dupSql.push(`${quote(k)} = $${++i}`);
555
+ break;
556
+ }
557
+ pushValue(values, item[k]);
558
+ }
559
+ break;
560
+ case SQLITE:
561
+ for (let k in item) {
562
+ dupSql.push(`${quote(k)} = excluded.${quote(k)}`);
563
+ }
564
+ break;
267
565
  }
268
566
  switch (provider) {
269
567
  case MYSQL:
270
568
  dupSql = ` ON DUPLICATE KEY UPDATE ${join(dupSql)}`;
271
569
  break;
272
570
  case POSTGRESQL:
273
- dupSql = ` ON CONFLICT (${defaultKey(options)}) DO UPDATE SET ${join(dupSql)}`;
571
+ case SQLITE:
572
+ dupSql = ' ON CONFLICT '
573
+ + `(${quote(defaultKey(options))}) DO UPDATE SET `
574
+ + join(dupSql);
274
575
  break;
275
576
  }
276
577
  } else { dupSql = ''; }
@@ -318,28 +619,36 @@ const tables = async (options) => {
318
619
  switch (provider) {
319
620
  case MYSQL: return Object.values(x)[0];
320
621
  case POSTGRESQL: return x.table_name;
622
+ case SQLITE: return x.name;
321
623
  }
322
624
  });
323
625
  };
324
626
 
325
627
  const desc = async (table, options) => {
326
628
  assertTable(table);
327
- const [resp, result] = [await query(sqlDesc, [table]), {}];
629
+ const sql = typeof sqlDesc === 'function' ? sqlDesc(table) : sqlDesc;
630
+ const params = typeof sqlDesc === 'function' ? [] : [table];
631
+ const [resp, result] = [await query(sql, params), {}];
328
632
  if (options?.raw) { return resp; }
329
633
  switch (provider) {
330
634
  case MYSQL: resp.map(x => result[x.Field] = x); break;
331
635
  case POSTGRESQL: resp.map(x => result[x.column_name] = x); break;
636
+ case SQLITE: resp.map(x => result[x.name] = x); break;
332
637
  }
333
638
  return result;
334
639
  };
335
640
 
336
641
  const indexes = async (table, options) => {
337
642
  assertTable(table);
338
- const [resp, result] = [await query(sqlShowIndexes, [table]), {}];
643
+ const sql = typeof sqlShowIndexes === 'function'
644
+ ? sqlShowIndexes(table) : sqlShowIndexes;
645
+ const params = typeof sqlShowIndexes === 'function' ? [] : [table];
646
+ const [resp, result] = [await query(sql, params), {}];
339
647
  if (options?.raw) { return resp; }
340
648
  switch (provider) {
341
649
  case MYSQL: resp.map(x => result[x.Key_name] = x); break;
342
650
  case POSTGRESQL: resp.map(x => result[x.indexname] = x); break;
651
+ case SQLITE: resp.map(x => result[x.name] = x); break;
343
652
  }
344
653
  return result;
345
654
  };
@@ -350,6 +659,7 @@ const drop = async (table, options) => {
350
659
  const act = {
351
660
  [MYSQL]: [query, [`DROP TABLE IF EXISTS ${doublePlaceholder}`, [table]]],
352
661
  [POSTGRESQL]: [execute, [`DROP TABLE IF EXISTS ${table}`]],
662
+ [SQLITE]: [execute, [`DROP TABLE IF EXISTS ${quote(table)}`]],
353
663
  }[provider]
354
664
  return await act[0](...act[1]);
355
665
  };
@@ -358,11 +668,11 @@ const queryAll = (table, options) =>
358
668
  query(`${assembleQuery(table, options)}${assembleTail(options)}`);
359
669
 
360
670
  const queryByKeyValue = async (table, key, value, options) => {
361
- Object.isObject(key) && (key = Object.entries(key));
671
+ const where = normalizeKeyValue(key, value);
362
672
  const sql = assembleQuery(table, options)
363
- + assembleKeyValue(key, value)
673
+ + assembleKeyValue(where.key, where.value)
364
674
  + assembleTail(options);
365
- const resp = await query(sql, Array.isArray(key) ? key.map(x => x[1]) : [value]);
675
+ const resp = await query(sql, where.params);
366
676
  return options?.unique ? (resp && resp.length ? resp[0] : null) : resp;
367
677
  };
368
678
 
@@ -387,7 +697,9 @@ const insert = async (table, fields, options) => {
387
697
  await vacuum(table, { auto: true, ...options || {} });
388
698
  }
389
699
  resp.key = key;
390
- !resp.insertId && item.object[key]
700
+ key !== fieldId && item.object[key]
701
+ ? (resp.insertId = item.object[key])
702
+ : !resp.insertId && item.object[key]
391
703
  && (resp.insertId = item.object[key]);
392
704
  result.push(resp);
393
705
  ids.push(resp.insertId);
@@ -396,6 +708,7 @@ const insert = async (table, fields, options) => {
396
708
  if (!options?.skipEcho && ids.length) {
397
709
  switch (provider) {
398
710
  case MYSQL:
711
+ case SQLITE:
399
712
  result = await queryById(table, ids, options);
400
713
  break;
401
714
  case POSTGRESQL:
@@ -418,24 +731,29 @@ const countAll = async (table) => {
418
731
  };
419
732
 
420
733
  const countByKeyValue = async (table, key, value) => {
734
+ const where = normalizeKeyValue(key, value);
421
735
  const sql = assembleQuery(table, { fields: fieldCount })
422
- + assembleKeyValue(key, value);
423
- return (await query(sql, [value]))[0][fieldCountResult];
736
+ + assembleKeyValue(where.key, where.value);
737
+ return (await query(sql, where.params))[0][fieldCountResult];
424
738
  };
425
739
 
426
740
  const updateByKeyValue = async (table, key, value, fields, options) => {
427
741
  assertTable(table);
742
+ const where = normalizeKeyValue(key, value);
428
743
  const dfKey = defaultKey(options);
429
- const subfix = assembleKeyValue(key, value, {
744
+ const subfix = assembleKeyValue(where.key, where.value, {
430
745
  placeholderIndex: Object.keys(fields).length + 1,
431
746
  }) + (provider === POSTGRESQL ? (options?.skipEcho ? (
432
747
  options?.key ? ` ${RETURNING} ${dfKey}` : ''
433
748
  ) : RETURNING_ALL) : '');
434
749
  let { sql, values } = assembleUpdate(table, fields, { subfix });
435
750
  sql += assembleTail(options);
436
- const resp = await query(sql, [...values, value]);
437
- return !options?.skipEcho && provider === MYSQL
438
- ? await queryByKeyValue(table, key, value, options) : resp;
751
+ const params = [...values, ...where.params];
752
+ const resp = provider === POSTGRESQL
753
+ ? await query(sql, params)
754
+ : await execute(sql, params);
755
+ return !options?.skipEcho && [MYSQL, SQLITE].includes(provider)
756
+ ? await queryByKeyValue(table, where.key, where.value, options) : resp;
439
757
  };
440
758
 
441
759
  const updateById = async (table, id, fields, options) => {
@@ -446,12 +764,13 @@ const updateById = async (table, id, fields, options) => {
446
764
  };
447
765
 
448
766
  const deleteByKeyValue = async (table, key, value, options) => {
767
+ const where = normalizeKeyValue(key, value);
449
768
  const sql = assembleDelete(table)
450
- + assembleKeyValue(key, value)
769
+ + assembleKeyValue(where.key, where.value)
451
770
  + assembleTail(options);
452
771
  return await {
453
- [MYSQL]: query, [POSTGRESQL]: execByQuery,
454
- }[provider](sql, [value]);
772
+ [MYSQL]: query, [POSTGRESQL]: execByQuery, [SQLITE]: execute,
773
+ }[provider](sql, where.params);
455
774
  };
456
775
 
457
776
  const deleteById = async (table, id, options) =>
@@ -480,6 +799,7 @@ export {
480
799
  _NEED,
481
800
  MYSQL,
482
801
  POSTGRESQL,
802
+ SQLITE,
483
803
  assembleInsert,
484
804
  assembleQuery,
485
805
  assembleSet,
@@ -510,6 +830,7 @@ export {
510
830
  rawAssembleKeyValue,
511
831
  rawExecute,
512
832
  rawQuery,
833
+ startSqliteWorker,
513
834
  tables,
514
835
  updateById,
515
836
  updateByKeyValue,
package/lib/manifest.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  const manifest = {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "2001.1.136",
4
+ "version": "2001.1.139",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
8
8
  "type": "module",
9
9
  "engines": {
10
- "node": ">=20.x"
10
+ "node": ">=22.5.0"
11
11
  },
12
12
  "author": "Leask Wong <i@leaskh.com>",
13
13
  "license": "MIT",
@@ -19,19 +19,19 @@ const manifest = {
19
19
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz"
20
20
  },
21
21
  "dependencies": {
22
- "file-type": "^21.3.3",
23
- "mathjs": "^15.1.1",
24
- "uuid": "^13.0.0"
22
+ "file-type": "^22.0.1",
23
+ "mathjs": "^15.2.0",
24
+ "uuid": "^14.0.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
28
28
  "@ffprobe-installer/ffprobe": "^2.1.2",
29
- "@google-cloud/discoveryengine": "^2.5.3",
29
+ "@google-cloud/discoveryengine": "^2.6.0",
30
30
  "@google-cloud/storage": "^7.19.0",
31
- "@google/genai": "^1.46.0",
31
+ "@google/genai": "^1.51.0",
32
32
  "@mozilla/readability": "github:mozilla/readability",
33
- "@sentry/node": "^10.44.0",
34
- "@sentry/profiling-node": "^10.44.0",
33
+ "@sentry/node": "^10.51.0",
34
+ "@sentry/profiling-node": "^10.51.0",
35
35
  "acme-client": "^5.4.0",
36
36
  "browserify-fs": "^1.0.0",
37
37
  "buffer": "^6.0.3",
@@ -40,35 +40,35 @@ const manifest = {
40
40
  "fluent-ffmpeg": "^2.1.3",
41
41
  "form-data": "^4.0.5",
42
42
  "google-gax": "^5.0.6",
43
- "ioredis": "^5.10.0",
44
- "jsdom": "^29.0.0",
43
+ "ioredis": "^5.10.1",
44
+ "jsdom": "^29.1.0",
45
45
  "lorem-ipsum": "^2.0.8",
46
- "mailgun.js": "^12.7.1",
47
- "mailparser": "^3.9.4",
46
+ "mailgun.js": "^13.0.0",
47
+ "mailparser": "^3.9.8",
48
48
  "mime": "^4.1.0",
49
- "mysql2": "^3.20.0",
49
+ "mysql2": "^3.22.3",
50
50
  "node-mailjet": "^6.0.11",
51
51
  "node-polyfill-webpack-plugin": "^4.1.0",
52
52
  "office-text-extractor": "^4.0.0",
53
- "openai": "^6.32.0",
53
+ "openai": "^6.35.0",
54
54
  "pdf-lib": "^1.17.1",
55
- "pdfjs-dist": "^5.5.207",
55
+ "pdfjs-dist": "^5.7.284",
56
56
  "pg": "^8.20.0",
57
57
  "pgvector": "^0.2.1",
58
58
  "ping": "^1.0.0",
59
59
  "process": "^0.11.10",
60
- "puppeteer": "^24.39.1",
60
+ "puppeteer": "^24.42.0",
61
61
  "say": "^0.16.0",
62
62
  "telegraf": "^4.16.3",
63
63
  "telesignsdk": "^5.0.0",
64
64
  "tesseract.js": "^7.0.0",
65
- "twilio": "^5.13.0",
65
+ "twilio": "^6.0.0",
66
66
  "url": "github:Leask/node-url",
67
67
  "webpack-cli": "^7.0.2",
68
68
  "whisper-node": "^1.1.1",
69
- "wrangler": "^4.75.0",
69
+ "wrangler": "^4.86.0",
70
70
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz",
71
- "youtube-transcript": "^1.3.0"
71
+ "youtube-transcript": "^1.3.1"
72
72
  }
73
73
  };
74
74