@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/index.js CHANGED
@@ -1,8 +1,367 @@
1
- import * as __WEBPACK_EXTERNAL_MODULE_bun_sqlite__ from "bun:sqlite";
2
- import * as __WEBPACK_EXTERNAL_MODULE__libsql_client__ from "@libsql/client";
3
- import * as __WEBPACK_EXTERNAL_MODULE_node_fs__ from "node:fs";
4
- // Core types for the ODBLite client
5
- // Error types
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
- * SQL template string parser that converts postgres.js-style template strings
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
- // For simple objects with primitive values, we need to guess context
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__')) // Handle raw SQL: sql`SELECT * FROM ${raw('users')}`
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) // Convert Date to ISO string for SQLite
318
- return value.toISOString();
319
- if (value instanceof Buffer) // Convert Buffer to Uint8Array for LibSQL
320
- return new Uint8Array(value);
321
- if ('boolean' == typeof value) // SQLite uses 0/1 for booleans
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
- // Determine if this is callback-style or traditional
615
- if ('function' == typeof modeOrCallback) // begin(callback)
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
- * Close connection (no-op for HTTP client)
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
- * Bun SQLite adapter for DatabaseManager
1007
- * Wraps bun:sqlite with Connection interface
1008
- */ class BunSQLiteAdapter {
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 __WEBPACK_EXTERNAL_MODULE_bun_sqlite__.Database(config.databasePath, {
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 __WEBPACK_EXTERNAL_MODULE_bun_sqlite__.Database(this.config.databasePath, {
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
- * Template tag query (postgres.js-like)
1049
- */ async sql(strings, ...values) {
1050
- // Build SQL from template
1051
- const sqlStr = strings.reduce((acc, str, i)=>acc + str + (i < values.length ? '?' : ''), '');
1052
- return this.execute(sqlStr, values);
1053
- }
1054
- /**
1055
- * Query with SQL string and parameters (alias for execute)
1056
- */ async query(sql, params = []) {
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 new Error(`SQL execution failed: ${error.message}`);
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
- * Execute function in transaction
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 = (0, __WEBPACK_EXTERNAL_MODULE__libsql_client__.createClient)({
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 = (0, __WEBPACK_EXTERNAL_MODULE__libsql_client__.createClient)({
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
- * Template tag query (postgres.js-like)
1194
- */ async sql(strings, ...values) {
1195
- // Build SQL from template
1196
- const sqlStr = strings.reduce((acc, str, i)=>acc + str + (i < values.length ? '?' : ''), '');
1197
- return this.execute(sqlStr, values);
1198
- }
1199
- /**
1200
- * Query with SQL string and parameters (alias for execute)
1201
- */ async query(sql, params = []) {
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
- query = 'string' == typeof sql ? {
1213
- sql,
1214
- args: params
1215
- } : {
1216
- sql: sql.sql,
1217
- args: sql.args || []
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 new Error(`SQL execution failed: ${error.message}`);
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
- * Execute function in transaction
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
- * Close connection
1267
- */ async close() {
1268
- // LibSQL client.close() if available in future versions
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
- // Ensure database exists
1289
- const dbInfo = await this.serviceClient.getDatabaseByName(databaseName);
1290
- if (!dbInfo) // Create database - pass nodeId from config if provided, otherwise server selects
1291
- await this.serviceClient.createDatabase(databaseName, this.config.nodeId);
1292
- // Get fresh database info
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 after creation`);
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_' // TODO: make configurable
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
- * Template tag query (postgres.js-like)
1341
- */ async sql(strings, ...values) {
1342
- // ODBLiteClient.sql is a function that returns a Promise
1343
- const result = await this.client.sql(strings, ...values);
1344
- return {
1345
- rows: result.rows,
1346
- rowsAffected: result.rowsAffected || 0
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
- * Query with SQL string and parameters (alias for execute)
1351
- */ async query(sql, params = []) {
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
- // Handle string format
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 new Error(`SQL execution failed: ${error.message}`);
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
- * Execute function in transaction
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
- * Close connection
1416
- */ async close() {
1417
- // ODBLiteClient connections are stateless HTTP requests
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
- // Handle single-line comments
1472
- if (!inQuote && '-' === char && '-' === nextChar) break; // Skip rest of line
1473
- // Track quotes
1474
- if (("'" === char || '"' === char) && '\\' !== prevChar) {
1475
- if (inQuote) {
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 __WEBPACK_EXTERNAL_MODULE_node_fs__["default"].existsSync(dbPath);
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 __WEBPACK_EXTERNAL_MODULE_node_fs__["default"].existsSync(dbPath);
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
- // Main entry point for ODB Client
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
- export { BunSQLiteAdapter, ConnectionError, DatabaseManager, HTTPClient, LibSQLAdapter, ODBLiteAdapter, ODBLiteClient, ODBLiteError, ODBLiteTransaction, types_QueryError as QueryError, sql_parser_SQLParser as SQLParser, ServiceClient, SimpleTransaction, odblite as default, fragment, src_identifier as identifier, src_insertValues as insertValues, src_join as join, odblite, parseSQL, src_raw as raw, splitSQLStatements, sql_parser_sql as sql, src_updateSet as updateSet, src_where as where };
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 };