@pineliner/odb-client 1.0.6 → 1.0.7
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/dist/core/http-client.d.ts.map +1 -1
- package/dist/database/adapters/bun-sqlite.d.ts.map +1 -1
- package/dist/database/adapters/libsql.d.ts.map +1 -1
- package/dist/database/adapters/odblite.d.ts +2 -1
- package/dist/database/adapters/odblite.d.ts.map +1 -1
- package/dist/database/index.d.ts +2 -0
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/sql-template.d.ts +432 -0
- package/dist/database/sql-template.d.ts.map +1 -0
- package/dist/database/sql-template.examples.d.ts +28 -0
- package/dist/database/sql-template.examples.d.ts.map +1 -0
- package/dist/database/types.d.ts +8 -1
- package/dist/database/types.d.ts.map +1 -1
- package/dist/index.cjs +1861 -1669
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +779 -653
- package/dist/orm/index.d.ts +228 -0
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/http-client.ts +1 -0
- package/src/database/adapters/bun-sqlite.ts +73 -15
- package/src/database/adapters/libsql.ts +73 -15
- package/src/database/adapters/odblite.ts +87 -23
- package/src/database/index.ts +4 -0
- package/src/database/sql-template.examples.ts +363 -0
- package/src/database/sql-template.ts +660 -0
- package/src/database/types.ts +15 -3
- package/src/index.ts +31 -0
- package/src/orm/index.ts +538 -0
- package/src/types.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,367 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { createClient } from "@libsql/client";
|
|
3
|
+
import node_fs from "node:fs";
|
|
4
|
+
var __webpack_modules__ = {
|
|
5
|
+
"./src/orm/index.ts": function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
6
|
+
__webpack_require__.r(__webpack_exports__);
|
|
7
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
8
|
+
ORM: ()=>ORM,
|
|
9
|
+
and: ()=>and,
|
|
10
|
+
createORM: ()=>createORM,
|
|
11
|
+
eq: ()=>eq,
|
|
12
|
+
gt: ()=>gt,
|
|
13
|
+
gte: ()=>gte,
|
|
14
|
+
inArray: ()=>inArray,
|
|
15
|
+
isNotNull: ()=>isNotNull,
|
|
16
|
+
isNull: ()=>isNull,
|
|
17
|
+
like: ()=>like,
|
|
18
|
+
lt: ()=>lt,
|
|
19
|
+
lte: ()=>lte,
|
|
20
|
+
ne: ()=>ne,
|
|
21
|
+
or: ()=>or
|
|
22
|
+
});
|
|
23
|
+
function eq(field, value) {
|
|
24
|
+
return {
|
|
25
|
+
sql: `${field} = ?`,
|
|
26
|
+
params: [
|
|
27
|
+
value
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function ne(field, value) {
|
|
32
|
+
return {
|
|
33
|
+
sql: `${field} != ?`,
|
|
34
|
+
params: [
|
|
35
|
+
value
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function gt(field, value) {
|
|
40
|
+
return {
|
|
41
|
+
sql: `${field} > ?`,
|
|
42
|
+
params: [
|
|
43
|
+
value
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function gte(field, value) {
|
|
48
|
+
return {
|
|
49
|
+
sql: `${field} >= ?`,
|
|
50
|
+
params: [
|
|
51
|
+
value
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function lt(field, value) {
|
|
56
|
+
return {
|
|
57
|
+
sql: `${field} < ?`,
|
|
58
|
+
params: [
|
|
59
|
+
value
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function lte(field, value) {
|
|
64
|
+
return {
|
|
65
|
+
sql: `${field} <= ?`,
|
|
66
|
+
params: [
|
|
67
|
+
value
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function like(field, pattern) {
|
|
72
|
+
return {
|
|
73
|
+
sql: `${field} LIKE ?`,
|
|
74
|
+
params: [
|
|
75
|
+
pattern
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function inArray(field, values) {
|
|
80
|
+
const placeholders = values.map(()=>'?').join(', ');
|
|
81
|
+
return {
|
|
82
|
+
sql: `${field} IN (${placeholders})`,
|
|
83
|
+
params: values
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function isNull(field) {
|
|
87
|
+
return {
|
|
88
|
+
sql: `${field} IS NULL`,
|
|
89
|
+
params: []
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function isNotNull(field) {
|
|
93
|
+
return {
|
|
94
|
+
sql: `${field} IS NOT NULL`,
|
|
95
|
+
params: []
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function and(...conditions) {
|
|
99
|
+
const sql = conditions.map((c)=>`(${c.sql})`).join(' AND ');
|
|
100
|
+
const params = conditions.flatMap((c)=>c.params);
|
|
101
|
+
return {
|
|
102
|
+
sql,
|
|
103
|
+
params
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function or(...conditions) {
|
|
107
|
+
const sql = conditions.map((c)=>`(${c.sql})`).join(' OR ');
|
|
108
|
+
const params = conditions.flatMap((c)=>c.params);
|
|
109
|
+
return {
|
|
110
|
+
sql,
|
|
111
|
+
params
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
class SelectBuilder {
|
|
115
|
+
db;
|
|
116
|
+
tableName;
|
|
117
|
+
selectFields = [
|
|
118
|
+
'*'
|
|
119
|
+
];
|
|
120
|
+
whereConditions = [];
|
|
121
|
+
orderByField;
|
|
122
|
+
orderByDir = 'ASC';
|
|
123
|
+
limitValue;
|
|
124
|
+
offsetValue;
|
|
125
|
+
constructor(db, tableName){
|
|
126
|
+
this.db = db;
|
|
127
|
+
this.tableName = tableName;
|
|
128
|
+
}
|
|
129
|
+
select(fields = {}) {
|
|
130
|
+
if (0 === Object.keys(fields).length) this.selectFields = [
|
|
131
|
+
'*'
|
|
132
|
+
];
|
|
133
|
+
else this.selectFields = Object.keys(fields);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
where(condition) {
|
|
137
|
+
this.whereConditions.push(condition);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
orderBy(field, direction = 'ASC') {
|
|
141
|
+
this.orderByField = field;
|
|
142
|
+
this.orderByDir = direction;
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
limit(value) {
|
|
146
|
+
this.limitValue = value;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
offset(value) {
|
|
150
|
+
this.offsetValue = value;
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
async execute() {
|
|
154
|
+
const fields = this.selectFields.join(', ');
|
|
155
|
+
const params = [];
|
|
156
|
+
let sql = `SELECT ${fields} FROM ${this.tableName}`;
|
|
157
|
+
if (this.whereConditions.length > 0) {
|
|
158
|
+
const combinedCondition = 1 === this.whereConditions.length ? this.whereConditions[0] : and(...this.whereConditions);
|
|
159
|
+
sql += ` WHERE ${combinedCondition.sql}`;
|
|
160
|
+
params.push(...combinedCondition.params);
|
|
161
|
+
}
|
|
162
|
+
if (this.orderByField) sql += ` ORDER BY ${this.orderByField} ${this.orderByDir}`;
|
|
163
|
+
if (void 0 !== this.limitValue) {
|
|
164
|
+
sql += " LIMIT ?";
|
|
165
|
+
params.push(this.limitValue);
|
|
166
|
+
}
|
|
167
|
+
if (void 0 !== this.offsetValue) {
|
|
168
|
+
sql += " OFFSET ?";
|
|
169
|
+
params.push(this.offsetValue);
|
|
170
|
+
}
|
|
171
|
+
const result = await this.db.execute(sql, params);
|
|
172
|
+
return result.rows;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
class InsertBuilder {
|
|
176
|
+
db;
|
|
177
|
+
tableName;
|
|
178
|
+
insertValues = {};
|
|
179
|
+
shouldReturn = false;
|
|
180
|
+
constructor(db, tableName){
|
|
181
|
+
this.db = db;
|
|
182
|
+
this.tableName = tableName;
|
|
183
|
+
}
|
|
184
|
+
values(data) {
|
|
185
|
+
this.insertValues = data;
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
returning() {
|
|
189
|
+
this.shouldReturn = true;
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
async execute() {
|
|
193
|
+
const fields = Object.keys(this.insertValues);
|
|
194
|
+
const placeholders = fields.map(()=>'?').join(', ');
|
|
195
|
+
const values = fields.map((f)=>this.insertValues[f]);
|
|
196
|
+
let sql = `
|
|
197
|
+
INSERT INTO ${this.tableName} (${fields.join(', ')})
|
|
198
|
+
VALUES (${placeholders})
|
|
199
|
+
`;
|
|
200
|
+
await this.db.execute(sql, values);
|
|
201
|
+
if (this.shouldReturn) {
|
|
202
|
+
const selectSql = `SELECT * FROM ${this.tableName} ORDER BY id DESC LIMIT 1`;
|
|
203
|
+
const result = await this.db.execute(selectSql, []);
|
|
204
|
+
return result.rows;
|
|
205
|
+
}
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
class UpdateBuilder {
|
|
210
|
+
db;
|
|
211
|
+
tableName;
|
|
212
|
+
updateValues = {};
|
|
213
|
+
whereConditions = [];
|
|
214
|
+
shouldReturn = false;
|
|
215
|
+
constructor(db, tableName){
|
|
216
|
+
this.db = db;
|
|
217
|
+
this.tableName = tableName;
|
|
218
|
+
}
|
|
219
|
+
set(data) {
|
|
220
|
+
this.updateValues = data;
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
where(condition) {
|
|
224
|
+
this.whereConditions.push(condition);
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
returning() {
|
|
228
|
+
this.shouldReturn = true;
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
async execute() {
|
|
232
|
+
const fields = Object.keys(this.updateValues);
|
|
233
|
+
const setClause = fields.map((f)=>`${f} = ?`).join(', ');
|
|
234
|
+
const values = fields.map((f)=>this.updateValues[f]);
|
|
235
|
+
let sql = `UPDATE ${this.tableName} SET ${setClause}`;
|
|
236
|
+
const params = [
|
|
237
|
+
...values
|
|
238
|
+
];
|
|
239
|
+
if (this.whereConditions.length > 0) {
|
|
240
|
+
const combinedCondition = 1 === this.whereConditions.length ? this.whereConditions[0] : and(...this.whereConditions);
|
|
241
|
+
sql += ` WHERE ${combinedCondition.sql}`;
|
|
242
|
+
params.push(...combinedCondition.params);
|
|
243
|
+
}
|
|
244
|
+
await this.db.execute(sql, params);
|
|
245
|
+
if (this.shouldReturn && this.whereConditions.length > 0) {
|
|
246
|
+
const combinedCondition = 1 === this.whereConditions.length ? this.whereConditions[0] : and(...this.whereConditions);
|
|
247
|
+
const selectSql = `SELECT * FROM ${this.tableName} WHERE ${combinedCondition.sql}`;
|
|
248
|
+
const result = await this.db.execute(selectSql, combinedCondition.params);
|
|
249
|
+
return result.rows;
|
|
250
|
+
}
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
class DeleteBuilder {
|
|
255
|
+
db;
|
|
256
|
+
tableName;
|
|
257
|
+
whereConditions = [];
|
|
258
|
+
constructor(db, tableName){
|
|
259
|
+
this.db = db;
|
|
260
|
+
this.tableName = tableName;
|
|
261
|
+
}
|
|
262
|
+
where(condition) {
|
|
263
|
+
this.whereConditions.push(condition);
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
async execute() {
|
|
267
|
+
let sql = `DELETE FROM ${this.tableName}`;
|
|
268
|
+
const params = [];
|
|
269
|
+
if (this.whereConditions.length > 0) {
|
|
270
|
+
const combinedCondition = 1 === this.whereConditions.length ? this.whereConditions[0] : and(...this.whereConditions);
|
|
271
|
+
sql += ` WHERE ${combinedCondition.sql}`;
|
|
272
|
+
params.push(...combinedCondition.params);
|
|
273
|
+
}
|
|
274
|
+
await this.db.execute(sql, params);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
class TableQueryBuilder {
|
|
278
|
+
db;
|
|
279
|
+
tableName;
|
|
280
|
+
constructor(db, tableName){
|
|
281
|
+
this.db = db;
|
|
282
|
+
this.tableName = tableName;
|
|
283
|
+
}
|
|
284
|
+
select(fields) {
|
|
285
|
+
const builder = new SelectBuilder(this.db, this.tableName);
|
|
286
|
+
if (fields) builder.select(fields);
|
|
287
|
+
return builder;
|
|
288
|
+
}
|
|
289
|
+
insert() {
|
|
290
|
+
return new InsertBuilder(this.db, this.tableName);
|
|
291
|
+
}
|
|
292
|
+
update() {
|
|
293
|
+
return new UpdateBuilder(this.db, this.tableName);
|
|
294
|
+
}
|
|
295
|
+
delete() {
|
|
296
|
+
return new DeleteBuilder(this.db, this.tableName);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
class ORM {
|
|
300
|
+
db;
|
|
301
|
+
constructor(db){
|
|
302
|
+
this.db = db;
|
|
303
|
+
}
|
|
304
|
+
table(tableName) {
|
|
305
|
+
return new TableQueryBuilder(this.db, tableName);
|
|
306
|
+
}
|
|
307
|
+
select(fields) {
|
|
308
|
+
return {
|
|
309
|
+
from: (tableName)=>{
|
|
310
|
+
const builder = new SelectBuilder(this.db, tableName);
|
|
311
|
+
if (fields) builder.select(fields);
|
|
312
|
+
return builder;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
insert(tableName) {
|
|
317
|
+
return new InsertBuilder(this.db, tableName);
|
|
318
|
+
}
|
|
319
|
+
update(tableName) {
|
|
320
|
+
return new UpdateBuilder(this.db, tableName);
|
|
321
|
+
}
|
|
322
|
+
delete(tableName) {
|
|
323
|
+
return new DeleteBuilder(this.db, tableName);
|
|
324
|
+
}
|
|
325
|
+
execute(sql, params) {
|
|
326
|
+
return this.db.execute(sql, params);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function createORM(db) {
|
|
330
|
+
return new ORM(db);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var __webpack_module_cache__ = {};
|
|
335
|
+
function __webpack_require__(moduleId) {
|
|
336
|
+
var cachedModule = __webpack_module_cache__[moduleId];
|
|
337
|
+
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
338
|
+
var module = __webpack_module_cache__[moduleId] = {
|
|
339
|
+
exports: {}
|
|
340
|
+
};
|
|
341
|
+
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
342
|
+
return module.exports;
|
|
343
|
+
}
|
|
344
|
+
(()=>{
|
|
345
|
+
__webpack_require__.d = (exports, definition)=>{
|
|
346
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
|
|
347
|
+
enumerable: true,
|
|
348
|
+
get: definition[key]
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
})();
|
|
352
|
+
(()=>{
|
|
353
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
354
|
+
})();
|
|
355
|
+
(()=>{
|
|
356
|
+
__webpack_require__.r = (exports)=>{
|
|
357
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, {
|
|
358
|
+
value: 'Module'
|
|
359
|
+
});
|
|
360
|
+
Object.defineProperty(exports, '__esModule', {
|
|
361
|
+
value: true
|
|
362
|
+
});
|
|
363
|
+
};
|
|
364
|
+
})();
|
|
6
365
|
class ODBLiteError extends Error {
|
|
7
366
|
code;
|
|
8
367
|
query;
|
|
@@ -26,9 +385,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
26
385
|
this.name = 'QueryError';
|
|
27
386
|
}
|
|
28
387
|
}
|
|
29
|
-
|
|
30
|
-
* HTTP client for communicating with ODBLite service
|
|
31
|
-
*/ class HTTPClient {
|
|
388
|
+
class HTTPClient {
|
|
32
389
|
config;
|
|
33
390
|
constructor(config){
|
|
34
391
|
this.config = {
|
|
@@ -36,12 +393,9 @@ class types_QueryError extends ODBLiteError {
|
|
|
36
393
|
retries: 3,
|
|
37
394
|
...config
|
|
38
395
|
};
|
|
39
|
-
// Ensure baseUrl doesn't end with slash
|
|
40
396
|
if ('string' == typeof this.config.baseUrl) this.config.baseUrl = this.config.baseUrl.replace(/\/$/, '');
|
|
41
397
|
}
|
|
42
|
-
|
|
43
|
-
* Execute a query against the ODBLite service
|
|
44
|
-
*/ async query(sql, params = []) {
|
|
398
|
+
async query(sql, params = []) {
|
|
45
399
|
if (!this.config.databaseId) throw new ConnectionError('No database ID configured. Use setDatabase() first.');
|
|
46
400
|
const url = `${this.config.baseUrl}/query/${this.config.databaseId}`;
|
|
47
401
|
const body = {
|
|
@@ -59,7 +413,6 @@ class types_QueryError extends ODBLiteError {
|
|
|
59
413
|
});
|
|
60
414
|
const data = await response.json();
|
|
61
415
|
if (!data.success) throw new types_QueryError(data.error || 'Query failed', sql, params);
|
|
62
|
-
// Handle nested data structure: { success: true, data: { rows: [...] } }
|
|
63
416
|
let rows = [];
|
|
64
417
|
if (data.data && 'object' == typeof data.data && 'rows' in data.data) rows = data.data.rows;
|
|
65
418
|
else if (Array.isArray(data.data)) rows = data.data;
|
|
@@ -67,6 +420,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
67
420
|
return {
|
|
68
421
|
rows: rows,
|
|
69
422
|
rowsAffected: data.rowsAffected || data.data?.rowsAffected || 0,
|
|
423
|
+
lastInsertRowid: data.lastInsertRowid || data.data?.lastInsertRowid,
|
|
70
424
|
executionTime: data.executionTime || data.data?.executionTime || 0,
|
|
71
425
|
databaseName: data.databaseName || data.data?.databaseName
|
|
72
426
|
};
|
|
@@ -75,9 +429,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
75
429
|
throw new types_QueryError(error instanceof Error ? error.message : 'Unknown error occurred', sql, params, error instanceof Error ? error : void 0);
|
|
76
430
|
}
|
|
77
431
|
}
|
|
78
|
-
|
|
79
|
-
* Check database health
|
|
80
|
-
*/ async ping() {
|
|
432
|
+
async ping() {
|
|
81
433
|
if (!this.config.databaseId) return false;
|
|
82
434
|
try {
|
|
83
435
|
const url = `${this.config.baseUrl}/api/db/${this.config.databaseId}/health`;
|
|
@@ -93,9 +445,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
93
445
|
return false;
|
|
94
446
|
}
|
|
95
447
|
}
|
|
96
|
-
|
|
97
|
-
* Get database information
|
|
98
|
-
*/ async getDatabaseInfo() {
|
|
448
|
+
async getDatabaseInfo() {
|
|
99
449
|
if (!this.config.databaseId) throw new ConnectionError('No database ID configured');
|
|
100
450
|
const url = `${this.config.baseUrl}/query/${this.config.databaseId}`;
|
|
101
451
|
const response = await this.makeRequest(url, {
|
|
@@ -108,14 +458,10 @@ class types_QueryError extends ODBLiteError {
|
|
|
108
458
|
if (!data.success) throw new ConnectionError(data.error || 'Failed to get database info');
|
|
109
459
|
return data;
|
|
110
460
|
}
|
|
111
|
-
|
|
112
|
-
* Set the database ID for subsequent queries
|
|
113
|
-
*/ setDatabase(databaseId) {
|
|
461
|
+
setDatabase(databaseId) {
|
|
114
462
|
this.config.databaseId = databaseId;
|
|
115
463
|
}
|
|
116
|
-
|
|
117
|
-
* Make HTTP request with retry logic
|
|
118
|
-
*/ async makeRequest(url, options) {
|
|
464
|
+
async makeRequest(url, options) {
|
|
119
465
|
let lastError;
|
|
120
466
|
for(let attempt = 1; attempt <= (this.config.retries || 3); attempt++)try {
|
|
121
467
|
const controller = new AbortController();
|
|
@@ -140,9 +486,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
140
486
|
return response;
|
|
141
487
|
} catch (error) {
|
|
142
488
|
lastError = error instanceof Error ? error : new Error('Unknown error');
|
|
143
|
-
// Don't retry on client errors (4xx)
|
|
144
489
|
if (error instanceof ConnectionError && error.message.includes('HTTP 4')) throw error;
|
|
145
|
-
// Wait before retry (exponential backoff)
|
|
146
490
|
if (attempt < (this.config.retries || 3)) {
|
|
147
491
|
const delay = Math.min(1000 * 2 ** (attempt - 1), 10000);
|
|
148
492
|
await new Promise((resolve)=>setTimeout(resolve, delay));
|
|
@@ -150,41 +494,25 @@ class types_QueryError extends ODBLiteError {
|
|
|
150
494
|
}
|
|
151
495
|
throw new ConnectionError(`Failed after ${this.config.retries} attempts: ${lastError?.message}`, lastError);
|
|
152
496
|
}
|
|
153
|
-
|
|
154
|
-
* Create a new HTTPClient with updated config
|
|
155
|
-
*/ configure(updates) {
|
|
497
|
+
configure(updates) {
|
|
156
498
|
return new HTTPClient({
|
|
157
499
|
...this.config,
|
|
158
500
|
...updates
|
|
159
501
|
});
|
|
160
502
|
}
|
|
161
|
-
|
|
162
|
-
* Get current configuration
|
|
163
|
-
*/ getConfig() {
|
|
503
|
+
getConfig() {
|
|
164
504
|
return {
|
|
165
505
|
...this.config
|
|
166
506
|
};
|
|
167
507
|
}
|
|
168
508
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
* into LibSQL-compatible parameterized queries
|
|
172
|
-
*/ class sql_parser_SQLParser {
|
|
173
|
-
/**
|
|
174
|
-
* Parse input that can be template strings, objects, or arrays
|
|
175
|
-
* Context-aware like postgres.js
|
|
176
|
-
*/ static parse(sql, values) {
|
|
177
|
-
// Handle template string arrays (have the raw property or look like template strings)
|
|
509
|
+
class sql_parser_SQLParser {
|
|
510
|
+
static parse(sql, values) {
|
|
178
511
|
if (Array.isArray(sql) && ('raw' in sql || 'string' == typeof sql[0] && void 0 !== values)) return this.parseTemplateString(sql, values || []);
|
|
179
|
-
// Handle direct object/array inputs (context detection)
|
|
180
512
|
return this.parseContextualInput(sql, values);
|
|
181
513
|
}
|
|
182
|
-
|
|
183
|
-
* Parse contextual input (objects, arrays, etc.)
|
|
184
|
-
* Uses heuristics to detect the intended context
|
|
185
|
-
*/ static parseContextualInput(input, values, context) {
|
|
514
|
+
static parseContextualInput(input, values, context) {
|
|
186
515
|
if (Array.isArray(input)) {
|
|
187
|
-
// Check if this is an array of objects (for INSERT VALUES)
|
|
188
516
|
if (input.length > 0 && input[0] && 'object' == typeof input[0] && input[0].constructor === Object) {
|
|
189
517
|
const insertResult = this.insertValues(input);
|
|
190
518
|
return {
|
|
@@ -192,7 +520,6 @@ class types_QueryError extends ODBLiteError {
|
|
|
192
520
|
params: insertResult.values
|
|
193
521
|
};
|
|
194
522
|
}
|
|
195
|
-
// Array of primitives - assume it's for IN clause
|
|
196
523
|
const placeholders = input.map(()=>'?').join(', ');
|
|
197
524
|
return {
|
|
198
525
|
sql: `(${placeholders})`,
|
|
@@ -200,20 +527,14 @@ class types_QueryError extends ODBLiteError {
|
|
|
200
527
|
};
|
|
201
528
|
}
|
|
202
529
|
if (input && 'object' == typeof input && input.constructor === Object) {
|
|
203
|
-
// Plain object - try to detect context based on structure and usage patterns
|
|
204
530
|
const entries = Object.entries(input).filter(([, value])=>void 0 !== value);
|
|
205
531
|
if (0 === entries.length) return {
|
|
206
532
|
sql: '',
|
|
207
533
|
params: []
|
|
208
534
|
};
|
|
209
|
-
// Analyze the object structure to determine intent
|
|
210
535
|
const hasNullValues = entries.some(([, value])=>null === value);
|
|
211
536
|
const hasArrayValues = entries.some(([, value])=>Array.isArray(value));
|
|
212
537
|
const hasComplexValues = entries.some(([, value])=>null !== value && 'object' == typeof value && !Array.isArray(value));
|
|
213
|
-
// Strong indicators for WHERE clauses:
|
|
214
|
-
// - null values (for IS NULL checks)
|
|
215
|
-
// - array values (for IN clauses)
|
|
216
|
-
// - complex nested objects
|
|
217
538
|
if (hasNullValues || hasArrayValues || hasComplexValues) {
|
|
218
539
|
const whereResult = this.where(input);
|
|
219
540
|
return {
|
|
@@ -221,20 +542,13 @@ class types_QueryError extends ODBLiteError {
|
|
|
221
542
|
params: whereResult.values
|
|
222
543
|
};
|
|
223
544
|
}
|
|
224
|
-
|
|
225
|
-
// This is inherently ambiguous - could be SET or VALUES
|
|
226
|
-
// We'll default to a flexible format that works for both
|
|
227
|
-
// If single object, more likely to be SET clause
|
|
228
|
-
if (entries.length <= 3 && !Array.isArray(input)) // Return SET format by default
|
|
229
|
-
return this.buildSetClause(input);
|
|
230
|
-
// For larger objects or arrays of objects, likely INSERT VALUES
|
|
545
|
+
if (entries.length <= 3 && !Array.isArray(input)) return this.buildSetClause(input);
|
|
231
546
|
const insertResult = this.insertValues(input);
|
|
232
547
|
return {
|
|
233
548
|
sql: insertResult.text,
|
|
234
549
|
params: insertResult.values
|
|
235
550
|
};
|
|
236
551
|
}
|
|
237
|
-
// Fallback for other types
|
|
238
552
|
return {
|
|
239
553
|
sql: '?',
|
|
240
554
|
params: [
|
|
@@ -242,9 +556,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
242
556
|
]
|
|
243
557
|
};
|
|
244
558
|
}
|
|
245
|
-
|
|
246
|
-
* Build a SET clause for UPDATE statements
|
|
247
|
-
*/ static buildSetClause(data) {
|
|
559
|
+
static buildSetClause(data) {
|
|
248
560
|
const entries = Object.entries(data).filter(([, value])=>void 0 !== value);
|
|
249
561
|
if (0 === entries.length) return {
|
|
250
562
|
sql: '',
|
|
@@ -257,9 +569,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
257
569
|
params: values
|
|
258
570
|
};
|
|
259
571
|
}
|
|
260
|
-
|
|
261
|
-
* Parse template string SQL into LibSQL format
|
|
262
|
-
*/ static parseTemplateString(sql, values) {
|
|
572
|
+
static parseTemplateString(sql, values) {
|
|
263
573
|
const fragments = [
|
|
264
574
|
...sql
|
|
265
575
|
];
|
|
@@ -269,21 +579,16 @@ class types_QueryError extends ODBLiteError {
|
|
|
269
579
|
query += fragments[i];
|
|
270
580
|
if (i < values.length) {
|
|
271
581
|
const value = values[i];
|
|
272
|
-
// Handle different value types using context detection
|
|
273
582
|
if (Array.isArray(value)) {
|
|
274
|
-
// Handle IN clauses: sql`SELECT * FROM users WHERE id IN ${[1, 2, 3]}`
|
|
275
583
|
const placeholders = value.map(()=>"?").join(', ');
|
|
276
584
|
query += `(${placeholders})`;
|
|
277
585
|
params.push(...value.map((v)=>this.convertValue(v)));
|
|
278
586
|
} else if (value && 'object' == typeof value && value.constructor === Object) {
|
|
279
|
-
// Use context detection for objects
|
|
280
587
|
const contextResult = this.parseContextualInput(value);
|
|
281
588
|
query += contextResult.sql;
|
|
282
589
|
params.push(...contextResult.params);
|
|
283
|
-
} else if ('string' == typeof value && value.startsWith('__RAW__'))
|
|
284
|
-
query += value.slice(7); // Remove __RAW__ prefix
|
|
590
|
+
} else if ('string' == typeof value && value.startsWith('__RAW__')) query += value.slice(7);
|
|
285
591
|
else {
|
|
286
|
-
// Regular parameter
|
|
287
592
|
query += '?';
|
|
288
593
|
params.push(this.convertValue(value));
|
|
289
594
|
}
|
|
@@ -294,50 +599,32 @@ class types_QueryError extends ODBLiteError {
|
|
|
294
599
|
params
|
|
295
600
|
};
|
|
296
601
|
}
|
|
297
|
-
|
|
298
|
-
* Create a raw SQL fragment (not parameterized)
|
|
299
|
-
*/ static raw(text) {
|
|
602
|
+
static raw(text) {
|
|
300
603
|
return `__RAW__${text}`;
|
|
301
604
|
}
|
|
302
|
-
|
|
303
|
-
* Create an identifier (table name, column name, etc.)
|
|
304
|
-
*/ static identifier(name) {
|
|
605
|
+
static identifier(name) {
|
|
305
606
|
return this.escapeIdentifier(name);
|
|
306
607
|
}
|
|
307
|
-
|
|
308
|
-
* Escape SQL identifiers (table names, column names)
|
|
309
|
-
*/ static escapeIdentifier(identifier) {
|
|
310
|
-
// SQLite uses double quotes for identifiers
|
|
608
|
+
static escapeIdentifier(identifier) {
|
|
311
609
|
return `"${identifier.replace(/"/g, '""')}"`;
|
|
312
610
|
}
|
|
313
|
-
|
|
314
|
-
* Convert JavaScript values to LibSQL-compatible values
|
|
315
|
-
*/ static convertValue(value) {
|
|
611
|
+
static convertValue(value) {
|
|
316
612
|
if (null == value) return null;
|
|
317
|
-
if (value instanceof Date)
|
|
318
|
-
return value
|
|
319
|
-
if (
|
|
320
|
-
|
|
321
|
-
if ('
|
|
322
|
-
return value ? 1 : 0;
|
|
323
|
-
if ('bigint' == typeof value) // Convert BigInt to string to avoid precision loss
|
|
324
|
-
return value.toString();
|
|
325
|
-
if ('object' == typeof value) // Serialize objects as JSON
|
|
326
|
-
return JSON.stringify(value);
|
|
613
|
+
if (value instanceof Date) return value.toISOString();
|
|
614
|
+
if (value instanceof Buffer) return new Uint8Array(value);
|
|
615
|
+
if ('boolean' == typeof value) return value ? 1 : 0;
|
|
616
|
+
if ('bigint' == typeof value) return value.toString();
|
|
617
|
+
if ('object' == typeof value) return JSON.stringify(value);
|
|
327
618
|
return value;
|
|
328
619
|
}
|
|
329
|
-
|
|
330
|
-
* Create a SQL fragment for building complex queries
|
|
331
|
-
*/ static fragment(sql, ...values) {
|
|
620
|
+
static fragment(sql, ...values) {
|
|
332
621
|
const parsed = this.parse(sql, values);
|
|
333
622
|
return {
|
|
334
623
|
text: parsed.sql,
|
|
335
624
|
values: parsed.params
|
|
336
625
|
};
|
|
337
626
|
}
|
|
338
|
-
|
|
339
|
-
* Join multiple SQL fragments
|
|
340
|
-
*/ static join(fragments, separator = ' ') {
|
|
627
|
+
static join(fragments, separator = ' ') {
|
|
341
628
|
const text = fragments.map((f)=>f.text).join(separator);
|
|
342
629
|
const values = fragments.flatMap((f)=>f.values);
|
|
343
630
|
return {
|
|
@@ -345,9 +632,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
345
632
|
values
|
|
346
633
|
};
|
|
347
634
|
}
|
|
348
|
-
|
|
349
|
-
* Helper for building WHERE clauses from objects
|
|
350
|
-
*/ static where(conditions) {
|
|
635
|
+
static where(conditions) {
|
|
351
636
|
const entries = Object.entries(conditions).filter(([, value])=>void 0 !== value);
|
|
352
637
|
if (0 === entries.length) return {
|
|
353
638
|
text: '',
|
|
@@ -373,9 +658,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
373
658
|
values
|
|
374
659
|
};
|
|
375
660
|
}
|
|
376
|
-
|
|
377
|
-
* Helper for building INSERT VALUES from objects
|
|
378
|
-
*/ static insertValues(data) {
|
|
661
|
+
static insertValues(data) {
|
|
379
662
|
const records = Array.isArray(data) ? data : [
|
|
380
663
|
data
|
|
381
664
|
];
|
|
@@ -392,9 +675,7 @@ class types_QueryError extends ODBLiteError {
|
|
|
392
675
|
values
|
|
393
676
|
};
|
|
394
677
|
}
|
|
395
|
-
|
|
396
|
-
* Helper for building UPDATE SET clauses from objects
|
|
397
|
-
*/ static updateSet(data) {
|
|
678
|
+
static updateSet(data) {
|
|
398
679
|
const entries = Object.entries(data).filter(([, value])=>void 0 !== value);
|
|
399
680
|
if (0 === entries.length) throw new Error('No data provided for update');
|
|
400
681
|
const setClauses = entries.map(([key])=>`${this.escapeIdentifier(key)} = ?`).join(', ');
|
|
@@ -405,7 +686,6 @@ class types_QueryError extends ODBLiteError {
|
|
|
405
686
|
};
|
|
406
687
|
}
|
|
407
688
|
}
|
|
408
|
-
// Export convenience functions
|
|
409
689
|
const sql_parser_sql = sql_parser_SQLParser.parse.bind(sql_parser_SQLParser);
|
|
410
690
|
sql_parser_SQLParser.raw.bind(sql_parser_SQLParser);
|
|
411
691
|
sql_parser_SQLParser.identifier.bind(sql_parser_SQLParser);
|
|
@@ -414,12 +694,7 @@ sql_parser_SQLParser.join.bind(sql_parser_SQLParser);
|
|
|
414
694
|
sql_parser_SQLParser.where.bind(sql_parser_SQLParser);
|
|
415
695
|
sql_parser_SQLParser.insertValues.bind(sql_parser_SQLParser);
|
|
416
696
|
sql_parser_SQLParser.updateSet.bind(sql_parser_SQLParser);
|
|
417
|
-
|
|
418
|
-
* Transaction implementation for ODBLite
|
|
419
|
-
* Note: SQLite transactions are simulated at the client level since ODBLite
|
|
420
|
-
* operates on individual queries. This provides a familiar API but doesn't
|
|
421
|
-
* provide true ACID guarantees across multiple HTTP requests.
|
|
422
|
-
*/ class ODBLiteTransaction {
|
|
697
|
+
class ODBLiteTransaction {
|
|
423
698
|
httpClient;
|
|
424
699
|
isCommitted = false;
|
|
425
700
|
isRolledBack = false;
|
|
@@ -427,136 +702,95 @@ sql_parser_SQLParser.updateSet.bind(sql_parser_SQLParser);
|
|
|
427
702
|
constructor(httpClient){
|
|
428
703
|
this.httpClient = httpClient;
|
|
429
704
|
}
|
|
430
|
-
|
|
431
|
-
* Execute a query within the transaction
|
|
432
|
-
* For SQLite, we'll queue queries and execute them in batch on commit
|
|
433
|
-
*/ async sql(sql, ...values) {
|
|
705
|
+
async sql(sql, ...values) {
|
|
434
706
|
this.checkTransactionState();
|
|
435
707
|
const parsed = sql_parser_SQLParser.parse(sql, values);
|
|
436
|
-
// For read queries, execute immediately
|
|
437
708
|
if (this.isReadQuery(parsed.sql)) return await this.httpClient.query(parsed.sql, parsed.params);
|
|
438
|
-
// For write queries, queue them for batch execution
|
|
439
709
|
this.queries.push(parsed);
|
|
440
|
-
// Return a placeholder result for queued queries
|
|
441
710
|
return {
|
|
442
711
|
rows: [],
|
|
443
712
|
rowsAffected: 0,
|
|
444
713
|
executionTime: 0
|
|
445
714
|
};
|
|
446
715
|
}
|
|
447
|
-
|
|
448
|
-
* Commit the transaction by executing all queued queries
|
|
449
|
-
*/ async commit() {
|
|
716
|
+
async commit() {
|
|
450
717
|
this.checkTransactionState();
|
|
451
718
|
try {
|
|
452
|
-
// Execute BEGIN
|
|
453
719
|
await this.httpClient.query('BEGIN');
|
|
454
|
-
// Execute all queued queries
|
|
455
720
|
for (const query of this.queries)await this.httpClient.query(query.sql, query.params);
|
|
456
|
-
// Commit the transaction
|
|
457
721
|
await this.httpClient.query('COMMIT');
|
|
458
722
|
this.isCommitted = true;
|
|
459
723
|
} catch (error) {
|
|
460
|
-
// Rollback on any error
|
|
461
724
|
try {
|
|
462
725
|
await this.httpClient.query('ROLLBACK');
|
|
463
|
-
} catch (rollbackError) {
|
|
464
|
-
// Ignore rollback errors
|
|
465
|
-
}
|
|
726
|
+
} catch (rollbackError) {}
|
|
466
727
|
this.isRolledBack = true;
|
|
467
728
|
throw new types_QueryError(`Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`, void 0, void 0, error instanceof Error ? error : void 0);
|
|
468
729
|
}
|
|
469
730
|
}
|
|
470
|
-
|
|
471
|
-
* Rollback the transaction
|
|
472
|
-
*/ async rollback() {
|
|
731
|
+
async rollback() {
|
|
473
732
|
this.checkTransactionState();
|
|
474
733
|
try {
|
|
475
|
-
// If we have any queries, we need to actually rollback
|
|
476
734
|
if (this.queries.length > 0) await this.httpClient.query('ROLLBACK');
|
|
477
735
|
} finally{
|
|
478
736
|
this.isRolledBack = true;
|
|
479
737
|
}
|
|
480
738
|
}
|
|
481
|
-
|
|
482
|
-
* Check if transaction is still active
|
|
483
|
-
*/ checkTransactionState() {
|
|
739
|
+
checkTransactionState() {
|
|
484
740
|
if (this.isCommitted) throw new types_QueryError('Transaction has already been committed');
|
|
485
741
|
if (this.isRolledBack) throw new types_QueryError('Transaction has been rolled back');
|
|
486
742
|
}
|
|
487
|
-
|
|
488
|
-
* Determine if a query is a read operation
|
|
489
|
-
*/ isReadQuery(sql) {
|
|
743
|
+
isReadQuery(sql) {
|
|
490
744
|
const trimmed = sql.trim().toUpperCase();
|
|
491
745
|
return trimmed.startsWith('SELECT') || trimmed.startsWith('WITH') || trimmed.startsWith('EXPLAIN') || trimmed.startsWith('PRAGMA');
|
|
492
746
|
}
|
|
493
747
|
}
|
|
494
|
-
|
|
495
|
-
* Create a simple transaction function that executes immediately
|
|
496
|
-
* This is more suitable for HTTP-based databases where true transactions
|
|
497
|
-
* across multiple requests are not practical
|
|
498
|
-
*/ function createSimpleTransaction(httpClient) {
|
|
748
|
+
function createSimpleTransaction(httpClient) {
|
|
499
749
|
let isActive = true;
|
|
500
|
-
// Create the callable transaction function with context awareness
|
|
501
750
|
const txFunction = (sql, ...values)=>{
|
|
502
751
|
if (!isActive) throw new types_QueryError('Transaction is no longer active');
|
|
503
|
-
// Handle template string queries (returns Promise)
|
|
504
752
|
if (Array.isArray(sql) && ('raw' in sql || 'string' == typeof sql[0] && values.length >= 0)) {
|
|
505
753
|
const parsed = sql_parser_SQLParser.parse(sql, values);
|
|
506
754
|
return httpClient.query(parsed.sql, parsed.params);
|
|
507
755
|
}
|
|
508
|
-
// Handle direct object/array inputs (returns SQLFragment for composing)
|
|
509
756
|
const parsed = sql_parser_SQLParser.parse(sql, values);
|
|
510
757
|
return {
|
|
511
758
|
text: parsed.sql,
|
|
512
759
|
values: parsed.params
|
|
513
760
|
};
|
|
514
761
|
};
|
|
515
|
-
// Attach utility methods to the transaction function
|
|
516
762
|
txFunction.raw = (text)=>sql_parser_SQLParser.raw(text);
|
|
517
763
|
txFunction.identifier = (name)=>sql_parser_SQLParser.identifier(name);
|
|
518
|
-
// Add execute method for compatibility
|
|
519
764
|
txFunction.execute = async (sql, args)=>{
|
|
520
765
|
if (!isActive) throw new types_QueryError('Transaction is no longer active');
|
|
521
766
|
if ('string' == typeof sql) return await httpClient.query(sql, args || []);
|
|
522
767
|
return await httpClient.query(sql.sql, sql.args || []);
|
|
523
768
|
};
|
|
524
|
-
// Add query method for compatibility
|
|
525
769
|
txFunction.query = async (sql, params = [])=>{
|
|
526
770
|
if (!isActive) throw new types_QueryError('Transaction is no longer active');
|
|
527
771
|
return await httpClient.query(sql, params);
|
|
528
772
|
};
|
|
529
773
|
txFunction.commit = async ()=>{
|
|
530
774
|
isActive = false;
|
|
531
|
-
// No-op for simple transactions
|
|
532
775
|
};
|
|
533
776
|
txFunction.rollback = async ()=>{
|
|
534
777
|
isActive = false;
|
|
535
|
-
// No-op for simple transactions - individual queries are atomic
|
|
536
778
|
};
|
|
537
779
|
txFunction.savepoint = async (callback)=>{
|
|
538
780
|
if (!isActive) throw new types_QueryError('Transaction is no longer active');
|
|
539
|
-
// Create a nested transaction for the savepoint
|
|
540
781
|
const savepointTx = createSimpleTransaction(httpClient);
|
|
541
782
|
try {
|
|
542
|
-
// Execute the callback with the savepoint transaction
|
|
543
783
|
const result = await callback(savepointTx);
|
|
544
|
-
// Commit the savepoint (no-op for simple transactions)
|
|
545
784
|
await savepointTx.commit();
|
|
546
785
|
return result;
|
|
547
786
|
} catch (error) {
|
|
548
|
-
// Rollback the savepoint on error
|
|
549
787
|
await savepointTx.rollback();
|
|
550
788
|
throw error;
|
|
551
789
|
}
|
|
552
790
|
};
|
|
553
791
|
return txFunction;
|
|
554
792
|
}
|
|
555
|
-
|
|
556
|
-
* Simple transaction implementation that executes immediately
|
|
557
|
-
* This is more suitable for HTTP-based databases where true transactions
|
|
558
|
-
* across multiple requests are not practical
|
|
559
|
-
*/ class SimpleTransaction {
|
|
793
|
+
class SimpleTransaction {
|
|
560
794
|
httpClient;
|
|
561
795
|
isActive = true;
|
|
562
796
|
constructor(httpClient){
|
|
@@ -569,60 +803,43 @@ sql_parser_SQLParser.updateSet.bind(sql_parser_SQLParser);
|
|
|
569
803
|
}
|
|
570
804
|
async commit() {
|
|
571
805
|
this.isActive = false;
|
|
572
|
-
// No-op for simple transactions
|
|
573
806
|
}
|
|
574
807
|
async rollback() {
|
|
575
808
|
this.isActive = false;
|
|
576
|
-
// No-op for simple transactions - individual queries are atomic
|
|
577
809
|
}
|
|
578
810
|
}
|
|
579
|
-
|
|
580
|
-
* Main ODBLite client that provides postgres.js-like interface
|
|
581
|
-
*/ class ODBLiteClient {
|
|
811
|
+
class ODBLiteClient {
|
|
582
812
|
httpClient;
|
|
583
813
|
config;
|
|
584
814
|
sql;
|
|
585
815
|
constructor(config){
|
|
586
816
|
this.config = config;
|
|
587
817
|
this.httpClient = new HTTPClient(config);
|
|
588
|
-
// Create the callable sql function with attached utility methods
|
|
589
818
|
const sqlFunction = (sql, ...values)=>{
|
|
590
|
-
// Handle template string queries (returns Promise)
|
|
591
819
|
if (Array.isArray(sql) && ('raw' in sql || 'string' == typeof sql[0] && values.length >= 0)) {
|
|
592
820
|
const parsed = sql_parser_SQLParser.parse(sql, values);
|
|
593
821
|
return this.httpClient.query(parsed.sql, parsed.params);
|
|
594
822
|
}
|
|
595
|
-
// Handle direct object/array inputs (returns SQLFragment for composing)
|
|
596
823
|
const parsed = sql_parser_SQLParser.parse(sql, values);
|
|
597
824
|
return {
|
|
598
825
|
text: parsed.sql,
|
|
599
826
|
values: parsed.params
|
|
600
827
|
};
|
|
601
828
|
};
|
|
602
|
-
// Attach minimal utility methods to the function
|
|
603
829
|
sqlFunction.raw = (text)=>sql_parser_SQLParser.raw(text);
|
|
604
830
|
sqlFunction.identifier = (name)=>sql_parser_SQLParser.identifier(name);
|
|
605
|
-
// Attach client methods to the function
|
|
606
831
|
sqlFunction.query = async (sql, params = [])=>await this.httpClient.query(sql, params);
|
|
607
|
-
// libsql-compatible execute method (for backward compatibility)
|
|
608
832
|
sqlFunction.execute = async (sql, args)=>{
|
|
609
833
|
if ('string' == typeof sql) return await this.httpClient.query(sql, args || []);
|
|
610
834
|
return await this.httpClient.query(sql.sql, sql.args || []);
|
|
611
835
|
};
|
|
612
|
-
// Enhanced begin method with callback support
|
|
613
836
|
sqlFunction.begin = async (modeOrCallback, callback)=>{
|
|
614
|
-
|
|
615
|
-
if ('
|
|
616
|
-
return this.executeTransactionWithCallback(modeOrCallback);
|
|
617
|
-
if ('string' == typeof modeOrCallback && callback) // begin(mode, callback)
|
|
618
|
-
return this.executeTransactionWithCallback(callback, modeOrCallback);
|
|
619
|
-
// begin() - traditional style
|
|
837
|
+
if ('function' == typeof modeOrCallback) return this.executeTransactionWithCallback(modeOrCallback);
|
|
838
|
+
if ('string' == typeof modeOrCallback && callback) return this.executeTransactionWithCallback(callback, modeOrCallback);
|
|
620
839
|
return createSimpleTransaction(this.httpClient);
|
|
621
840
|
};
|
|
622
841
|
sqlFunction.ping = async ()=>await this.httpClient.ping();
|
|
623
|
-
sqlFunction.end = async ()=>{
|
|
624
|
-
// No-op for HTTP-based client
|
|
625
|
-
};
|
|
842
|
+
sqlFunction.end = async ()=>{};
|
|
626
843
|
sqlFunction.setDatabase = (databaseId)=>{
|
|
627
844
|
this.httpClient.setDatabase(databaseId);
|
|
628
845
|
this.config.databaseId = databaseId;
|
|
@@ -638,99 +855,58 @@ sql_parser_SQLParser.updateSet.bind(sql_parser_SQLParser);
|
|
|
638
855
|
};
|
|
639
856
|
this.sql = sqlFunction;
|
|
640
857
|
}
|
|
641
|
-
|
|
642
|
-
* Execute a transaction with callback (postgres.js style)
|
|
643
|
-
*/ async executeTransactionWithCallback(callback, mode) {
|
|
858
|
+
async executeTransactionWithCallback(callback, mode) {
|
|
644
859
|
const tx = createSimpleTransaction(this.httpClient);
|
|
645
860
|
try {
|
|
646
|
-
// Execute the callback with the transaction
|
|
647
861
|
const result = await callback(tx);
|
|
648
|
-
// Commit the transaction
|
|
649
862
|
await tx.commit();
|
|
650
863
|
return result;
|
|
651
864
|
} catch (error) {
|
|
652
|
-
// Rollback on any error
|
|
653
865
|
await tx.rollback();
|
|
654
866
|
throw error;
|
|
655
867
|
}
|
|
656
868
|
}
|
|
657
|
-
|
|
658
|
-
* Raw query method
|
|
659
|
-
* Usage: client.query('SELECT * FROM users WHERE id = ?', [123])
|
|
660
|
-
*/ async query(sql, params = []) {
|
|
869
|
+
async query(sql, params = []) {
|
|
661
870
|
return await this.httpClient.query(sql, params);
|
|
662
871
|
}
|
|
663
|
-
|
|
664
|
-
* Begin a transaction
|
|
665
|
-
* Note: Uses simple transaction model suitable for HTTP-based access
|
|
666
|
-
*/ async begin() {
|
|
872
|
+
async begin() {
|
|
667
873
|
return createSimpleTransaction(this.httpClient);
|
|
668
874
|
}
|
|
669
|
-
|
|
670
|
-
* Health check
|
|
671
|
-
*/ async ping() {
|
|
875
|
+
async ping() {
|
|
672
876
|
return await this.httpClient.ping();
|
|
673
877
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
*/ async end() {
|
|
677
|
-
// No-op for HTTP-based client
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* Set the database ID for queries
|
|
681
|
-
*/ setDatabase(databaseId) {
|
|
878
|
+
async end() {}
|
|
879
|
+
setDatabase(databaseId) {
|
|
682
880
|
this.httpClient.setDatabase(databaseId);
|
|
683
881
|
this.config.databaseId = databaseId;
|
|
684
882
|
return this;
|
|
685
883
|
}
|
|
686
|
-
|
|
687
|
-
* Get database information
|
|
688
|
-
*/ async getDatabaseInfo() {
|
|
884
|
+
async getDatabaseInfo() {
|
|
689
885
|
return await this.httpClient.getDatabaseInfo();
|
|
690
886
|
}
|
|
691
|
-
|
|
692
|
-
* Create a new client instance with updated configuration
|
|
693
|
-
*/ configure(updates) {
|
|
887
|
+
configure(updates) {
|
|
694
888
|
const newConfig = {
|
|
695
889
|
...this.config,
|
|
696
890
|
...updates
|
|
697
891
|
};
|
|
698
892
|
return new ODBLiteClient(newConfig);
|
|
699
893
|
}
|
|
700
|
-
|
|
701
|
-
* Create raw SQL that won't be parameterized
|
|
702
|
-
* Usage: sql`SELECT * FROM ${raw('users')}`
|
|
703
|
-
*/ static raw(text) {
|
|
894
|
+
static raw(text) {
|
|
704
895
|
return sql_parser_SQLParser.raw(text);
|
|
705
896
|
}
|
|
706
|
-
|
|
707
|
-
* Escape identifier (table/column names)
|
|
708
|
-
* Usage: sql`SELECT * FROM ${identifier('user-table')}`
|
|
709
|
-
*/ static identifier(name) {
|
|
897
|
+
static identifier(name) {
|
|
710
898
|
return sql_parser_SQLParser.identifier(name);
|
|
711
899
|
}
|
|
712
|
-
|
|
713
|
-
* Build WHERE clause from object
|
|
714
|
-
* Usage: const whereClause = ODBLiteClient.where({ id: 1, name: 'John' });
|
|
715
|
-
*/ static where(conditions) {
|
|
900
|
+
static where(conditions) {
|
|
716
901
|
return sql_parser_SQLParser.where(conditions);
|
|
717
902
|
}
|
|
718
|
-
|
|
719
|
-
* Build INSERT VALUES from object(s)
|
|
720
|
-
* Usage: const insertClause = ODBLiteClient.insertValues({ name: 'John', age: 30 });
|
|
721
|
-
*/ static insertValues(data) {
|
|
903
|
+
static insertValues(data) {
|
|
722
904
|
return sql_parser_SQLParser.insertValues(data);
|
|
723
905
|
}
|
|
724
|
-
|
|
725
|
-
* Build UPDATE SET clause from object
|
|
726
|
-
* Usage: const setClause = ODBLiteClient.updateSet({ name: 'John', age: 30 });
|
|
727
|
-
*/ static updateSet(data) {
|
|
906
|
+
static updateSet(data) {
|
|
728
907
|
return sql_parser_SQLParser.updateSet(data);
|
|
729
908
|
}
|
|
730
|
-
|
|
731
|
-
* Join SQL fragments
|
|
732
|
-
* Usage: const query = ODBLiteClient.join([baseQuery, whereClause], ' WHERE ');
|
|
733
|
-
*/ static join(fragments, separator = ' ') {
|
|
909
|
+
static join(fragments, separator = ' ') {
|
|
734
910
|
return sql_parser_SQLParser.join(fragments, separator);
|
|
735
911
|
}
|
|
736
912
|
}
|
|
@@ -742,43 +918,13 @@ function odblite(configOrBaseUrl, apiKey, databaseId) {
|
|
|
742
918
|
}) : new ODBLiteClient(configOrBaseUrl);
|
|
743
919
|
return client.sql;
|
|
744
920
|
}
|
|
745
|
-
// Export static utility functions
|
|
746
921
|
ODBLiteClient.raw;
|
|
747
922
|
ODBLiteClient.identifier;
|
|
748
923
|
ODBLiteClient.where;
|
|
749
924
|
ODBLiteClient.insertValues;
|
|
750
925
|
ODBLiteClient.updateSet;
|
|
751
926
|
ODBLiteClient.join;
|
|
752
|
-
|
|
753
|
-
* Service Client - High-level client for managing tenant databases via ODB-Lite Tenant API
|
|
754
|
-
*
|
|
755
|
-
* This client provides automatic database provisioning and management for multi-tenant applications.
|
|
756
|
-
* It handles:
|
|
757
|
-
* - Automatic database creation on first use
|
|
758
|
-
* - Database hash caching for performance
|
|
759
|
-
* - Tenant API integration with ODB-Lite
|
|
760
|
-
* - Query execution via ODB-Lite's query API
|
|
761
|
-
*
|
|
762
|
-
* @example
|
|
763
|
-
* ```typescript
|
|
764
|
-
* const service = new ServiceClient({
|
|
765
|
-
* baseUrl: 'http://localhost:8671',
|
|
766
|
-
* apiKey: 'odblite_tenant_key'
|
|
767
|
-
* });
|
|
768
|
-
*
|
|
769
|
-
* // Automatically creates database if it doesn't exist
|
|
770
|
-
* const dbHash = await service.ensureDatabaseForTenant('wallet', 'tenant-123');
|
|
771
|
-
*
|
|
772
|
-
* // Execute queries
|
|
773
|
-
* const result = await service.query(dbHash, 'SELECT * FROM wallets', []);
|
|
774
|
-
* ```
|
|
775
|
-
*/ /**
|
|
776
|
-
* Service Client for managing tenant databases in ODB-Lite
|
|
777
|
-
*
|
|
778
|
-
* This is a higher-level client that sits on top of the base ODB client.
|
|
779
|
-
* It provides automatic database provisioning and management for services
|
|
780
|
-
* that need per-tenant database isolation.
|
|
781
|
-
*/ class ServiceClient {
|
|
927
|
+
class ServiceClient {
|
|
782
928
|
apiUrl;
|
|
783
929
|
apiKey;
|
|
784
930
|
databaseCache;
|
|
@@ -787,35 +933,15 @@ ODBLiteClient.join;
|
|
|
787
933
|
this.apiKey = config.apiKey;
|
|
788
934
|
this.databaseCache = new Map();
|
|
789
935
|
}
|
|
790
|
-
|
|
791
|
-
* Get or create a database for a tenant
|
|
792
|
-
*
|
|
793
|
-
* This is the main method used by services. It will:
|
|
794
|
-
* 1. Check the cache for an existing database hash
|
|
795
|
-
* 2. Query ODB-Lite to see if the database exists
|
|
796
|
-
* 3. Create the database if it doesn't exist
|
|
797
|
-
* 4. Cache and return the database hash
|
|
798
|
-
*
|
|
799
|
-
* @param prefix - Database name prefix (e.g., 'wallet', 'tracking')
|
|
800
|
-
* @param tenantId - Tenant identifier
|
|
801
|
-
* @returns Database hash for querying
|
|
802
|
-
*
|
|
803
|
-
* @example
|
|
804
|
-
* ```typescript
|
|
805
|
-
* const hash = await service.ensureDatabaseForTenant('wallet', 'tenant-123');
|
|
806
|
-
* // Returns hash for database named 'wallet_tenant-123'
|
|
807
|
-
* ```
|
|
808
|
-
*/ async ensureDatabaseForTenant(prefix, tenantId) {
|
|
936
|
+
async ensureDatabaseForTenant(prefix, tenantId) {
|
|
809
937
|
const cacheKey = `${prefix}_${tenantId}`;
|
|
810
938
|
console.log(`📊 Ensuring database for ${cacheKey}`);
|
|
811
|
-
// Check cache first
|
|
812
939
|
const cached = this.databaseCache.get(cacheKey);
|
|
813
940
|
if (cached) {
|
|
814
941
|
console.log(`✅ Found cached database hash: ${cached}`);
|
|
815
942
|
return cached;
|
|
816
943
|
}
|
|
817
944
|
try {
|
|
818
|
-
// Check if database already exists
|
|
819
945
|
console.log(`🔍 Checking if database exists: ${cacheKey}`);
|
|
820
946
|
const existing = await this.getDatabaseByName(cacheKey);
|
|
821
947
|
if (existing) {
|
|
@@ -823,12 +949,10 @@ ODBLiteClient.join;
|
|
|
823
949
|
this.databaseCache.set(cacheKey, existing.hash);
|
|
824
950
|
return existing.hash;
|
|
825
951
|
}
|
|
826
|
-
// Create new database
|
|
827
952
|
console.log(`🆕 Creating new database: ${cacheKey}`);
|
|
828
953
|
const nodes = await this.listNodes();
|
|
829
954
|
console.log(`📡 Available nodes: ${nodes.length}`);
|
|
830
955
|
if (0 === nodes.length) throw new Error('No available nodes to create database');
|
|
831
|
-
// Use first healthy node
|
|
832
956
|
const node = nodes.find((n)=>'healthy' === n.status) || nodes[0];
|
|
833
957
|
if (!node) throw new Error('No available nodes to create database');
|
|
834
958
|
console.log(`🎯 Using node: ${node.nodeId}`);
|
|
@@ -841,13 +965,7 @@ ODBLiteClient.join;
|
|
|
841
965
|
throw error;
|
|
842
966
|
}
|
|
843
967
|
}
|
|
844
|
-
|
|
845
|
-
* List all databases owned by this tenant
|
|
846
|
-
*
|
|
847
|
-
* Queries ODB-Lite's tenant API to get all databases accessible with the current API key.
|
|
848
|
-
*
|
|
849
|
-
* @returns Array of database objects
|
|
850
|
-
*/ async listDatabases() {
|
|
968
|
+
async listDatabases() {
|
|
851
969
|
const response = await fetch(`${this.apiUrl}/api/tenant/databases`, {
|
|
852
970
|
headers: {
|
|
853
971
|
Authorization: `Bearer ${this.apiKey}`
|
|
@@ -857,13 +975,7 @@ ODBLiteClient.join;
|
|
|
857
975
|
if (!result.success) throw new Error(result.error || 'Failed to list databases');
|
|
858
976
|
return result.databases;
|
|
859
977
|
}
|
|
860
|
-
|
|
861
|
-
* Create a new database
|
|
862
|
-
*
|
|
863
|
-
* @param name - Database name (should be unique)
|
|
864
|
-
* @param nodeId - ID of the node to host the database (optional - server will select if null)
|
|
865
|
-
* @returns Created database object with hash
|
|
866
|
-
*/ async createDatabase(name, nodeId) {
|
|
978
|
+
async createDatabase(name, nodeId) {
|
|
867
979
|
const body = {
|
|
868
980
|
name
|
|
869
981
|
};
|
|
@@ -880,12 +992,7 @@ ODBLiteClient.join;
|
|
|
880
992
|
if (!result.success) throw new Error(result.error || 'Failed to create database');
|
|
881
993
|
return result.database;
|
|
882
994
|
}
|
|
883
|
-
|
|
884
|
-
* Get database details by hash
|
|
885
|
-
*
|
|
886
|
-
* @param hash - Database hash
|
|
887
|
-
* @returns Database object
|
|
888
|
-
*/ async getDatabase(hash) {
|
|
995
|
+
async getDatabase(hash) {
|
|
889
996
|
const response = await fetch(`${this.apiUrl}/api/tenant/databases/${hash}`, {
|
|
890
997
|
headers: {
|
|
891
998
|
Authorization: `Bearer ${this.apiKey}`
|
|
@@ -895,12 +1002,7 @@ ODBLiteClient.join;
|
|
|
895
1002
|
if (!result.success) throw new Error(result.error || 'Failed to get database');
|
|
896
1003
|
return result.database;
|
|
897
1004
|
}
|
|
898
|
-
|
|
899
|
-
* Get database details by name
|
|
900
|
-
*
|
|
901
|
-
* @param name - Database name
|
|
902
|
-
* @returns Database object or null if not found
|
|
903
|
-
*/ async getDatabaseByName(name) {
|
|
1005
|
+
async getDatabaseByName(name) {
|
|
904
1006
|
const response = await fetch(`${this.apiUrl}/api/tenant/databases/by-name/${name}`, {
|
|
905
1007
|
headers: {
|
|
906
1008
|
Authorization: `Bearer ${this.apiKey}`
|
|
@@ -911,11 +1013,7 @@ ODBLiteClient.join;
|
|
|
911
1013
|
if (!result.success) throw new Error(result.error || 'Failed to get database');
|
|
912
1014
|
return result.database;
|
|
913
1015
|
}
|
|
914
|
-
|
|
915
|
-
* Delete a database
|
|
916
|
-
*
|
|
917
|
-
* @param hash - Database hash to delete
|
|
918
|
-
*/ async deleteDatabase(hash) {
|
|
1016
|
+
async deleteDatabase(hash) {
|
|
919
1017
|
const response = await fetch(`${this.apiUrl}/api/tenant/databases/${hash}`, {
|
|
920
1018
|
method: 'DELETE',
|
|
921
1019
|
headers: {
|
|
@@ -924,17 +1022,12 @@ ODBLiteClient.join;
|
|
|
924
1022
|
});
|
|
925
1023
|
const result = await response.json();
|
|
926
1024
|
if (!result.success) throw new Error(result.error || 'Failed to delete database');
|
|
927
|
-
// Remove from cache
|
|
928
1025
|
for (const [key, cachedHash] of this.databaseCache.entries())if (cachedHash === hash) {
|
|
929
1026
|
this.databaseCache.delete(key);
|
|
930
1027
|
break;
|
|
931
1028
|
}
|
|
932
1029
|
}
|
|
933
|
-
|
|
934
|
-
* List available nodes
|
|
935
|
-
*
|
|
936
|
-
* @returns Array of node objects
|
|
937
|
-
*/ async listNodes() {
|
|
1030
|
+
async listNodes() {
|
|
938
1031
|
const response = await fetch(`${this.apiUrl}/api/tenant/nodes`, {
|
|
939
1032
|
headers: {
|
|
940
1033
|
Authorization: `Bearer ${this.apiKey}`
|
|
@@ -944,18 +1037,7 @@ ODBLiteClient.join;
|
|
|
944
1037
|
if (!result.success) throw new Error(result.error || 'Failed to list nodes');
|
|
945
1038
|
return result.nodes;
|
|
946
1039
|
}
|
|
947
|
-
|
|
948
|
-
* Execute a query on a specific database
|
|
949
|
-
*
|
|
950
|
-
* This is a low-level query method. For most use cases, you should use
|
|
951
|
-
* the full ODB client (`odblite()` function) instead, which provides
|
|
952
|
-
* template tag support and better ergonomics.
|
|
953
|
-
*
|
|
954
|
-
* @param databaseHash - Hash of the database to query
|
|
955
|
-
* @param sql - SQL query string
|
|
956
|
-
* @param params - Query parameters
|
|
957
|
-
* @returns Query result
|
|
958
|
-
*/ async query(databaseHash, sql, params = []) {
|
|
1040
|
+
async query(databaseHash, sql, params = []) {
|
|
959
1041
|
const response = await fetch(`${this.apiUrl}/query/${databaseHash}`, {
|
|
960
1042
|
method: 'POST',
|
|
961
1043
|
headers: {
|
|
@@ -971,60 +1053,174 @@ ODBLiteClient.join;
|
|
|
971
1053
|
if (!result.success) throw new Error(result.error || 'Query failed');
|
|
972
1054
|
return result.data;
|
|
973
1055
|
}
|
|
974
|
-
|
|
975
|
-
* Clear the database hash cache
|
|
976
|
-
*
|
|
977
|
-
* Useful when database mappings have changed or for testing.
|
|
978
|
-
*/ clearCache() {
|
|
1056
|
+
clearCache() {
|
|
979
1057
|
this.databaseCache.clear();
|
|
980
1058
|
}
|
|
981
|
-
|
|
982
|
-
* Get cached database hash for a specific tenant (if exists)
|
|
983
|
-
*
|
|
984
|
-
* @param prefix - Database name prefix
|
|
985
|
-
* @param tenantId - Tenant identifier
|
|
986
|
-
* @returns Cached hash or undefined
|
|
987
|
-
*/ getCachedHash(prefix, tenantId) {
|
|
1059
|
+
getCachedHash(prefix, tenantId) {
|
|
988
1060
|
const cacheKey = `${prefix}_${tenantId}`;
|
|
989
1061
|
return this.databaseCache.get(cacheKey);
|
|
990
1062
|
}
|
|
991
|
-
|
|
992
|
-
* Pre-cache a database hash
|
|
993
|
-
*
|
|
994
|
-
* Useful when you know the mapping ahead of time and want to avoid
|
|
995
|
-
* the initial lookup.
|
|
996
|
-
*
|
|
997
|
-
* @param prefix - Database name prefix
|
|
998
|
-
* @param tenantId - Tenant identifier
|
|
999
|
-
* @param hash - Database hash
|
|
1000
|
-
*/ setCachedHash(prefix, tenantId, hash) {
|
|
1063
|
+
setCachedHash(prefix, tenantId, hash) {
|
|
1001
1064
|
const cacheKey = `${prefix}_${tenantId}`;
|
|
1002
1065
|
this.databaseCache.set(cacheKey, hash);
|
|
1003
1066
|
}
|
|
1004
1067
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1068
|
+
function convertTemplateToQuery(strings, values) {
|
|
1069
|
+
let sql = '';
|
|
1070
|
+
const args = [];
|
|
1071
|
+
for(let i = 0; i < strings.length; i++){
|
|
1072
|
+
sql += strings[i];
|
|
1073
|
+
if (i < values.length) {
|
|
1074
|
+
const value = values[i];
|
|
1075
|
+
if (isSqlFragment(value)) {
|
|
1076
|
+
sql += value.sql;
|
|
1077
|
+
args.push(...value.args);
|
|
1078
|
+
} else if (Array.isArray(value)) if (value.length > 0 && Array.isArray(value[0])) {
|
|
1079
|
+
const rowPlaceholders = value.map((row)=>`(${row.map(()=>'?').join(', ')})`).join(', ');
|
|
1080
|
+
sql += rowPlaceholders;
|
|
1081
|
+
args.push(...value.flat());
|
|
1082
|
+
} else {
|
|
1083
|
+
const placeholders = value.map(()=>'?').join(', ');
|
|
1084
|
+
sql += `(${placeholders})`;
|
|
1085
|
+
args.push(...value);
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
sql += '?';
|
|
1089
|
+
args.push(value);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return {
|
|
1094
|
+
sql,
|
|
1095
|
+
args
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function isSqlFragment(value) {
|
|
1099
|
+
return value && 'object' == typeof value && true === value._isSqlFragment;
|
|
1100
|
+
}
|
|
1101
|
+
function sql_template_sql(value, ...keys) {
|
|
1102
|
+
let columnKeys = [];
|
|
1103
|
+
if (keys.length > 0) columnKeys = Array.isArray(keys[0]) ? keys[0] : keys;
|
|
1104
|
+
if ('string' == typeof value && 0 === columnKeys.length) return {
|
|
1105
|
+
_isSqlFragment: true,
|
|
1106
|
+
sql: value,
|
|
1107
|
+
args: []
|
|
1108
|
+
};
|
|
1109
|
+
if (Array.isArray(value) && value.length > 0 && 'string' == typeof value[0]) return {
|
|
1110
|
+
_isSqlFragment: true,
|
|
1111
|
+
sql: value.join(', '),
|
|
1112
|
+
args: []
|
|
1113
|
+
};
|
|
1114
|
+
if (Array.isArray(value) && value.length > 0 && 'object' == typeof value[0] && !Array.isArray(value[0])) {
|
|
1115
|
+
if (0 === columnKeys.length) columnKeys = Object.keys(value[0]);
|
|
1116
|
+
const allValues = [];
|
|
1117
|
+
const rowPlaceholders = [];
|
|
1118
|
+
for (const obj of value){
|
|
1119
|
+
const rowValues = columnKeys.map((key)=>obj[key]);
|
|
1120
|
+
allValues.push(...rowValues);
|
|
1121
|
+
rowPlaceholders.push(`(${columnKeys.map(()=>'?').join(', ')})`);
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
_isSqlFragment: true,
|
|
1125
|
+
sql: `(${columnKeys.join(', ')}) VALUES ${rowPlaceholders.join(', ')}`,
|
|
1126
|
+
args: allValues
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
if (value && 'object' == typeof value && !Array.isArray(value)) {
|
|
1130
|
+
if (0 === columnKeys.length) columnKeys = Object.keys(value);
|
|
1131
|
+
const values = columnKeys.map((key)=>value[key]);
|
|
1132
|
+
const placeholders = columnKeys.map(()=>'?').join(', ');
|
|
1133
|
+
return {
|
|
1134
|
+
_isSqlFragment: true,
|
|
1135
|
+
sql: `(${columnKeys.join(', ')}) VALUES (${placeholders})`,
|
|
1136
|
+
args: values
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
if (Array.isArray(value) && value.length > 0 && Array.isArray(value[0])) {
|
|
1140
|
+
const rowPlaceholders = value.map((row)=>`(${row.map(()=>'?').join(', ')})`).join(', ');
|
|
1141
|
+
return {
|
|
1142
|
+
_isSqlFragment: true,
|
|
1143
|
+
sql: rowPlaceholders,
|
|
1144
|
+
args: value.flat()
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
return {
|
|
1148
|
+
_isSqlFragment: true,
|
|
1149
|
+
sql: '',
|
|
1150
|
+
args: []
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
function empty() {
|
|
1154
|
+
return {
|
|
1155
|
+
_isSqlFragment: true,
|
|
1156
|
+
sql: '',
|
|
1157
|
+
args: []
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function sql_template_raw(value) {
|
|
1161
|
+
return {
|
|
1162
|
+
_isSqlFragment: true,
|
|
1163
|
+
sql: value,
|
|
1164
|
+
args: []
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
function sql_template_fragment(strings, ...values) {
|
|
1168
|
+
const query = convertTemplateToQuery(strings, values);
|
|
1169
|
+
return {
|
|
1170
|
+
_isSqlFragment: true,
|
|
1171
|
+
...query
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function sql_template_join(fragments, separator = ', ') {
|
|
1175
|
+
if (0 === fragments.length) return empty();
|
|
1176
|
+
const sql = fragments.map((f)=>f.sql).join(separator);
|
|
1177
|
+
const args = fragments.flatMap((f)=>f.args);
|
|
1178
|
+
return {
|
|
1179
|
+
_isSqlFragment: true,
|
|
1180
|
+
sql,
|
|
1181
|
+
args
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function set(data, ...keys) {
|
|
1185
|
+
let columns;
|
|
1186
|
+
columns = keys.length > 0 ? keys : Object.keys(data);
|
|
1187
|
+
if (0 === columns.length) return empty();
|
|
1188
|
+
const setClauses = columns.map((key)=>`${key} = ?`);
|
|
1189
|
+
const values = columns.map((key)=>data[key]);
|
|
1190
|
+
return {
|
|
1191
|
+
_isSqlFragment: true,
|
|
1192
|
+
sql: setClauses.join(', '),
|
|
1193
|
+
args: values
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
function sql_template_where(conditions) {
|
|
1197
|
+
const entries = Object.entries(conditions);
|
|
1198
|
+
if (0 === entries.length) return empty();
|
|
1199
|
+
const whereClauses = entries.map(([key])=>`${key} = ?`);
|
|
1200
|
+
const values = entries.map(([, value])=>value);
|
|
1201
|
+
return {
|
|
1202
|
+
_isSqlFragment: true,
|
|
1203
|
+
sql: whereClauses.join(' AND '),
|
|
1204
|
+
args: values
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
class BunSQLiteAdapter {
|
|
1009
1208
|
type = 'bun-sqlite';
|
|
1010
1209
|
config;
|
|
1011
1210
|
constructor(config){
|
|
1012
1211
|
this.config = config;
|
|
1013
1212
|
}
|
|
1014
1213
|
async connect(config) {
|
|
1015
|
-
const db = new
|
|
1214
|
+
const db = new Database(config.databasePath, {
|
|
1016
1215
|
readonly: config.readonly,
|
|
1017
1216
|
create: config.create ?? true
|
|
1018
1217
|
});
|
|
1019
1218
|
return new BunSQLiteConnection(db);
|
|
1020
1219
|
}
|
|
1021
|
-
async disconnect(tenantId) {
|
|
1022
|
-
// bun:sqlite doesn't require explicit disconnection
|
|
1023
|
-
// File handles are cleaned up by GC
|
|
1024
|
-
}
|
|
1220
|
+
async disconnect(tenantId) {}
|
|
1025
1221
|
async isHealthy() {
|
|
1026
1222
|
try {
|
|
1027
|
-
const db = new
|
|
1223
|
+
const db = new Database(this.config.databasePath, {
|
|
1028
1224
|
readonly: true,
|
|
1029
1225
|
create: false
|
|
1030
1226
|
});
|
|
@@ -1036,37 +1232,54 @@ ODBLiteClient.join;
|
|
|
1036
1232
|
}
|
|
1037
1233
|
}
|
|
1038
1234
|
}
|
|
1039
|
-
|
|
1040
|
-
* Connection implementation for Bun SQLite
|
|
1041
|
-
*/ class BunSQLiteConnection {
|
|
1235
|
+
class BunSQLiteConnection {
|
|
1042
1236
|
db;
|
|
1043
1237
|
inTransaction = false;
|
|
1238
|
+
sql;
|
|
1044
1239
|
constructor(db){
|
|
1045
1240
|
this.db = db;
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1241
|
+
const self = this;
|
|
1242
|
+
this.sql = function(stringsOrValue, ...values) {
|
|
1243
|
+
if (!stringsOrValue || 'object' != typeof stringsOrValue || !('raw' in stringsOrValue)) return sql_template_sql(stringsOrValue, ...values);
|
|
1244
|
+
{
|
|
1245
|
+
const query = convertTemplateToQuery(stringsOrValue, values);
|
|
1246
|
+
const thenableFragment = {
|
|
1247
|
+
_isSqlFragment: true,
|
|
1248
|
+
sql: query.sql,
|
|
1249
|
+
args: query.args
|
|
1250
|
+
};
|
|
1251
|
+
thenableFragment['then'] = function(onFulfilled, onRejected) {
|
|
1252
|
+
return self.execute(query).then((result)=>result.rows).then(onFulfilled, onRejected);
|
|
1253
|
+
};
|
|
1254
|
+
return thenableFragment;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
this.sql.empty = empty;
|
|
1258
|
+
this.sql.raw = sql_template_raw;
|
|
1259
|
+
this.sql.fragment = sql_template_fragment;
|
|
1260
|
+
this.sql.join = sql_template_join;
|
|
1261
|
+
this.sql.set = set;
|
|
1262
|
+
this.sql.where = sql_template_where;
|
|
1263
|
+
}
|
|
1264
|
+
async query(sql, params = []) {
|
|
1057
1265
|
return this.execute(sql, params);
|
|
1058
1266
|
}
|
|
1059
|
-
|
|
1060
|
-
* Execute SQL with parameters
|
|
1061
|
-
*/ async execute(sql, params = []) {
|
|
1267
|
+
async execute(sql, params = []) {
|
|
1062
1268
|
try {
|
|
1063
|
-
// Handle object format { sql, args }
|
|
1064
1269
|
let sqlStr;
|
|
1065
1270
|
let sqlParams;
|
|
1066
1271
|
if ('object' == typeof sql) {
|
|
1272
|
+
if (process.env.DEBUG_SQL) {
|
|
1273
|
+
console.log('[BunSQLite] Executing SQL:', sql.sql);
|
|
1274
|
+
console.log('[BunSQLite] With args:', sql.args);
|
|
1275
|
+
}
|
|
1067
1276
|
sqlStr = sql.sql;
|
|
1068
1277
|
sqlParams = sql.args || [];
|
|
1069
1278
|
} else {
|
|
1279
|
+
if (process.env.DEBUG_SQL) {
|
|
1280
|
+
console.log('[BunSQLite] Executing SQL:', sql);
|
|
1281
|
+
console.log('[BunSQLite] With params:', params);
|
|
1282
|
+
}
|
|
1070
1283
|
sqlStr = sql;
|
|
1071
1284
|
sqlParams = params;
|
|
1072
1285
|
}
|
|
@@ -1088,12 +1301,10 @@ ODBLiteClient.join;
|
|
|
1088
1301
|
};
|
|
1089
1302
|
}
|
|
1090
1303
|
} catch (error) {
|
|
1091
|
-
throw
|
|
1304
|
+
throw error;
|
|
1092
1305
|
}
|
|
1093
1306
|
}
|
|
1094
|
-
|
|
1095
|
-
* Prepare statement for repeated execution
|
|
1096
|
-
*/ prepare(sql) {
|
|
1307
|
+
prepare(sql) {
|
|
1097
1308
|
const stmt = this.db.query(sql);
|
|
1098
1309
|
return {
|
|
1099
1310
|
execute: async (params = [])=>{
|
|
@@ -1118,11 +1329,8 @@ ODBLiteClient.join;
|
|
|
1118
1329
|
get: async (params = [])=>stmt.get(...params)
|
|
1119
1330
|
};
|
|
1120
1331
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
*/ async transaction(fn) {
|
|
1124
|
-
if (this.inTransaction) // Nested transaction - just execute the function
|
|
1125
|
-
return fn(this);
|
|
1332
|
+
async transaction(fn) {
|
|
1333
|
+
if (this.inTransaction) return fn(this);
|
|
1126
1334
|
this.inTransaction = true;
|
|
1127
1335
|
try {
|
|
1128
1336
|
await this.execute('BEGIN');
|
|
@@ -1136,41 +1344,35 @@ ODBLiteClient.join;
|
|
|
1136
1344
|
this.inTransaction = false;
|
|
1137
1345
|
}
|
|
1138
1346
|
}
|
|
1139
|
-
|
|
1140
|
-
* Execute function in transaction (alias for transaction)
|
|
1141
|
-
*/ async begin(fn) {
|
|
1347
|
+
async begin(fn) {
|
|
1142
1348
|
return this.transaction(fn);
|
|
1143
1349
|
}
|
|
1144
|
-
|
|
1145
|
-
* Close connection
|
|
1146
|
-
*/ async close() {
|
|
1350
|
+
async close() {
|
|
1147
1351
|
this.db.close();
|
|
1148
1352
|
}
|
|
1353
|
+
createORM() {
|
|
1354
|
+
const { createORM } = __webpack_require__("./src/orm/index.ts");
|
|
1355
|
+
return createORM(this);
|
|
1356
|
+
}
|
|
1149
1357
|
}
|
|
1150
|
-
|
|
1151
|
-
* LibSQL adapter for DatabaseManager
|
|
1152
|
-
* Wraps @libsql/client with Connection interface
|
|
1153
|
-
*/ class LibSQLAdapter {
|
|
1358
|
+
class LibSQLAdapter {
|
|
1154
1359
|
type = 'libsql';
|
|
1155
1360
|
config;
|
|
1156
1361
|
constructor(config){
|
|
1157
1362
|
this.config = config;
|
|
1158
1363
|
}
|
|
1159
1364
|
async connect(config) {
|
|
1160
|
-
const client =
|
|
1365
|
+
const client = createClient({
|
|
1161
1366
|
url: config.url,
|
|
1162
1367
|
authToken: config.authToken,
|
|
1163
1368
|
encryptionKey: config.encryptionKey
|
|
1164
1369
|
});
|
|
1165
1370
|
return new LibSQLConnection(client);
|
|
1166
1371
|
}
|
|
1167
|
-
async disconnect(tenantId) {
|
|
1168
|
-
// LibSQL clients don't require explicit disconnection
|
|
1169
|
-
// Connections are pooled internally
|
|
1170
|
-
}
|
|
1372
|
+
async disconnect(tenantId) {}
|
|
1171
1373
|
async isHealthy() {
|
|
1172
1374
|
try {
|
|
1173
|
-
const client =
|
|
1375
|
+
const client = createClient({
|
|
1174
1376
|
url: this.config.url,
|
|
1175
1377
|
authToken: this.config.authToken
|
|
1176
1378
|
});
|
|
@@ -1181,41 +1383,61 @@ ODBLiteClient.join;
|
|
|
1181
1383
|
}
|
|
1182
1384
|
}
|
|
1183
1385
|
}
|
|
1184
|
-
|
|
1185
|
-
* Connection implementation for LibSQL
|
|
1186
|
-
*/ class LibSQLConnection {
|
|
1386
|
+
class LibSQLConnection {
|
|
1187
1387
|
client;
|
|
1188
1388
|
txClient;
|
|
1389
|
+
sql;
|
|
1189
1390
|
constructor(client){
|
|
1190
1391
|
this.client = client;
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1392
|
+
const self = this;
|
|
1393
|
+
this.sql = function(stringsOrValue, ...values) {
|
|
1394
|
+
if (!stringsOrValue || 'object' != typeof stringsOrValue || !('raw' in stringsOrValue)) return sql_template_sql(stringsOrValue, ...values);
|
|
1395
|
+
{
|
|
1396
|
+
const query = convertTemplateToQuery(stringsOrValue, values);
|
|
1397
|
+
const thenableFragment = {
|
|
1398
|
+
_isSqlFragment: true,
|
|
1399
|
+
sql: query.sql,
|
|
1400
|
+
args: query.args
|
|
1401
|
+
};
|
|
1402
|
+
thenableFragment['then'] = function(onFulfilled, onRejected) {
|
|
1403
|
+
return self.execute(query).then((result)=>result.rows).then(onFulfilled, onRejected);
|
|
1404
|
+
};
|
|
1405
|
+
return thenableFragment;
|
|
1406
|
+
}
|
|
1407
|
+
};
|
|
1408
|
+
this.sql.empty = empty;
|
|
1409
|
+
this.sql.raw = sql_template_raw;
|
|
1410
|
+
this.sql.fragment = sql_template_fragment;
|
|
1411
|
+
this.sql.join = sql_template_join;
|
|
1412
|
+
this.sql.set = set;
|
|
1413
|
+
this.sql.where = sql_template_where;
|
|
1414
|
+
}
|
|
1415
|
+
async query(sql, params = []) {
|
|
1202
1416
|
return this.execute(sql, params);
|
|
1203
1417
|
}
|
|
1204
|
-
|
|
1205
|
-
* Execute SQL with parameters
|
|
1206
|
-
* Supports both formats: execute(sql, params) and execute({sql, args})
|
|
1207
|
-
*/ async execute(sql, params = []) {
|
|
1418
|
+
async execute(sql, params = []) {
|
|
1208
1419
|
try {
|
|
1209
1420
|
const target = this.txClient || this.client;
|
|
1210
|
-
// Support both execute(sql, params) and execute({sql, args}) formats
|
|
1211
1421
|
let query;
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1422
|
+
if ('string' == typeof sql) {
|
|
1423
|
+
if (process.env.DEBUG_SQL) {
|
|
1424
|
+
console.log('[LibSQL] Executing SQL:', sql);
|
|
1425
|
+
console.log('[LibSQL] With params:', params);
|
|
1426
|
+
}
|
|
1427
|
+
query = {
|
|
1428
|
+
sql,
|
|
1429
|
+
args: params
|
|
1430
|
+
};
|
|
1431
|
+
} else {
|
|
1432
|
+
if (process.env.DEBUG_SQL) {
|
|
1433
|
+
console.log('[LibSQL] Executing SQL:', sql.sql);
|
|
1434
|
+
console.log('[LibSQL] With args:', sql.args);
|
|
1435
|
+
}
|
|
1436
|
+
query = {
|
|
1437
|
+
sql: sql.sql,
|
|
1438
|
+
args: sql.args || []
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1219
1441
|
const result = await target.execute(query);
|
|
1220
1442
|
return {
|
|
1221
1443
|
rows: result.rows,
|
|
@@ -1223,12 +1445,10 @@ ODBLiteClient.join;
|
|
|
1223
1445
|
lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid.toString()) : void 0
|
|
1224
1446
|
};
|
|
1225
1447
|
} catch (error) {
|
|
1226
|
-
throw
|
|
1448
|
+
throw error;
|
|
1227
1449
|
}
|
|
1228
1450
|
}
|
|
1229
|
-
|
|
1230
|
-
* Prepare statement for repeated execution
|
|
1231
|
-
*/ prepare(sql) {
|
|
1451
|
+
prepare(sql) {
|
|
1232
1452
|
return {
|
|
1233
1453
|
execute: async (params = [])=>this.execute(sql, params),
|
|
1234
1454
|
all: async (params = [])=>{
|
|
@@ -1241,12 +1461,8 @@ ODBLiteClient.join;
|
|
|
1241
1461
|
}
|
|
1242
1462
|
};
|
|
1243
1463
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
*/ async transaction(fn) {
|
|
1247
|
-
if (this.txClient) // Nested transaction - just execute the function
|
|
1248
|
-
return fn(this);
|
|
1249
|
-
// Use manual BEGIN/COMMIT/ROLLBACK for simplicity
|
|
1464
|
+
async transaction(fn) {
|
|
1465
|
+
if (this.txClient) return fn(this);
|
|
1250
1466
|
try {
|
|
1251
1467
|
await this.execute('BEGIN');
|
|
1252
1468
|
const result = await fn(this);
|
|
@@ -1257,22 +1473,16 @@ ODBLiteClient.join;
|
|
|
1257
1473
|
throw error;
|
|
1258
1474
|
}
|
|
1259
1475
|
}
|
|
1260
|
-
|
|
1261
|
-
* Execute function in transaction (alias for transaction)
|
|
1262
|
-
*/ async begin(fn) {
|
|
1476
|
+
async begin(fn) {
|
|
1263
1477
|
return this.transaction(fn);
|
|
1264
1478
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
// For now, connections are managed by the client pool
|
|
1479
|
+
async close() {}
|
|
1480
|
+
createORM() {
|
|
1481
|
+
const { createORM } = __webpack_require__("./src/orm/index.ts");
|
|
1482
|
+
return createORM(this);
|
|
1270
1483
|
}
|
|
1271
1484
|
}
|
|
1272
|
-
|
|
1273
|
-
* ODB-Lite adapter for DatabaseManager
|
|
1274
|
-
* Wraps ServiceClient and ODBLiteClient with Connection interface
|
|
1275
|
-
*/ class ODBLiteAdapter {
|
|
1485
|
+
class ODBLiteAdapter {
|
|
1276
1486
|
type = 'odblite';
|
|
1277
1487
|
config;
|
|
1278
1488
|
serviceClient;
|
|
@@ -1285,14 +1495,17 @@ ODBLiteClient.join;
|
|
|
1285
1495
|
}
|
|
1286
1496
|
async connect(config) {
|
|
1287
1497
|
const databaseName = config.databaseName || 'default';
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1498
|
+
const databaseHash = config.databaseHash;
|
|
1499
|
+
if (databaseHash) {
|
|
1500
|
+
const client = new ODBLiteClient({
|
|
1501
|
+
baseUrl: this.config.serviceUrl,
|
|
1502
|
+
apiKey: this.config.apiKey,
|
|
1503
|
+
databaseId: databaseHash
|
|
1504
|
+
});
|
|
1505
|
+
return new ODBLiteConnection(client, this.serviceClient, databaseName, databaseHash);
|
|
1506
|
+
}
|
|
1293
1507
|
const db = await this.serviceClient.getDatabaseByName(databaseName);
|
|
1294
|
-
if (!db) throw new Error(`Database ${databaseName} not found
|
|
1295
|
-
// Create ODBLiteClient for this database
|
|
1508
|
+
if (!db) throw new Error(`Database ${databaseName} not found. Please create it first using the admin API.`);
|
|
1296
1509
|
const client = new ODBLiteClient({
|
|
1297
1510
|
baseUrl: this.config.serviceUrl,
|
|
1298
1511
|
apiKey: this.config.apiKey,
|
|
@@ -1302,8 +1515,7 @@ ODBLiteClient.join;
|
|
|
1302
1515
|
}
|
|
1303
1516
|
async disconnect(tenantId) {
|
|
1304
1517
|
if (tenantId) {
|
|
1305
|
-
const prefix = 'pipeline_tenant_'
|
|
1306
|
-
;
|
|
1518
|
+
const prefix = 'pipeline_tenant_';
|
|
1307
1519
|
const databaseName = `${prefix}${tenantId}`;
|
|
1308
1520
|
try {
|
|
1309
1521
|
await this.serviceClient.deleteDatabase(databaseName);
|
|
@@ -1321,61 +1533,73 @@ ODBLiteClient.join;
|
|
|
1321
1533
|
}
|
|
1322
1534
|
}
|
|
1323
1535
|
}
|
|
1324
|
-
|
|
1325
|
-
* Connection implementation for ODB-Lite
|
|
1326
|
-
*/ class ODBLiteConnection {
|
|
1536
|
+
class ODBLiteConnection {
|
|
1327
1537
|
client;
|
|
1328
1538
|
serviceClient;
|
|
1329
1539
|
inTransaction = false;
|
|
1330
|
-
// Public metadata fields
|
|
1331
1540
|
databaseName;
|
|
1332
1541
|
databaseHash;
|
|
1542
|
+
sql;
|
|
1333
1543
|
constructor(client, serviceClient, databaseName, databaseHash){
|
|
1334
1544
|
this.client = client;
|
|
1335
1545
|
this.serviceClient = serviceClient;
|
|
1336
1546
|
this.databaseName = databaseName;
|
|
1337
1547
|
this.databaseHash = databaseHash;
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1548
|
+
const self = this;
|
|
1549
|
+
this.sql = function(stringsOrValue, ...values) {
|
|
1550
|
+
if (!stringsOrValue || 'object' != typeof stringsOrValue || !('raw' in stringsOrValue)) return sql_template_sql(stringsOrValue, ...values);
|
|
1551
|
+
{
|
|
1552
|
+
const query = convertTemplateToQuery(stringsOrValue, values);
|
|
1553
|
+
const thenableFragment = {
|
|
1554
|
+
_isSqlFragment: true,
|
|
1555
|
+
sql: query.sql,
|
|
1556
|
+
args: query.args
|
|
1557
|
+
};
|
|
1558
|
+
thenableFragment['then'] = function(onFulfilled, onRejected) {
|
|
1559
|
+
return self.execute(query).then((result)=>result.rows).then(onFulfilled, onRejected);
|
|
1560
|
+
};
|
|
1561
|
+
return thenableFragment;
|
|
1562
|
+
}
|
|
1347
1563
|
};
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1564
|
+
this.sql.empty = empty;
|
|
1565
|
+
this.sql.raw = sql_template_raw;
|
|
1566
|
+
this.sql.fragment = sql_template_fragment;
|
|
1567
|
+
this.sql.join = sql_template_join;
|
|
1568
|
+
this.sql.set = set;
|
|
1569
|
+
this.sql.where = sql_template_where;
|
|
1570
|
+
}
|
|
1571
|
+
async query(sql, params = []) {
|
|
1352
1572
|
return this.execute(sql, params);
|
|
1353
1573
|
}
|
|
1354
|
-
|
|
1355
|
-
* Execute SQL with parameters
|
|
1356
|
-
*/ async execute(sql, params = []) {
|
|
1574
|
+
async execute(sql, params = []) {
|
|
1357
1575
|
try {
|
|
1358
|
-
// Handle object format { sql, args }
|
|
1359
1576
|
if ('object' == typeof sql) {
|
|
1577
|
+
if (process.env.DEBUG_SQL) {
|
|
1578
|
+
console.log('[ODBLite] Executing SQL:', sql.sql);
|
|
1579
|
+
console.log('[ODBLite] With args:', sql.args);
|
|
1580
|
+
}
|
|
1360
1581
|
const result = await this.client.sql.execute(sql.sql, sql.args || []);
|
|
1361
1582
|
return {
|
|
1362
1583
|
rows: result.rows,
|
|
1363
|
-
rowsAffected: result.rowsAffected || 0
|
|
1584
|
+
rowsAffected: result.rowsAffected || 0,
|
|
1585
|
+
lastInsertRowid: result.lastInsertRowid
|
|
1364
1586
|
};
|
|
1365
1587
|
}
|
|
1366
|
-
|
|
1588
|
+
if (process.env.DEBUG_SQL) {
|
|
1589
|
+
console.log('[ODBLite] Executing SQL:', sql);
|
|
1590
|
+
console.log('[ODBLite] With params:', params);
|
|
1591
|
+
}
|
|
1367
1592
|
const result = await this.client.sql.execute(sql, params);
|
|
1368
1593
|
return {
|
|
1369
1594
|
rows: result.rows,
|
|
1370
|
-
rowsAffected: result.rowsAffected || 0
|
|
1595
|
+
rowsAffected: result.rowsAffected || 0,
|
|
1596
|
+
lastInsertRowid: result.lastInsertRowid
|
|
1371
1597
|
};
|
|
1372
1598
|
} catch (error) {
|
|
1373
|
-
throw
|
|
1599
|
+
throw error;
|
|
1374
1600
|
}
|
|
1375
1601
|
}
|
|
1376
|
-
|
|
1377
|
-
* Prepare statement for repeated execution
|
|
1378
|
-
*/ prepare(sql) {
|
|
1602
|
+
prepare(sql) {
|
|
1379
1603
|
return {
|
|
1380
1604
|
execute: async (params = [])=>this.execute(sql, params),
|
|
1381
1605
|
all: async (params = [])=>{
|
|
@@ -1388,11 +1612,8 @@ ODBLiteClient.join;
|
|
|
1388
1612
|
}
|
|
1389
1613
|
};
|
|
1390
1614
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
*/ async transaction(fn) {
|
|
1394
|
-
if (this.inTransaction) // Nested transaction - just execute the function
|
|
1395
|
-
return fn(this);
|
|
1615
|
+
async transaction(fn) {
|
|
1616
|
+
if (this.inTransaction) return fn(this);
|
|
1396
1617
|
this.inTransaction = true;
|
|
1397
1618
|
try {
|
|
1398
1619
|
await this.execute('BEGIN');
|
|
@@ -1406,24 +1627,17 @@ ODBLiteClient.join;
|
|
|
1406
1627
|
this.inTransaction = false;
|
|
1407
1628
|
}
|
|
1408
1629
|
}
|
|
1409
|
-
|
|
1410
|
-
* Execute function in transaction (alias for transaction)
|
|
1411
|
-
*/ async begin(fn) {
|
|
1630
|
+
async begin(fn) {
|
|
1412
1631
|
return this.transaction(fn);
|
|
1413
1632
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
// No explicit close needed
|
|
1633
|
+
async close() {}
|
|
1634
|
+
createORM() {
|
|
1635
|
+
const { createORM } = __webpack_require__("./src/orm/index.ts");
|
|
1636
|
+
return createORM(this);
|
|
1419
1637
|
}
|
|
1420
1638
|
}
|
|
1421
|
-
|
|
1422
|
-
* Parse SQL file into statements, separating PRAGMA from regular SQL
|
|
1423
|
-
* Uses custom parser that handles BEGIN...END blocks
|
|
1424
|
-
*/ function parseSQL(sqlContent, options = {}) {
|
|
1639
|
+
function parseSQL(sqlContent, options = {}) {
|
|
1425
1640
|
const separatePragma = options.separatePragma ?? true;
|
|
1426
|
-
// Split SQL statements manually
|
|
1427
1641
|
const statements = splitStatements(sqlContent);
|
|
1428
1642
|
if (!separatePragma) return {
|
|
1429
1643
|
pragmaStatements: [],
|
|
@@ -1441,10 +1655,7 @@ ODBLiteClient.join;
|
|
|
1441
1655
|
regularStatements
|
|
1442
1656
|
};
|
|
1443
1657
|
}
|
|
1444
|
-
|
|
1445
|
-
* Split SQL content into individual statements
|
|
1446
|
-
* Handles comments, quotes, semicolons, and BEGIN...END blocks properly
|
|
1447
|
-
*/ function splitStatements(sqlContent) {
|
|
1658
|
+
function splitStatements(sqlContent) {
|
|
1448
1659
|
const statements = [];
|
|
1449
1660
|
let currentStatement = '';
|
|
1450
1661
|
let inQuote = false;
|
|
@@ -1454,7 +1665,6 @@ ODBLiteClient.join;
|
|
|
1454
1665
|
for (const line of lines){
|
|
1455
1666
|
let processedLine = '';
|
|
1456
1667
|
const lineWithoutComments = line.replace(/--.*$/, '');
|
|
1457
|
-
// Track BEGIN...END depth for this line BEFORE processing characters
|
|
1458
1668
|
if (!inQuote && lineWithoutComments.trim()) {
|
|
1459
1669
|
const beginMatches = lineWithoutComments.match(/\bBEGIN\b/gi);
|
|
1460
1670
|
const endMatches = lineWithoutComments.match(/\bEND\b/gi);
|
|
@@ -1468,22 +1678,17 @@ ODBLiteClient.join;
|
|
|
1468
1678
|
const char = line[i];
|
|
1469
1679
|
const prevChar = i > 0 ? line[i - 1] : '';
|
|
1470
1680
|
const nextChar = i < line.length - 1 ? line[i + 1] : '';
|
|
1471
|
-
|
|
1472
|
-
if (
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
if (char === quoteChar) {
|
|
1477
|
-
inQuote = false;
|
|
1478
|
-
quoteChar = '';
|
|
1479
|
-
}
|
|
1480
|
-
} else {
|
|
1481
|
-
inQuote = true;
|
|
1482
|
-
quoteChar = char;
|
|
1681
|
+
if (!inQuote && '-' === char && '-' === nextChar) break;
|
|
1682
|
+
if (("'" === char || '"' === char) && '\\' !== prevChar) if (inQuote) {
|
|
1683
|
+
if (char === quoteChar) {
|
|
1684
|
+
inQuote = false;
|
|
1685
|
+
quoteChar = '';
|
|
1483
1686
|
}
|
|
1687
|
+
} else {
|
|
1688
|
+
inQuote = true;
|
|
1689
|
+
quoteChar = char;
|
|
1484
1690
|
}
|
|
1485
1691
|
processedLine += char;
|
|
1486
|
-
// Split on semicolon when not in quotes AND not inside BEGIN...END
|
|
1487
1692
|
if (';' === char && !inQuote && 0 === beginEndDepth) {
|
|
1488
1693
|
currentStatement += processedLine;
|
|
1489
1694
|
const stmt = currentStatement.trim();
|
|
@@ -1494,55 +1699,26 @@ ODBLiteClient.join;
|
|
|
1494
1699
|
}
|
|
1495
1700
|
if (processedLine.trim()) currentStatement += `${processedLine}\n`;
|
|
1496
1701
|
}
|
|
1497
|
-
// Handle remaining statement
|
|
1498
1702
|
const finalStmt = currentStatement.trim();
|
|
1499
1703
|
if (finalStmt && !finalStmt.startsWith('--')) statements.push(finalStmt);
|
|
1500
1704
|
return statements;
|
|
1501
1705
|
}
|
|
1502
|
-
|
|
1503
|
-
* Simple split for systems that don't need PRAGMA separation
|
|
1504
|
-
*/ function splitSQLStatements(sqlContent) {
|
|
1706
|
+
function splitSQLStatements(sqlContent) {
|
|
1505
1707
|
const { pragmaStatements, regularStatements } = parseSQL(sqlContent);
|
|
1506
1708
|
return [
|
|
1507
1709
|
...pragmaStatements,
|
|
1508
1710
|
...regularStatements
|
|
1509
1711
|
];
|
|
1510
1712
|
}
|
|
1511
|
-
|
|
1512
|
-
* Database factory for creating and managing multiple databases
|
|
1513
|
-
*
|
|
1514
|
-
* Features:
|
|
1515
|
-
* - Creates multiple databases from single manager instance
|
|
1516
|
-
* - Supports libsql, bun:sqlite, and ODB-Lite backends
|
|
1517
|
-
* - Connection pooling with LRU eviction
|
|
1518
|
-
* - Automatic schema migrations
|
|
1519
|
-
*
|
|
1520
|
-
* @example
|
|
1521
|
-
* ```ts
|
|
1522
|
-
* const dbManager = new DatabaseManager({
|
|
1523
|
-
* backend: 'libsql',
|
|
1524
|
-
* databasePath: './data'
|
|
1525
|
-
* })
|
|
1526
|
-
*
|
|
1527
|
-
* // Create databases
|
|
1528
|
-
* await dbManager.createDatabase('identity', { schemaContent })
|
|
1529
|
-
* await dbManager.createDatabase('analytics', { schemaContent })
|
|
1530
|
-
*
|
|
1531
|
-
* // Get connections
|
|
1532
|
-
* const identityDB = await dbManager.getConnection('identity')
|
|
1533
|
-
* const analyticsDB = await dbManager.getConnection('analytics')
|
|
1534
|
-
* ```
|
|
1535
|
-
*/ class DatabaseManager {
|
|
1713
|
+
class DatabaseManager {
|
|
1536
1714
|
config;
|
|
1537
1715
|
adapter;
|
|
1538
1716
|
connections = new Map();
|
|
1539
|
-
// Connection pool management (LRU)
|
|
1540
1717
|
connectionTimestamps = new Map();
|
|
1541
1718
|
maxConnections;
|
|
1542
1719
|
constructor(config){
|
|
1543
1720
|
this.config = config;
|
|
1544
1721
|
this.maxConnections = config.connectionPoolSize || 10;
|
|
1545
|
-
// Create appropriate adapter based on backend
|
|
1546
1722
|
switch(config.backend){
|
|
1547
1723
|
case 'bun-sqlite':
|
|
1548
1724
|
this.adapter = new BunSQLiteAdapter({
|
|
@@ -1564,147 +1740,92 @@ ODBLiteClient.join;
|
|
|
1564
1740
|
throw new Error(`Unknown backend type: ${config.backend}`);
|
|
1565
1741
|
}
|
|
1566
1742
|
}
|
|
1567
|
-
|
|
1568
|
-
* Create a new database
|
|
1569
|
-
* @param name - Database name (becomes filename or ODB-Lite database name)
|
|
1570
|
-
* @param options - Optional creation options
|
|
1571
|
-
* @returns Connection object (contains databaseHash for ODB-Lite)
|
|
1572
|
-
*/ async createDatabase(name, options) {
|
|
1743
|
+
async createDatabase(name, options) {
|
|
1573
1744
|
console.log(`📦 Creating database: ${name}`);
|
|
1574
|
-
// Build database-specific config
|
|
1575
1745
|
const dbConfig = this.getDatabaseConfig(name);
|
|
1576
|
-
// Create connection
|
|
1577
1746
|
const conn = await this.adapter.connect(dbConfig);
|
|
1578
|
-
// Run migrations if schema provided
|
|
1579
1747
|
if (options?.schemaContent) await this.runMigrations(conn, {
|
|
1580
1748
|
schemaContent: options.schemaContent
|
|
1581
1749
|
});
|
|
1582
|
-
// Store connection
|
|
1583
1750
|
this.connections.set(name, conn);
|
|
1584
1751
|
this.connectionTimestamps.set(name, Date.now());
|
|
1585
|
-
// Evict LRU if pool is full
|
|
1586
1752
|
if (this.connections.size > this.maxConnections) await this.evictLRU();
|
|
1587
1753
|
console.log(`✅ Database created: ${name}`);
|
|
1588
|
-
// Return connection so caller can access metadata (e.g., databaseHash for ODB-Lite)
|
|
1589
1754
|
return conn;
|
|
1590
1755
|
}
|
|
1591
|
-
|
|
1592
|
-
* Check if a database exists
|
|
1593
|
-
* @param name - Database name
|
|
1594
|
-
* @returns true if database exists, false otherwise
|
|
1595
|
-
*
|
|
1596
|
-
* Note: For local backends (libsql, bun-sqlite), checks file existence.
|
|
1597
|
-
* For ODB-Lite, checks if database is in connection cache.
|
|
1598
|
-
*/ async databaseExists(name) {
|
|
1599
|
-
// Check if already in connection cache
|
|
1756
|
+
async databaseExists(name) {
|
|
1600
1757
|
if (this.connections.has(name)) return true;
|
|
1601
|
-
// For local file-based backends, check if file exists
|
|
1602
1758
|
switch(this.config.backend){
|
|
1603
1759
|
case 'bun-sqlite':
|
|
1604
1760
|
{
|
|
1605
1761
|
const dbPath = `${this.config.databasePath}/${name}.db`;
|
|
1606
|
-
return
|
|
1762
|
+
return node_fs.existsSync(dbPath);
|
|
1607
1763
|
}
|
|
1608
1764
|
case 'libsql':
|
|
1609
1765
|
{
|
|
1610
1766
|
const dbPath = `${this.config.databasePath}/${name}.db`;
|
|
1611
|
-
return
|
|
1767
|
+
return node_fs.existsSync(dbPath);
|
|
1612
1768
|
}
|
|
1613
1769
|
case 'odblite':
|
|
1614
|
-
// For ODB-Lite, only return true if in cache
|
|
1615
|
-
// (no way to check remote database existence without connecting)
|
|
1616
1770
|
return false;
|
|
1617
1771
|
default:
|
|
1618
1772
|
return false;
|
|
1619
1773
|
}
|
|
1620
1774
|
}
|
|
1621
|
-
|
|
1622
|
-
* Get connection to a database
|
|
1623
|
-
* @param name - Database name
|
|
1624
|
-
*
|
|
1625
|
-
* If database exists on disk but not in cache, it will be connected automatically.
|
|
1626
|
-
* If database doesn't exist at all, an error will be thrown.
|
|
1627
|
-
*/ async getConnection(name) {
|
|
1628
|
-
// Check if connection exists in cache
|
|
1775
|
+
async getConnection(name) {
|
|
1629
1776
|
let conn = this.connections.get(name);
|
|
1630
1777
|
if (conn) {
|
|
1631
1778
|
this.connectionTimestamps.set(name, Date.now());
|
|
1632
1779
|
return conn;
|
|
1633
1780
|
}
|
|
1634
|
-
// Check if database exists on disk
|
|
1635
1781
|
const exists = await this.databaseExists(name);
|
|
1636
1782
|
if (exists) {
|
|
1637
|
-
// Connect to existing database
|
|
1638
1783
|
const dbConfig = this.getDatabaseConfig(name);
|
|
1639
1784
|
conn = await this.adapter.connect(dbConfig);
|
|
1640
|
-
// Store connection
|
|
1641
1785
|
this.connections.set(name, conn);
|
|
1642
1786
|
this.connectionTimestamps.set(name, Date.now());
|
|
1643
|
-
// Evict LRU if pool is full
|
|
1644
1787
|
if (this.connections.size > this.maxConnections) await this.evictLRU();
|
|
1645
1788
|
return conn;
|
|
1646
1789
|
}
|
|
1647
|
-
// If database doesn't exist, throw error
|
|
1648
1790
|
throw new Error(`Database "${name}" not found. Call createDatabase("${name}", { schemaContent }) first.`);
|
|
1649
1791
|
}
|
|
1650
|
-
|
|
1651
|
-
* Delete a database
|
|
1652
|
-
* @param name - Database name
|
|
1653
|
-
*/ async deleteDatabase(name) {
|
|
1792
|
+
async deleteDatabase(name) {
|
|
1654
1793
|
console.log(`🗑️ Deleting database: ${name}`);
|
|
1655
|
-
// Close connection if exists
|
|
1656
1794
|
const conn = this.connections.get(name);
|
|
1657
1795
|
if (conn) {
|
|
1658
1796
|
await conn.close();
|
|
1659
1797
|
this.connections.delete(name);
|
|
1660
1798
|
this.connectionTimestamps.delete(name);
|
|
1661
1799
|
}
|
|
1662
|
-
// Backend-specific deletion
|
|
1663
1800
|
await this.adapter.disconnect(name);
|
|
1664
1801
|
console.log(`✅ Database deleted: ${name}`);
|
|
1665
1802
|
}
|
|
1666
|
-
|
|
1667
|
-
* Execute SQL content on a database connection
|
|
1668
|
-
* Useful for running migrations or initial schema
|
|
1669
|
-
* @param name - Database name
|
|
1670
|
-
* @param sqlContent - SQL content to execute
|
|
1671
|
-
*/ async executeSQLFile(name, sqlContent) {
|
|
1803
|
+
async executeSQLFile(name, sqlContent) {
|
|
1672
1804
|
const conn = await this.getConnection(name);
|
|
1673
1805
|
await this.runMigrations(conn, {
|
|
1674
1806
|
schemaContent: sqlContent
|
|
1675
1807
|
});
|
|
1676
1808
|
}
|
|
1677
|
-
|
|
1678
|
-
* List all managed databases
|
|
1679
|
-
*/ async listDatabases() {
|
|
1809
|
+
async listDatabases() {
|
|
1680
1810
|
return Array.from(this.connections.keys());
|
|
1681
1811
|
}
|
|
1682
|
-
|
|
1683
|
-
* Close all connections
|
|
1684
|
-
*/ async close() {
|
|
1812
|
+
async close() {
|
|
1685
1813
|
console.log(`🔌 Closing all database connections...`);
|
|
1686
1814
|
for (const [name, conn] of this.connections)await conn.close();
|
|
1687
1815
|
this.connections.clear();
|
|
1688
1816
|
this.connectionTimestamps.clear();
|
|
1689
1817
|
console.log(`✅ All connections closed`);
|
|
1690
1818
|
}
|
|
1691
|
-
|
|
1692
|
-
* Run migrations on a connection
|
|
1693
|
-
*/ async runMigrations(conn, config) {
|
|
1694
|
-
// Parse SQL statements
|
|
1819
|
+
async runMigrations(conn, config) {
|
|
1695
1820
|
const { pragmaStatements, regularStatements } = parseSQL(config.schemaContent);
|
|
1696
|
-
// Execute PRAGMA statements first
|
|
1697
1821
|
for (const pragma of pragmaStatements)try {
|
|
1698
1822
|
await conn.execute(pragma);
|
|
1699
1823
|
} catch (error) {
|
|
1700
|
-
// Ignore PRAGMA errors (they might not be supported on all backends)
|
|
1701
1824
|
console.log(`⚠️ PRAGMA note: ${error.message}`);
|
|
1702
1825
|
}
|
|
1703
|
-
// Execute regular statements
|
|
1704
1826
|
for (const statement of regularStatements)try {
|
|
1705
1827
|
await conn.execute(statement);
|
|
1706
1828
|
} catch (error) {
|
|
1707
|
-
// Ignore "already exists" errors for idempotency
|
|
1708
1829
|
if (error.message?.includes('already exists') || error.message?.includes('no such column') || error.message?.includes('no such table')) {
|
|
1709
1830
|
console.log(` ⚠️ Skipping: ${error.message}`);
|
|
1710
1831
|
continue;
|
|
@@ -1714,9 +1835,7 @@ ODBLiteClient.join;
|
|
|
1714
1835
|
}
|
|
1715
1836
|
console.log(`✅ Migrations completed (${regularStatements.length} statements)`);
|
|
1716
1837
|
}
|
|
1717
|
-
|
|
1718
|
-
* Get database-specific configuration
|
|
1719
|
-
*/ getDatabaseConfig(name) {
|
|
1838
|
+
getDatabaseConfig(name) {
|
|
1720
1839
|
switch(this.config.backend){
|
|
1721
1840
|
case 'bun-sqlite':
|
|
1722
1841
|
return {
|
|
@@ -1737,9 +1856,7 @@ ODBLiteClient.join;
|
|
|
1737
1856
|
throw new Error(`Unknown backend: ${this.config.backend}`);
|
|
1738
1857
|
}
|
|
1739
1858
|
}
|
|
1740
|
-
|
|
1741
|
-
* Evict least recently used connection
|
|
1742
|
-
*/ async evictLRU() {
|
|
1859
|
+
async evictLRU() {
|
|
1743
1860
|
let oldestKey = null;
|
|
1744
1861
|
let oldestTime = 1 / 0;
|
|
1745
1862
|
for (const [key, timestamp] of this.connectionTimestamps)if (timestamp < oldestTime) {
|
|
@@ -1755,11 +1872,20 @@ ODBLiteClient.join;
|
|
|
1755
1872
|
}
|
|
1756
1873
|
}
|
|
1757
1874
|
}
|
|
1758
|
-
|
|
1759
|
-
// Core query client exports (postgres.js-like interface)
|
|
1760
|
-
// Service management exports (high-level tenant database management)
|
|
1761
|
-
// Database Manager exports (unified multi-backend database abstraction)
|
|
1762
|
-
// Export error classes
|
|
1763
|
-
// Export static utility functions for easy access
|
|
1875
|
+
var orm = __webpack_require__("./src/orm/index.ts");
|
|
1764
1876
|
const { raw: src_raw, identifier: src_identifier, where: src_where, insertValues: src_insertValues, updateSet: src_updateSet, join: src_join } = ODBLiteClient;
|
|
1765
|
-
|
|
1877
|
+
var __webpack_exports__ORM = orm.ORM;
|
|
1878
|
+
var __webpack_exports__and = orm.and;
|
|
1879
|
+
var __webpack_exports__createORM = orm.createORM;
|
|
1880
|
+
var __webpack_exports__eq = orm.eq;
|
|
1881
|
+
var __webpack_exports__gt = orm.gt;
|
|
1882
|
+
var __webpack_exports__gte = orm.gte;
|
|
1883
|
+
var __webpack_exports__inArray = orm.inArray;
|
|
1884
|
+
var __webpack_exports__isNotNull = orm.isNotNull;
|
|
1885
|
+
var __webpack_exports__isNull = orm.isNull;
|
|
1886
|
+
var __webpack_exports__like = orm.like;
|
|
1887
|
+
var __webpack_exports__lt = orm.lt;
|
|
1888
|
+
var __webpack_exports__lte = orm.lte;
|
|
1889
|
+
var __webpack_exports__ne = orm.ne;
|
|
1890
|
+
var __webpack_exports__or = orm.or;
|
|
1891
|
+
export { BunSQLiteAdapter, ConnectionError, DatabaseManager, HTTPClient, LibSQLAdapter, ODBLiteAdapter, ODBLiteClient, ODBLiteError, ODBLiteTransaction, types_QueryError as QueryError, sql_parser_SQLParser as SQLParser, ServiceClient, SimpleTransaction, convertTemplateToQuery, odblite as default, empty, fragment, src_identifier as identifier, src_insertValues as insertValues, src_join as join, odblite, parseSQL, src_raw as raw, sql_template_raw as rawSQL, set, splitSQLStatements, sql_parser_sql as sql, sql_template_fragment as sqlFragment, sql_template_join as sqlJoin, sql_template_sql as sqlTemplate, sql_template_where as sqlWhere, src_updateSet as updateSet, src_where as where, __webpack_exports__ORM as ORM, __webpack_exports__and as and, __webpack_exports__createORM as createORM, __webpack_exports__eq as eq, __webpack_exports__gt as gt, __webpack_exports__gte as gte, __webpack_exports__inArray as inArray, __webpack_exports__isNotNull as isNotNull, __webpack_exports__isNull as isNull, __webpack_exports__like as like, __webpack_exports__lt as lt, __webpack_exports__lte as lte, __webpack_exports__ne as ne, __webpack_exports__or as or };
|