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/README.md +10 -8
- package/dist/utilitas.lite.mjs +1 -1
- package/dist/utilitas.lite.mjs.map +1 -1
- package/lib/alan.mjs +169 -301
- package/lib/dbio.mjs +355 -34
- package/lib/manifest.mjs +20 -20
- package/lib/rag.mjs +85 -43
- package/lib/storage.mjs +4 -2
- package/package.json +20 -20
package/lib/dbio.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
assertSet, checkChance, ensureArray, ensureString,
|
|
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: ${
|
|
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 (
|
|
100
|
-
pool
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
437
|
-
|
|
438
|
-
? await
|
|
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,
|
|
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.
|
|
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": ">=
|
|
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": "^
|
|
23
|
-
"mathjs": "^15.
|
|
24
|
-
"uuid": "^
|
|
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.
|
|
29
|
+
"@google-cloud/discoveryengine": "^2.6.0",
|
|
30
30
|
"@google-cloud/storage": "^7.19.0",
|
|
31
|
-
"@google/genai": "^1.
|
|
31
|
+
"@google/genai": "^1.51.0",
|
|
32
32
|
"@mozilla/readability": "github:mozilla/readability",
|
|
33
|
-
"@sentry/node": "^10.
|
|
34
|
-
"@sentry/profiling-node": "^10.
|
|
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.
|
|
44
|
-
"jsdom": "^29.
|
|
43
|
+
"ioredis": "^5.10.1",
|
|
44
|
+
"jsdom": "^29.1.0",
|
|
45
45
|
"lorem-ipsum": "^2.0.8",
|
|
46
|
-
"mailgun.js": "^
|
|
47
|
-
"mailparser": "^3.9.
|
|
46
|
+
"mailgun.js": "^13.0.0",
|
|
47
|
+
"mailparser": "^3.9.8",
|
|
48
48
|
"mime": "^4.1.0",
|
|
49
|
-
"mysql2": "^3.
|
|
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.
|
|
53
|
+
"openai": "^6.35.0",
|
|
54
54
|
"pdf-lib": "^1.17.1",
|
|
55
|
-
"pdfjs-dist": "^5.
|
|
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.
|
|
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": "^
|
|
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.
|
|
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.
|
|
71
|
+
"youtube-transcript": "^1.3.1"
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
74
|
|