@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/src/index.ts CHANGED
@@ -18,8 +18,37 @@ export {
18
18
  BunSQLiteAdapter,
19
19
  LibSQLAdapter,
20
20
  ODBLiteAdapter,
21
+ // SQL Template helpers (postgres.js-compatible)
22
+ sql as sqlTemplate,
23
+ raw as rawSQL,
24
+ empty,
25
+ fragment as sqlFragment,
26
+ join as sqlJoin,
27
+ set,
28
+ where as sqlWhere,
29
+ convertTemplateToQuery,
21
30
  } from './database/index.ts';
22
31
 
32
+ // ORM exports (Drizzle-like query builder)
33
+ export {
34
+ ORM,
35
+ createORM,
36
+ eq,
37
+ ne,
38
+ gt,
39
+ gte,
40
+ lt,
41
+ lte,
42
+ like,
43
+ inArray,
44
+ isNull,
45
+ isNotNull,
46
+ and,
47
+ or,
48
+ type WhereCondition,
49
+ type OrderByDirection,
50
+ } from './orm/index.ts';
51
+
23
52
  // Export all types
24
53
  export type {
25
54
  ODBLiteConfig,
@@ -56,6 +85,8 @@ export type {
56
85
  TenantInfo,
57
86
  ParsedStatements,
58
87
  SQLParserOptions,
88
+ SqlQuery,
89
+ SqlFragment,
59
90
  } from './database/index.ts';
60
91
 
61
92
  // Export error classes
@@ -0,0 +1,538 @@
1
+ /**
2
+ * Simple ORM for Database Operations
3
+ *
4
+ * Provides a Drizzle-like API for common database operations
5
+ * Wraps the raw SQL query interface with a more intuitive syntax
6
+ */
7
+
8
+ import type { Connection } from '../database/index.ts'
9
+
10
+ // ============================================================
11
+ // TYPES
12
+ // ============================================================
13
+
14
+ type WhereCondition = {
15
+ sql: string
16
+ params: any[]
17
+ }
18
+
19
+ type OrderByDirection = 'ASC' | 'DESC'
20
+
21
+ // ============================================================
22
+ // WHERE CLAUSE BUILDERS
23
+ // ============================================================
24
+
25
+ /**
26
+ * Equal condition
27
+ */
28
+ export function eq(field: string, value: any): WhereCondition {
29
+ return {
30
+ sql: `${field} = ?`,
31
+ params: [value]
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Not equal condition
37
+ */
38
+ export function ne(field: string, value: any): WhereCondition {
39
+ return {
40
+ sql: `${field} != ?`,
41
+ params: [value]
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Greater than condition
47
+ */
48
+ export function gt(field: string, value: any): WhereCondition {
49
+ return {
50
+ sql: `${field} > ?`,
51
+ params: [value]
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Greater than or equal condition
57
+ */
58
+ export function gte(field: string, value: any): WhereCondition {
59
+ return {
60
+ sql: `${field} >= ?`,
61
+ params: [value]
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Less than condition
67
+ */
68
+ export function lt(field: string, value: any): WhereCondition {
69
+ return {
70
+ sql: `${field} < ?`,
71
+ params: [value]
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Less than or equal condition
77
+ */
78
+ export function lte(field: string, value: any): WhereCondition {
79
+ return {
80
+ sql: `${field} <= ?`,
81
+ params: [value]
82
+ }
83
+ }
84
+
85
+ /**
86
+ * LIKE condition
87
+ */
88
+ export function like(field: string, pattern: string): WhereCondition {
89
+ return {
90
+ sql: `${field} LIKE ?`,
91
+ params: [pattern]
92
+ }
93
+ }
94
+
95
+ /**
96
+ * IN condition
97
+ */
98
+ export function inArray(field: string, values: any[]): WhereCondition {
99
+ const placeholders = values.map(() => '?').join(', ')
100
+ return {
101
+ sql: `${field} IN (${placeholders})`,
102
+ params: values
103
+ }
104
+ }
105
+
106
+ /**
107
+ * IS NULL condition
108
+ */
109
+ export function isNull(field: string): WhereCondition {
110
+ return {
111
+ sql: `${field} IS NULL`,
112
+ params: []
113
+ }
114
+ }
115
+
116
+ /**
117
+ * IS NOT NULL condition
118
+ */
119
+ export function isNotNull(field: string): WhereCondition {
120
+ return {
121
+ sql: `${field} IS NOT NULL`,
122
+ params: []
123
+ }
124
+ }
125
+
126
+ /**
127
+ * AND combinator
128
+ */
129
+ export function and(...conditions: WhereCondition[]): WhereCondition {
130
+ const sql = conditions.map(c => `(${c.sql})`).join(' AND ')
131
+ const params = conditions.flatMap(c => c.params)
132
+ return { sql, params }
133
+ }
134
+
135
+ /**
136
+ * OR combinator
137
+ */
138
+ export function or(...conditions: WhereCondition[]): WhereCondition {
139
+ const sql = conditions.map(c => `(${c.sql})`).join(' OR ')
140
+ const params = conditions.flatMap(c => c.params)
141
+ return { sql, params }
142
+ }
143
+
144
+ // ============================================================
145
+ // QUERY BUILDERS
146
+ // ============================================================
147
+
148
+ /**
149
+ * Select Query Builder
150
+ */
151
+ class SelectBuilder<T = any> {
152
+ private db: Connection
153
+ private tableName: string
154
+ private selectFields: string[] = ['*']
155
+ private whereConditions: WhereCondition[] = []
156
+ private orderByField?: string
157
+ private orderByDir: OrderByDirection = 'ASC'
158
+ private limitValue?: number
159
+ private offsetValue?: number
160
+
161
+ constructor(db: Connection, tableName: string) {
162
+ this.db = db
163
+ this.tableName = tableName
164
+ }
165
+
166
+ /**
167
+ * Specify which fields to select
168
+ */
169
+ select(fields: Record<string, any> = {}): this {
170
+ if (Object.keys(fields).length === 0) {
171
+ this.selectFields = ['*']
172
+ } else {
173
+ this.selectFields = Object.keys(fields)
174
+ }
175
+ return this
176
+ }
177
+
178
+ /**
179
+ * Add WHERE clause (can be called multiple times, will be combined with AND)
180
+ */
181
+ where(condition: WhereCondition): this {
182
+ this.whereConditions.push(condition)
183
+ return this
184
+ }
185
+
186
+ /**
187
+ * Add ORDER BY clause
188
+ */
189
+ orderBy(field: string, direction: OrderByDirection = 'ASC'): this {
190
+ this.orderByField = field
191
+ this.orderByDir = direction
192
+ return this
193
+ }
194
+
195
+ /**
196
+ * Add LIMIT clause
197
+ */
198
+ limit(value: number): this {
199
+ this.limitValue = value
200
+ return this
201
+ }
202
+
203
+ /**
204
+ * Add OFFSET clause
205
+ */
206
+ offset(value: number): this {
207
+ this.offsetValue = value
208
+ return this
209
+ }
210
+
211
+ /**
212
+ * Execute the query
213
+ */
214
+ async execute(): Promise<T[]> {
215
+ const fields = this.selectFields.join(', ')
216
+ const params: any[] = []
217
+ let sql = `SELECT ${fields} FROM ${this.tableName}`
218
+
219
+ // Combine all where conditions with AND
220
+ if (this.whereConditions.length > 0) {
221
+ const combinedCondition = this.whereConditions.length === 1
222
+ ? this.whereConditions[0]!
223
+ : and(...this.whereConditions)
224
+ sql += ` WHERE ${combinedCondition.sql}`
225
+ params.push(...combinedCondition.params)
226
+ }
227
+
228
+ if (this.orderByField) {
229
+ sql += ` ORDER BY ${this.orderByField} ${this.orderByDir}`
230
+ }
231
+
232
+ if (this.limitValue !== undefined) {
233
+ sql += ` LIMIT ?`
234
+ params.push(this.limitValue)
235
+ }
236
+
237
+ if (this.offsetValue !== undefined) {
238
+ sql += ` OFFSET ?`
239
+ params.push(this.offsetValue)
240
+ }
241
+
242
+ const result = await this.db.execute(sql, params)
243
+ return result.rows as T[]
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Insert Query Builder
249
+ */
250
+ class InsertBuilder<T = any> {
251
+ private db: Connection
252
+ private tableName: string
253
+ private insertValues: Record<string, any> = {}
254
+ private shouldReturn = false
255
+
256
+ constructor(db: Connection, tableName: string) {
257
+ this.db = db
258
+ this.tableName = tableName
259
+ }
260
+
261
+ /**
262
+ * Specify values to insert
263
+ */
264
+ values(data: Record<string, any>): this {
265
+ this.insertValues = data
266
+ return this
267
+ }
268
+
269
+ /**
270
+ * Return inserted row
271
+ */
272
+ returning(): this {
273
+ this.shouldReturn = true
274
+ return this
275
+ }
276
+
277
+ /**
278
+ * Execute the query
279
+ */
280
+ async execute(): Promise<T[]> {
281
+ const fields = Object.keys(this.insertValues)
282
+ const placeholders = fields.map(() => '?').join(', ')
283
+ const values = fields.map(f => this.insertValues[f])
284
+
285
+ let sql = `
286
+ INSERT INTO ${this.tableName} (${fields.join(', ')})
287
+ VALUES (${placeholders})
288
+ `
289
+
290
+ await this.db.execute(sql, values)
291
+
292
+ if (this.shouldReturn) {
293
+ // Get the last inserted row
294
+ const selectSql = `SELECT * FROM ${this.tableName} ORDER BY id DESC LIMIT 1`
295
+ const result = await this.db.execute(selectSql, [])
296
+ return result.rows as T[]
297
+ }
298
+
299
+ return []
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Update Query Builder
305
+ */
306
+ class UpdateBuilder<T = any> {
307
+ private db: Connection
308
+ private tableName: string
309
+ private updateValues: Record<string, any> = {}
310
+ private whereConditions: WhereCondition[] = []
311
+ private shouldReturn = false
312
+
313
+ constructor(db: Connection, tableName: string) {
314
+ this.db = db
315
+ this.tableName = tableName
316
+ }
317
+
318
+ /**
319
+ * Specify values to update
320
+ */
321
+ set(data: Record<string, any>): this {
322
+ this.updateValues = data
323
+ return this
324
+ }
325
+
326
+ /**
327
+ * Add WHERE clause (can be called multiple times, will be combined with AND)
328
+ */
329
+ where(condition: WhereCondition): this {
330
+ this.whereConditions.push(condition)
331
+ return this
332
+ }
333
+
334
+ /**
335
+ * Return updated row
336
+ */
337
+ returning(): this {
338
+ this.shouldReturn = true
339
+ return this
340
+ }
341
+
342
+ /**
343
+ * Execute the query
344
+ */
345
+ async execute(): Promise<T[]> {
346
+ const fields = Object.keys(this.updateValues)
347
+ const setClause = fields.map(f => `${f} = ?`).join(', ')
348
+ const values = fields.map(f => this.updateValues[f])
349
+
350
+ let sql = `UPDATE ${this.tableName} SET ${setClause}`
351
+ const params = [...values]
352
+
353
+ // Combine all where conditions with AND
354
+ if (this.whereConditions.length > 0) {
355
+ const combinedCondition = this.whereConditions.length === 1
356
+ ? this.whereConditions[0]!
357
+ : and(...this.whereConditions)
358
+ sql += ` WHERE ${combinedCondition.sql}`
359
+ params.push(...combinedCondition.params)
360
+ }
361
+
362
+ await this.db.execute(sql, params)
363
+
364
+ if (this.shouldReturn && this.whereConditions.length > 0) {
365
+ // Get the updated rows
366
+ const combinedCondition = this.whereConditions.length === 1
367
+ ? this.whereConditions[0]!
368
+ : and(...this.whereConditions)
369
+ const selectSql = `SELECT * FROM ${this.tableName} WHERE ${combinedCondition.sql}`
370
+ const result = await this.db.execute(selectSql, combinedCondition.params)
371
+ return result.rows as T[]
372
+ }
373
+
374
+ return []
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Delete Query Builder
380
+ */
381
+ class DeleteBuilder {
382
+ private db: Connection
383
+ private tableName: string
384
+ private whereConditions: WhereCondition[] = []
385
+
386
+ constructor(db: Connection, tableName: string) {
387
+ this.db = db
388
+ this.tableName = tableName
389
+ }
390
+
391
+ /**
392
+ * Add WHERE clause (can be called multiple times, will be combined with AND)
393
+ */
394
+ where(condition: WhereCondition): this {
395
+ this.whereConditions.push(condition)
396
+ return this
397
+ }
398
+
399
+ /**
400
+ * Execute the query
401
+ */
402
+ async execute(): Promise<void> {
403
+ let sql = `DELETE FROM ${this.tableName}`
404
+ const params: any[] = []
405
+
406
+ // Combine all where conditions with AND
407
+ if (this.whereConditions.length > 0) {
408
+ const combinedCondition = this.whereConditions.length === 1
409
+ ? this.whereConditions[0]!
410
+ : and(...this.whereConditions)
411
+ sql += ` WHERE ${combinedCondition.sql}`
412
+ params.push(...combinedCondition.params)
413
+ }
414
+
415
+ await this.db.execute(sql, params)
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Table Query Builder
421
+ */
422
+ class TableQueryBuilder {
423
+ private db: Connection
424
+ private tableName: string
425
+
426
+ constructor(db: Connection, tableName: string) {
427
+ this.db = db
428
+ this.tableName = tableName
429
+ }
430
+
431
+ /**
432
+ * Start a SELECT query
433
+ */
434
+ select(fields?: Record<string, any>): SelectBuilder {
435
+ const builder = new SelectBuilder(this.db, this.tableName)
436
+ if (fields) {
437
+ builder.select(fields)
438
+ }
439
+ return builder
440
+ }
441
+
442
+ /**
443
+ * Start an INSERT query
444
+ */
445
+ insert(): InsertBuilder {
446
+ return new InsertBuilder(this.db, this.tableName)
447
+ }
448
+
449
+ /**
450
+ * Start an UPDATE query
451
+ */
452
+ update(): UpdateBuilder {
453
+ return new UpdateBuilder(this.db, this.tableName)
454
+ }
455
+
456
+ /**
457
+ * Start a DELETE query
458
+ */
459
+ delete(): DeleteBuilder {
460
+ return new DeleteBuilder(this.db, this.tableName)
461
+ }
462
+ }
463
+
464
+ // ============================================================
465
+ // ORM CONNECTION WRAPPER
466
+ // ============================================================
467
+
468
+ /**
469
+ * ORM Wrapper for Connection
470
+ * Provides Drizzle-like syntax for database operations
471
+ */
472
+ export class ORM {
473
+ private db: Connection
474
+
475
+ constructor(db: Connection) {
476
+ this.db = db
477
+ }
478
+
479
+ /**
480
+ * Access a table for querying
481
+ */
482
+ table(tableName: string): TableQueryBuilder {
483
+ return new TableQueryBuilder(this.db, tableName)
484
+ }
485
+
486
+ /**
487
+ * Select from table (shorthand)
488
+ */
489
+ select(fields?: Record<string, any>) {
490
+ return {
491
+ from: (tableName: string) => {
492
+ const builder = new SelectBuilder(this.db, tableName)
493
+ if (fields) {
494
+ builder.select(fields)
495
+ }
496
+ return builder
497
+ }
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Insert into table (shorthand)
503
+ */
504
+ insert(tableName: string) {
505
+ return new InsertBuilder(this.db, tableName)
506
+ }
507
+
508
+ /**
509
+ * Update table (shorthand)
510
+ */
511
+ update(tableName: string) {
512
+ return new UpdateBuilder(this.db, tableName)
513
+ }
514
+
515
+ /**
516
+ * Delete from table (shorthand)
517
+ */
518
+ delete(tableName: string) {
519
+ return new DeleteBuilder(this.db, tableName)
520
+ }
521
+
522
+ /**
523
+ * Raw query access
524
+ */
525
+ execute(sql: string, params?: any[]) {
526
+ return this.db.execute(sql, params)
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Create ORM instance from Connection
532
+ */
533
+ export function createORM(db: Connection): ORM {
534
+ return new ORM(db)
535
+ }
536
+
537
+ // Export types
538
+ export type { WhereCondition, OrderByDirection }
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ export interface ODBLiteConfig {
10
10
  export interface QueryResult<T = any> {
11
11
  rows: T[];
12
12
  rowsAffected: number;
13
+ lastInsertRowid?: number | bigint;
13
14
  executionTime: number;
14
15
  databaseName?: string;
15
16
  }
@@ -19,6 +20,7 @@ export interface ODBLiteResponse<T = any> {
19
20
  data?: T[];
20
21
  rows?: T[];
21
22
  rowsAffected?: number;
23
+ lastInsertRowid?: number | bigint;
22
24
  executionTime?: number;
23
25
  dbId?: string;
24
26
  databaseName?: string;