@dqcai/sqlite 1.0.0 → 2.0.2

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.
Files changed (51) hide show
  1. package/README.md +1491 -2328
  2. package/lib/adapters/base-adapter.d.ts.map +1 -1
  3. package/lib/adapters/index.d.ts +2 -0
  4. package/lib/adapters/index.d.ts.map +1 -0
  5. package/lib/core/base-service.d.ts +119 -0
  6. package/lib/core/base-service.d.ts.map +1 -0
  7. package/lib/core/database-factory.d.ts +98 -0
  8. package/lib/core/database-factory.d.ts.map +1 -0
  9. package/lib/core/database-manager.d.ts +208 -0
  10. package/lib/core/database-manager.d.ts.map +1 -0
  11. package/lib/core/index.d.ts +5 -0
  12. package/lib/core/index.d.ts.map +1 -0
  13. package/lib/core/universal-dao.d.ts +64 -0
  14. package/lib/core/universal-dao.d.ts.map +1 -0
  15. package/lib/index.d.ts +324 -14
  16. package/lib/index.d.ts.map +1 -1
  17. package/lib/index.js +1 -1
  18. package/lib/index.js.map +1 -1
  19. package/lib/index.mjs +45 -1
  20. package/lib/index.mjs.map +1 -1
  21. package/lib/index.umd.js +1 -1
  22. package/lib/index.umd.js.map +1 -1
  23. package/lib/query/query-builder.d.ts +120 -0
  24. package/lib/query/query-builder.d.ts.map +1 -0
  25. package/lib/types.d.ts +142 -4
  26. package/lib/types.d.ts.map +1 -1
  27. package/lib/utils/csv-import.d.ts +102 -0
  28. package/lib/utils/csv-import.d.ts.map +1 -0
  29. package/lib/utils/index.d.ts +3 -0
  30. package/lib/utils/index.d.ts.map +1 -0
  31. package/lib/utils/migration-manager.d.ts +184 -0
  32. package/lib/utils/migration-manager.d.ts.map +1 -0
  33. package/package.json +82 -63
  34. package/README-all-source.md +0 -1248
  35. package/README-ps-gemini.md +0 -1180
  36. package/lib/adapters/browser-adapter.d.ts +0 -17
  37. package/lib/adapters/browser-adapter.d.ts.map +0 -1
  38. package/lib/adapters/bun-adapter.d.ts +0 -7
  39. package/lib/adapters/bun-adapter.d.ts.map +0 -1
  40. package/lib/adapters/deno-adapter.d.ts +0 -7
  41. package/lib/adapters/deno-adapter.d.ts.map +0 -1
  42. package/lib/adapters/node-adapter.d.ts +0 -7
  43. package/lib/adapters/node-adapter.d.ts.map +0 -1
  44. package/lib/adapters/react-native-adapter.d.ts +0 -20
  45. package/lib/adapters/react-native-adapter.d.ts.map +0 -1
  46. package/lib/query-builder.d.ts +0 -19
  47. package/lib/query-builder.d.ts.map +0 -1
  48. package/lib/sqlite-manager.d.ts +0 -11
  49. package/lib/sqlite-manager.d.ts.map +0 -1
  50. package/scripts/obfuscate.mjs +0 -155
  51. package/scripts/version-manager.js +0 -317
@@ -1,1248 +0,0 @@
1
- # Source code all for @dqcai/sqlite
2
- ```ts
3
- // src/types.ts
4
- export interface SQLiteRow {
5
- [key: string]: any;
6
- }
7
-
8
- export interface SQLiteResult {
9
- rows: SQLiteRow[];
10
- rowsAffected: number;
11
- lastInsertRowId?: number;
12
- }
13
-
14
- export interface SQLiteConnection {
15
- execute(sql: string, params?: any[]): Promise<SQLiteResult>;
16
- close(): Promise<void>;
17
- }
18
-
19
- export interface SQLiteAdapter {
20
- connect(path: string): Promise<SQLiteConnection>;
21
- isSupported(): boolean;
22
- }
23
-
24
- export interface SQLiteConfig {
25
- path: string;
26
- timeout?: number;
27
- busyTimeout?: number;
28
- }
29
-
30
- // Global type declarations for different environments
31
- declare global {
32
- // Browser environment
33
- interface Window {
34
- SQL?: any;
35
- initSqlJs?: (config?: any) => Promise<any>;
36
- openDatabase?: (name: string, version: string, displayName: string, estimatedSize: number) => any;
37
- }
38
-
39
- // Deno environment
40
- var Deno: {
41
- env: any;
42
- version?: { deno: string };
43
- [key: string]: any;
44
- } | undefined;
45
-
46
- // Bun environment
47
- var Bun: {
48
- version: string;
49
- [key: string]: any;
50
- } | undefined;
51
-
52
- // React Native Windows
53
- var Windows: any;
54
-
55
- // React Native Platform
56
- var Platform: {
57
- OS: string;
58
- Version?: string;
59
- } | undefined;
60
-
61
- // Navigator (React Native detection)
62
- var navigator: {
63
- product?: string;
64
- } | undefined;
65
- }
66
-
67
- // src/adapters/base-adapter.ts
68
- export abstract class BaseAdapter implements SQLiteAdapter {
69
- abstract connect(path: string): Promise<SQLiteConnection>;
70
- abstract isSupported(): boolean;
71
-
72
- protected sanitizeSQL(sql: string): string {
73
- // Basic SQL injection prevention
74
- return sql.trim();
75
- }
76
-
77
- protected bindParameters(sql: string, params?: any[]): string {
78
- if (!params || params.length === 0) {
79
- return sql;
80
- }
81
-
82
- let paramIndex = 0;
83
- return sql.replace(/\?/g, () => {
84
- if (paramIndex < params.length) {
85
- const param = params[paramIndex++];
86
- if (typeof param === 'string') {
87
- return `'${param.replace(/'/g, "''")}'`;
88
- }
89
- if (param === null || param === undefined) {
90
- return 'NULL';
91
- }
92
- return String(param);
93
- }
94
- return '?';
95
- });
96
- }
97
- }
98
-
99
- // src/adapters/node-adapter.ts
100
- import { BaseAdapter } from './base-adapter';
101
- import { SQLiteConnection, SQLiteResult, SQLiteRow } from '../types';
102
-
103
- class NodeSQLiteConnection implements SQLiteConnection {
104
- private db: any;
105
-
106
- constructor(db: any) {
107
- this.db = db;
108
- }
109
-
110
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
111
- return new Promise((resolve, reject) => {
112
- const sanitizedSQL = this.bindParameters(sql, params);
113
-
114
- if (sql.toLowerCase().trim().startsWith('select')) {
115
- this.db.all(sanitizedSQL, (err: any, rows: SQLiteRow[]) => {
116
- if (err) {
117
- reject(new Error(`SQLite error: ${err.message}`));
118
- } else {
119
- resolve({
120
- rows: rows || [],
121
- rowsAffected: 0
122
- });
123
- }
124
- });
125
- } else {
126
- this.db.run(sanitizedSQL, function(this: any, err: any) {
127
- if (err) {
128
- reject(new Error(`SQLite error: ${err.message}`));
129
- } else {
130
- resolve({
131
- rows: [],
132
- rowsAffected: this.changes || 0,
133
- lastInsertRowId: this.lastID
134
- });
135
- }
136
- });
137
- }
138
- });
139
- }
140
-
141
- private bindParameters(sql: string, params?: any[]): string {
142
- if (!params || params.length === 0) {
143
- return sql;
144
- }
145
-
146
- let paramIndex = 0;
147
- return sql.replace(/\?/g, () => {
148
- if (paramIndex < params.length) {
149
- const param = params[paramIndex++];
150
- if (typeof param === 'string') {
151
- return `'${param.replace(/'/g, "''")}'`;
152
- }
153
- if (param === null || param === undefined) {
154
- return 'NULL';
155
- }
156
- return String(param);
157
- }
158
- return '?';
159
- });
160
- }
161
-
162
- async close(): Promise<void> {
163
- return new Promise((resolve, reject) => {
164
- this.db.close((err: any) => {
165
- if (err) {
166
- reject(new Error(`Error closing database: ${err.message}`));
167
- } else {
168
- resolve();
169
- }
170
- });
171
- });
172
- }
173
- }
174
-
175
- export class NodeAdapter extends BaseAdapter {
176
- isSupported(): boolean {
177
- try {
178
- return typeof require !== 'undefined' && require('sqlite3') !== undefined;
179
- } catch {
180
- return false;
181
- }
182
- }
183
-
184
- async connect(path: string): Promise<SQLiteConnection> {
185
- return new Promise((resolve, reject) => {
186
- try {
187
- const sqlite3 = require('sqlite3').verbose();
188
- const db = new sqlite3.Database(path, (err: any) => {
189
- if (err) {
190
- reject(new Error(`Cannot connect to database: ${err.message}`));
191
- } else {
192
- resolve(new NodeSQLiteConnection(db));
193
- }
194
- });
195
- } catch (error) {
196
- reject(new Error(`SQLite3 module not available: ${error}`));
197
- }
198
- });
199
- }
200
- }
201
-
202
- // src/adapters/browser-adapter.ts
203
- import { BaseAdapter } from './base-adapter';
204
- import { SQLiteConnection, SQLiteResult, SQLiteRow } from '../types';
205
-
206
- class BrowserSQLiteConnection implements SQLiteConnection {
207
- private worker: Worker | null = null;
208
- private db: any = null;
209
- private adapter: BrowserAdapter;
210
-
211
- constructor(db: any, adapter: BrowserAdapter, worker?: Worker) {
212
- this.db = db;
213
- this.adapter = adapter;
214
- this.worker = worker || null;
215
- }
216
-
217
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
218
- if (!this.db) {
219
- throw new Error('Database connection not available');
220
- }
221
-
222
- try {
223
- const boundSQL = this.adapter.bindParameters(sql, params);
224
-
225
- if (sql.toLowerCase().trim().startsWith('select')) {
226
- const result = this.db.exec(boundSQL);
227
- const rows: SQLiteRow[] = [];
228
-
229
- if (result.length > 0 && result[0].values) {
230
- const columns = result[0].columns;
231
- const values = result[0].values;
232
-
233
- for (const row of values) {
234
- const rowObj: SQLiteRow = {};
235
- columns.forEach((col: string, index: number) => {
236
- rowObj[col] = row[index];
237
- });
238
- rows.push(rowObj);
239
- }
240
- }
241
-
242
- return {
243
- rows,
244
- rowsAffected: 0
245
- };
246
- } else {
247
- this.db.exec(boundSQL);
248
- return {
249
- rows: [],
250
- rowsAffected: 1 // Browser doesn't provide exact count
251
- };
252
- }
253
- } catch (error) {
254
- throw new Error(`SQLite error: ${error}`);
255
- }
256
- }
257
-
258
- async close(): Promise<void> {
259
- if (this.db) {
260
- this.db.close();
261
- this.db = null;
262
- }
263
- if (this.worker) {
264
- this.worker.terminate();
265
- this.worker = null;
266
- }
267
- }
268
- }
269
-
270
- export class BrowserAdapter extends BaseAdapter {
271
- private sqlJs: any = null;
272
-
273
- isSupported(): boolean {
274
- return typeof window !== 'undefined' &&
275
- (typeof window.SQL !== 'undefined' || this.sqlJs !== null);
276
- }
277
-
278
- // Make bindParameters public so BrowserSQLiteConnection can access it
279
- public bindParameters(sql: string, params?: any[]): string {
280
- return super.bindParameters(sql, params);
281
- }
282
-
283
- async connect(path: string): Promise<SQLiteConnection> {
284
- try {
285
- // Try to load sql.js if not already loaded
286
- if (!this.sqlJs) {
287
- if (window.SQL) {
288
- this.sqlJs = window.SQL;
289
- } else {
290
- // Try to load from CDN
291
- await this.loadSqlJs();
292
- }
293
- }
294
-
295
- let db;
296
-
297
- if (path === ':memory:') {
298
- // In-memory database
299
- db = new this.sqlJs.Database();
300
- } else {
301
- // Try to load from file
302
- const data = await this.loadDatabaseFile(path);
303
- db = new this.sqlJs.Database(data);
304
- }
305
-
306
- return new BrowserSQLiteConnection(db, this);
307
- } catch (error) {
308
- throw new Error(`Cannot connect to browser database: ${error}`);
309
- }
310
- }
311
-
312
- private async loadSqlJs(): Promise<void> {
313
- return new Promise((resolve, reject) => {
314
- const script = document.createElement('script');
315
- script.src = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js';
316
- script.onload = async () => {
317
- try {
318
- if (window.initSqlJs) {
319
- this.sqlJs = await window.initSqlJs({
320
- locateFile: (file: string) => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
321
- });
322
- }
323
- resolve();
324
- } catch (err) {
325
- reject(err);
326
- }
327
- };
328
- script.onerror = () => reject(new Error('Failed to load sql.js'));
329
- document.head.appendChild(script);
330
- });
331
- }
332
-
333
- private async loadDatabaseFile(path: string): Promise<Uint8Array | undefined> {
334
- // Try to fetch as a file
335
- try {
336
- const response = await fetch(path);
337
- if (response.ok) {
338
- const arrayBuffer = await response.arrayBuffer();
339
- return new Uint8Array(arrayBuffer);
340
- }
341
- } catch {
342
- // File doesn't exist, return undefined for new database
343
- }
344
-
345
- return undefined;
346
- }
347
- }
348
-
349
- // src/adapters/deno-adapter.ts
350
- import { BaseAdapter } from './base-adapter';
351
- import { SQLiteConnection, SQLiteResult, SQLiteRow } from '../types';
352
-
353
- class DenoSQLiteConnection implements SQLiteConnection {
354
- private db: any;
355
-
356
- constructor(db: any) {
357
- this.db = db;
358
- }
359
-
360
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
361
- try {
362
- const boundSQL = this.bindParameters(sql, params);
363
-
364
- if (sql.toLowerCase().trim().startsWith('select')) {
365
- const result = this.db.queryEntries(boundSQL);
366
- return {
367
- rows: result,
368
- rowsAffected: 0
369
- };
370
- } else {
371
- const result = this.db.query(boundSQL);
372
- return {
373
- rows: [],
374
- rowsAffected: result.length || 1
375
- };
376
- }
377
- } catch (error) {
378
- throw new Error(`SQLite error: ${error}`);
379
- }
380
- }
381
-
382
- private bindParameters(sql: string, params?: any[]): string {
383
- if (!params || params.length === 0) {
384
- return sql;
385
- }
386
-
387
- let paramIndex = 0;
388
- return sql.replace(/\?/g, () => {
389
- if (paramIndex < params.length) {
390
- const param = params[paramIndex++];
391
- if (typeof param === 'string') {
392
- return `'${param.replace(/'/g, "''")}'`;
393
- }
394
- if (param === null || param === undefined) {
395
- return 'NULL';
396
- }
397
- return String(param);
398
- }
399
- return '?';
400
- });
401
- }
402
-
403
- async close(): Promise<void> {
404
- if (this.db) {
405
- this.db.close();
406
- }
407
- }
408
- }
409
-
410
- export class DenoAdapter extends BaseAdapter {
411
- isSupported(): boolean {
412
- try {
413
- return typeof globalThis.Deno !== 'undefined' &&
414
- globalThis.Deno.env !== undefined;
415
- } catch {
416
- return false;
417
- }
418
- }
419
-
420
- async connect(path: string): Promise<SQLiteConnection> {
421
- try {
422
- // @ts-ignore - Bỏ qua type checking cho dòng này
423
- const { DB } = await import('https://deno.land/x/sqlite@v3.8.0/mod.ts');
424
- const db = new DB(path);
425
- return new DenoSQLiteConnection(db);
426
- } catch (error) {
427
- throw new Error(`Cannot connect to Deno database: ${error}`);
428
- }
429
- }
430
- }
431
-
432
- // src/adapters/bun-adapter.ts
433
- import { BaseAdapter } from './base-adapter';
434
- import { SQLiteConnection, SQLiteResult, SQLiteRow } from '../types';
435
-
436
- class BunSQLiteConnection implements SQLiteConnection {
437
- private db: any;
438
-
439
- constructor(db: any) {
440
- this.db = db;
441
- }
442
-
443
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
444
- try {
445
- if (sql.toLowerCase().trim().startsWith('select')) {
446
- const result = this.db.query(sql).all(params || []);
447
- return {
448
- rows: result,
449
- rowsAffected: 0
450
- };
451
- } else {
452
- const result = this.db.query(sql).run(params || []);
453
- return {
454
- rows: [],
455
- rowsAffected: result.changes || 0,
456
- lastInsertRowId: result.lastInsertRowid
457
- };
458
- }
459
- } catch (error) {
460
- throw new Error(`SQLite error: ${error}`);
461
- }
462
- }
463
-
464
- async close(): Promise<void> {
465
- if (this.db) {
466
- this.db.close();
467
- }
468
- }
469
- }
470
-
471
- export class BunAdapter extends BaseAdapter {
472
- isSupported(): boolean {
473
- try {
474
- return typeof globalThis.Bun !== 'undefined' &&
475
- globalThis.Bun.version !== undefined;
476
- } catch {
477
- return false;
478
- }
479
- }
480
-
481
- async connect(path: string): Promise<SQLiteConnection> {
482
- try {
483
- const { Database } = require('bun:sqlite');
484
- const db = new Database(path);
485
- return new BunSQLiteConnection(db);
486
- } catch (error) {
487
- throw new Error(`Cannot connect to Bun database: ${error}`);
488
- }
489
- }
490
- }
491
-
492
- // src/adapters/react-native-adapter.ts
493
- import { BaseAdapter } from './base-adapter';
494
- import { SQLiteConnection, SQLiteResult, SQLiteRow } from '../types';
495
-
496
- class ReactNativeSQLiteConnection implements SQLiteConnection {
497
- private db: any;
498
- private dbName: string;
499
-
500
- constructor(db: any, dbName: string) {
501
- this.db = db;
502
- this.dbName = dbName;
503
- }
504
-
505
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
506
- return new Promise((resolve, reject) => {
507
- try {
508
- const boundSQL = this.bindParameters(sql, params);
509
-
510
- this.db.transaction((tx: any) => {
511
- tx.executeSql(
512
- boundSQL,
513
- [],
514
- (tx: any, results: any) => {
515
- const rows: SQLiteRow[] = [];
516
-
517
- if (results.rows) {
518
- for (let i = 0; i < results.rows.length; i++) {
519
- rows.push(results.rows.item(i));
520
- }
521
- }
522
-
523
- resolve({
524
- rows,
525
- rowsAffected: results.rowsAffected || 0,
526
- lastInsertRowId: results.insertId
527
- });
528
- },
529
- (tx: any, error: any) => {
530
- reject(new Error(`SQLite error: ${error.message}`));
531
- return false;
532
- }
533
- );
534
- });
535
- } catch (error) {
536
- reject(new Error(`SQLite execution error: ${error}`));
537
- }
538
- });
539
- }
540
-
541
- private bindParameters(sql: string, params?: any[]): string {
542
- if (!params || params.length === 0) {
543
- return sql;
544
- }
545
-
546
- let paramIndex = 0;
547
- return sql.replace(/\?/g, () => {
548
- if (paramIndex < params.length) {
549
- const param = params[paramIndex++];
550
- if (typeof param === 'string') {
551
- return `'${param.replace(/'/g, "''")}'`;
552
- }
553
- if (param === null || param === undefined) {
554
- return 'NULL';
555
- }
556
- return String(param);
557
- }
558
- return '?';
559
- });
560
- }
561
-
562
- async close(): Promise<void> {
563
- return new Promise((resolve, reject) => {
564
- if (this.db && this.db.close) {
565
- this.db.close(
566
- () => resolve(),
567
- (error: any) => reject(new Error(`Error closing database: ${error.message}`))
568
- );
569
- } else {
570
- resolve();
571
- }
572
- });
573
- }
574
- }
575
-
576
- // Adapter for react-native-sqlite-storage
577
- class ReactNativeSQLiteStorageConnection implements SQLiteConnection {
578
- private db: any;
579
-
580
- constructor(db: any) {
581
- this.db = db;
582
- }
583
-
584
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
585
- return new Promise((resolve, reject) => {
586
- this.db.executeSql(
587
- sql,
588
- params || [],
589
- (results: any) => {
590
- const rows: SQLiteRow[] = [];
591
-
592
- if (results.rows) {
593
- for (let i = 0; i < results.rows.length; i++) {
594
- rows.push(results.rows.item(i));
595
- }
596
- }
597
-
598
- resolve({
599
- rows,
600
- rowsAffected: results.rowsAffected || 0,
601
- lastInsertRowId: results.insertId
602
- });
603
- },
604
- (error: any) => {
605
- reject(new Error(`SQLite error: ${error.message}`));
606
- }
607
- );
608
- });
609
- }
610
-
611
- async close(): Promise<void> {
612
- return new Promise((resolve, reject) => {
613
- if (this.db && this.db.close) {
614
- this.db.close(resolve, reject);
615
- } else {
616
- resolve();
617
- }
618
- });
619
- }
620
- }
621
-
622
- // Adapter for expo-sqlite
623
- class ExpoSQLiteConnection implements SQLiteConnection {
624
- private db: any;
625
-
626
- constructor(db: any) {
627
- this.db = db;
628
- }
629
-
630
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
631
- try {
632
- const result = await this.db.execAsync([{
633
- sql,
634
- args: params || []
635
- }]);
636
-
637
- if (result && result[0]) {
638
- const firstResult = result[0];
639
- return {
640
- rows: firstResult.rows || [],
641
- rowsAffected: firstResult.rowsAffected || 0,
642
- lastInsertRowId: firstResult.lastInsertRowId
643
- };
644
- }
645
-
646
- return {
647
- rows: [],
648
- rowsAffected: 0
649
- };
650
- } catch (error) {
651
- throw new Error(`SQLite error: ${error}`);
652
- }
653
- }
654
-
655
- async close(): Promise<void> {
656
- if (this.db && this.db.closeAsync) {
657
- await this.db.closeAsync();
658
- }
659
- }
660
- }
661
-
662
- // Adapter for React Native Windows SQLite
663
- class ReactNativeWindowsSQLiteConnection implements SQLiteConnection {
664
- private db: any;
665
-
666
- constructor(db: any) {
667
- this.db = db;
668
- }
669
-
670
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
671
- try {
672
- if (sql.toLowerCase().trim().startsWith('select')) {
673
- const result = await this.db.all(sql, params || []);
674
- return {
675
- rows: result || [],
676
- rowsAffected: 0
677
- };
678
- } else {
679
- const result = await this.db.run(sql, params || []);
680
- return {
681
- rows: [],
682
- rowsAffected: result.changes || 0,
683
- lastInsertRowId: result.lastID
684
- };
685
- }
686
- } catch (error) {
687
- throw new Error(`SQLite error: ${error}`);
688
- }
689
- }
690
-
691
- async close(): Promise<void> {
692
- if (this.db && this.db.close) {
693
- await this.db.close();
694
- }
695
- }
696
- }
697
-
698
- // Adapter for react-native-sqlite-2 (Windows specific)
699
- class ReactNativeSQLite2Connection implements SQLiteConnection {
700
- private db: any;
701
-
702
- constructor(db: any) {
703
- this.db = db;
704
- }
705
-
706
- async execute(sql: string, params?: any[]): Promise<SQLiteResult> {
707
- return new Promise((resolve, reject) => {
708
- this.db.exec(
709
- [{ sql, args: params || [] }],
710
- false,
711
- (results: any) => {
712
- if (results && results[0]) {
713
- const result = results[0];
714
- if (result.error) {
715
- reject(new Error(`SQLite error: ${result.error.message}`));
716
- } else {
717
- resolve({
718
- rows: result.rows || [],
719
- rowsAffected: result.rowsAffected || 0,
720
- lastInsertRowId: result.insertId
721
- });
722
- }
723
- } else {
724
- resolve({
725
- rows: [],
726
- rowsAffected: 0
727
- });
728
- }
729
- }
730
- );
731
- });
732
- }
733
-
734
- async close(): Promise<void> {
735
- return new Promise((resolve, reject) => {
736
- if (this.db && this.db.close) {
737
- this.db.close(resolve, reject);
738
- } else {
739
- resolve();
740
- }
741
- });
742
- }
743
- }
744
-
745
- export class ReactNativeAdapter extends BaseAdapter {
746
- private adapterType: 'webview' | 'storage' | 'expo' | 'windows' | 'sqlite2' | null = null;
747
- private isWindows: boolean = false;
748
-
749
- isSupported(): boolean {
750
- try {
751
- // Check for React Native environment
752
- const isRN = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
753
- if (!isRN) return false;
754
-
755
- // Detect Windows platform
756
- this.isWindows = this.isReactNativeWindows();
757
-
758
- // Check for available SQLite libraries
759
- if (this.isWindows) {
760
- if (this.hasSQLite2()) {
761
- this.adapterType = 'sqlite2';
762
- return true;
763
- }
764
-
765
- if (this.hasWindowsSQLite()) {
766
- this.adapterType = 'windows';
767
- return true;
768
- }
769
- }
770
-
771
- if (this.hasExpoSQLite()) {
772
- this.adapterType = 'expo';
773
- return true;
774
- }
775
-
776
- if (this.hasSQLiteStorage()) {
777
- this.adapterType = 'storage';
778
- return true;
779
- }
780
-
781
- if (this.hasWebViewSQLite()) {
782
- this.adapterType = 'webview';
783
- return true;
784
- }
785
-
786
- return false;
787
- } catch {
788
- return false;
789
- }
790
- }
791
-
792
- private isReactNativeWindows(): boolean {
793
- try {
794
- // Check for Windows-specific APIs or platform detection
795
- return (
796
- typeof navigator !== 'undefined' &&
797
- navigator.product === 'ReactNative' &&
798
- (
799
- // Windows global object check
800
- typeof globalThis.Windows !== 'undefined' ||
801
- // React Native Windows module check
802
- (() => {
803
- try {
804
- if (typeof require !== 'undefined') {
805
- require('react-native-windows');
806
- return true;
807
- }
808
- } catch {
809
- return false;
810
- }
811
- return false;
812
- })() ||
813
- // Platform module check
814
- (() => {
815
- try {
816
- if (typeof require !== 'undefined') {
817
- const { Platform } = require('react-native');
818
- return Platform && Platform.OS === 'windows';
819
- }
820
- } catch {
821
- return false;
822
- }
823
- return false;
824
- })()
825
- )
826
- );
827
- } catch {
828
- return false;
829
- }
830
- }
831
-
832
- private hasExpoSQLite(): boolean {
833
- try {
834
- require('expo-sqlite');
835
- return !this.isWindows; // Expo SQLite might not work on Windows
836
- } catch {
837
- return false;
838
- }
839
- }
840
-
841
- private hasSQLiteStorage(): boolean {
842
- try {
843
- require('react-native-sqlite-storage');
844
- return !this.isWindows; // Standard version might not work on Windows
845
- } catch {
846
- return false;
847
- }
848
- }
849
-
850
- private hasWebViewSQLite(): boolean {
851
- try {
852
- return typeof window !== 'undefined' &&
853
- typeof window.openDatabase === 'function' &&
854
- !this.isWindows; // WebView SQLite not available on Windows
855
- } catch {
856
- return false;
857
- }
858
- }
859
-
860
- private hasWindowsSQLite(): boolean {
861
- try {
862
- require('react-native-windows-sqlite');
863
- return true;
864
- } catch {
865
- return false;
866
- }
867
- }
868
-
869
- private hasSQLite2(): boolean {
870
- try {
871
- require('react-native-sqlite-2');
872
- return true;
873
- } catch {
874
- return false;
875
- }
876
- }
877
-
878
- async connect(path: string): Promise<SQLiteConnection> {
879
- if (!this.isSupported()) {
880
- throw new Error('React Native SQLite not supported in this environment');
881
- }
882
-
883
- switch (this.adapterType) {
884
- case 'sqlite2':
885
- return this.connectSQLite2(path);
886
-
887
- case 'windows':
888
- return this.connectWindows(path);
889
-
890
- case 'expo':
891
- return this.connectExpo(path);
892
-
893
- case 'storage':
894
- return this.connectStorage(path);
895
-
896
- case 'webview':
897
- return this.connectWebView(path);
898
-
899
- default:
900
- throw new Error('No React Native SQLite adapter available');
901
- }
902
- }
903
-
904
- private async connectSQLite2(path: string): Promise<SQLiteConnection> {
905
- return new Promise((resolve, reject) => {
906
- try {
907
- const SQLite = require('react-native-sqlite-2');
908
-
909
- SQLite.openDatabase(
910
- path,
911
- '1.0',
912
- 'Database',
913
- 200000,
914
- (db: any) => {
915
- resolve(new ReactNativeSQLite2Connection(db));
916
- },
917
- (error: any) => {
918
- reject(new Error(`Cannot connect to React Native SQLite-2: ${error.message}`));
919
- }
920
- );
921
- } catch (error) {
922
- reject(new Error(`React Native SQLite-2 not available: ${error}`));
923
- }
924
- });
925
- }
926
-
927
- private async connectWindows(path: string): Promise<SQLiteConnection> {
928
- try {
929
- const SQLite = require('react-native-windows-sqlite');
930
- const db = await SQLite.openDatabase({
931
- name: path,
932
- location: 'default'
933
- });
934
- return new ReactNativeWindowsSQLiteConnection(db);
935
- } catch (error) {
936
- throw new Error(`Cannot connect to React Native Windows SQLite: ${error}`);
937
- }
938
- }
939
-
940
- private async connectExpo(path: string): Promise<SQLiteConnection> {
941
- try {
942
- const SQLite = require('expo-sqlite');
943
- const db = SQLite.openDatabaseSync(path);
944
- return new ExpoSQLiteConnection(db);
945
- } catch (error) {
946
- throw new Error(`Cannot connect to Expo SQLite database: ${error}`);
947
- }
948
- }
949
-
950
- private async connectStorage(path: string): Promise<SQLiteConnection> {
951
- return new Promise((resolve, reject) => {
952
- try {
953
- const SQLite = require('react-native-sqlite-storage');
954
-
955
- // Enable debugging (optional)
956
- SQLite.DEBUG(false);
957
- SQLite.enablePromise(true);
958
-
959
- SQLite.openDatabase({
960
- name: path,
961
- location: 'default'
962
- }).then((db: any) => {
963
- resolve(new ReactNativeSQLiteStorageConnection(db));
964
- }).catch((error: any) => {
965
- reject(new Error(`Cannot connect to React Native SQLite database: ${error.message}`));
966
- });
967
- } catch (error) {
968
- reject(new Error(`React Native SQLite Storage not available: ${error}`));
969
- }
970
- });
971
- }
972
-
973
- private async connectWebView(path: string): Promise<SQLiteConnection> {
974
- try {
975
- if (!window.openDatabase) {
976
- throw new Error('WebView openDatabase not available');
977
- }
978
- const db = window.openDatabase(path, '1.0', 'Database', 2 * 1024 * 1024);
979
- return new ReactNativeSQLiteConnection(db, path);
980
- } catch (error) {
981
- throw new Error(`Cannot connect to WebView SQLite database: ${error}`);
982
- }
983
- }
984
- }
985
-
986
- // src/sqlite-manager.ts
987
- import { SQLiteAdapter, SQLiteConnection, SQLiteConfig } from './types';
988
- import { NodeAdapter } from './adapters/node-adapter';
989
- import { BrowserAdapter } from './adapters/browser-adapter';
990
- import { DenoAdapter } from './adapters/deno-adapter';
991
- import { BunAdapter } from './adapters/bun-adapter';
992
- import { ReactNativeAdapter } from './adapters/react-native-adapter';
993
-
994
- // Type declarations for React Native globals
995
- declare global {
996
- var Windows: any;
997
- var Platform: {
998
- OS: string;
999
- Version?: string;
1000
- };
1001
- var navigator: {
1002
- product?: string;
1003
- };
1004
- }
1005
-
1006
- export class SQLiteManager {
1007
- private adapters: SQLiteAdapter[] = [];
1008
- private currentAdapter: SQLiteAdapter | null = null;
1009
-
1010
- constructor() {
1011
- this.adapters = [
1012
- new ReactNativeAdapter(), // Check React Native first
1013
- new BunAdapter(),
1014
- new DenoAdapter(),
1015
- new NodeAdapter(),
1016
- new BrowserAdapter()
1017
- ];
1018
- }
1019
-
1020
- private detectEnvironment(): SQLiteAdapter {
1021
- for (const adapter of this.adapters) {
1022
- if (adapter.isSupported()) {
1023
- return adapter;
1024
- }
1025
- }
1026
- throw new Error('No supported SQLite adapter found for this environment');
1027
- }
1028
-
1029
- async connect(config: string | SQLiteConfig): Promise<SQLiteConnection> {
1030
- const path = typeof config === 'string' ? config : config.path;
1031
-
1032
- if (!this.currentAdapter) {
1033
- this.currentAdapter = this.detectEnvironment();
1034
- }
1035
-
1036
- try {
1037
- return await this.currentAdapter.connect(path);
1038
- } catch (error) {
1039
- throw new Error(`Failed to connect to SQLite database: ${error}`);
1040
- }
1041
- }
1042
-
1043
- getEnvironmentInfo(): string {
1044
- try {
1045
- // Safe check for React Native
1046
- if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
1047
- // Safe check for Windows environment
1048
- if (typeof globalThis.Windows !== 'undefined') {
1049
- return 'React Native Windows';
1050
- }
1051
-
1052
- // Safe check for Platform API
1053
- try {
1054
- // Try to import Platform from react-native if available
1055
- const Platform = this.getPlatform();
1056
- if (Platform && Platform.OS === 'windows') {
1057
- return 'React Native Windows';
1058
- }
1059
- } catch {
1060
- // Platform not available, continue
1061
- }
1062
-
1063
- return 'React Native';
1064
- }
1065
-
1066
- if (typeof globalThis.Bun !== 'undefined') return 'Bun';
1067
- if (typeof globalThis.Deno !== 'undefined') return 'Deno';
1068
- if (typeof window !== 'undefined') return 'Browser';
1069
- if (typeof process !== 'undefined') return 'Node.js';
1070
- return 'Unknown';
1071
- } catch {
1072
- return 'Unknown';
1073
- }
1074
- }
1075
-
1076
- private getPlatform(): any {
1077
- try {
1078
- // Try to require Platform from react-native
1079
- if (typeof require !== 'undefined') {
1080
- const { Platform } = require('react-native');
1081
- return Platform;
1082
- }
1083
- } catch {
1084
- // react-native not available
1085
- }
1086
-
1087
- // Try global Platform
1088
- try {
1089
- return globalThis.Platform;
1090
- } catch {
1091
- return null;
1092
- }
1093
- }
1094
- }
1095
- }
1096
-
1097
- // src/query-builder.ts
1098
- export class QueryBuilder {
1099
- private tableName = '';
1100
- private selectFields: string[] = ['*'];
1101
- private whereConditions: string[] = [];
1102
- private orderByFields: string[] = [];
1103
- private limitValue: number | null = null;
1104
- private offsetValue: number | null = null;
1105
-
1106
- static table(name: string): QueryBuilder {
1107
- const builder = new QueryBuilder();
1108
- builder.tableName = name;
1109
- return builder;
1110
- }
1111
-
1112
- select(fields: string | string[]): QueryBuilder {
1113
- this.selectFields = Array.isArray(fields) ? fields : [fields];
1114
- return this;
1115
- }
1116
-
1117
- where(condition: string): QueryBuilder {
1118
- this.whereConditions.push(condition);
1119
- return this;
1120
- }
1121
-
1122
- orderBy(field: string, direction: 'ASC' | 'DESC' = 'ASC'): QueryBuilder {
1123
- this.orderByFields.push(`${field} ${direction}`);
1124
- return this;
1125
- }
1126
-
1127
- limit(count: number): QueryBuilder {
1128
- this.limitValue = count;
1129
- return this;
1130
- }
1131
-
1132
- offset(count: number): QueryBuilder {
1133
- this.offsetValue = count;
1134
- return this;
1135
- }
1136
-
1137
- toSQL(): string {
1138
- let sql = `SELECT ${this.selectFields.join(', ')} FROM ${this.tableName}`;
1139
-
1140
- if (this.whereConditions.length > 0) {
1141
- sql += ` WHERE ${this.whereConditions.join(' AND ')}`;
1142
- }
1143
-
1144
- if (this.orderByFields.length > 0) {
1145
- sql += ` ORDER BY ${this.orderByFields.join(', ')}`;
1146
- }
1147
-
1148
- if (this.limitValue !== null) {
1149
- sql += ` LIMIT ${this.limitValue}`;
1150
- }
1151
-
1152
- if (this.offsetValue !== null) {
1153
- sql += ` OFFSET ${this.offsetValue}`;
1154
- }
1155
-
1156
- return sql;
1157
- }
1158
-
1159
- // Insert methods
1160
- static insert(tableName: string, data: Record<string, any>): string {
1161
- const fields = Object.keys(data);
1162
- const values = Object.values(data);
1163
- const placeholders = values.map(() => '?').join(', ');
1164
-
1165
- return `INSERT INTO ${tableName} (${fields.join(', ')}) VALUES (${placeholders})`;
1166
- }
1167
-
1168
- static update(tableName: string, data: Record<string, any>, where: string): string {
1169
- const sets = Object.keys(data).map(key => `${key} = ?`).join(', ');
1170
- return `UPDATE ${tableName} SET ${sets} WHERE ${where}`;
1171
- }
1172
-
1173
- static delete(tableName: string, where: string): string {
1174
- return `DELETE FROM ${tableName} WHERE ${where}`;
1175
- }
1176
- }
1177
-
1178
- // src/index.ts
1179
- export { SQLiteManager } from './sqlite-manager';
1180
- export { QueryBuilder } from './query-builder';
1181
- export * from './types';
1182
-
1183
- // Example usage and main export
1184
- export default class UniversalSqlite {
1185
- private manager: SQLiteManager;
1186
- private connection: SQLiteConnection | null = null;
1187
-
1188
- constructor() {
1189
- this.manager = new SQLiteManager();
1190
- }
1191
-
1192
- async connect(path: string): Promise<void> {
1193
- this.connection = await this.manager.connect(path);
1194
- }
1195
-
1196
- async query(sql: string, params?: any[]) {
1197
- if (!this.connection) {
1198
- throw new Error('Database not connected');
1199
- }
1200
- return await this.connection.execute(sql, params);
1201
- }
1202
-
1203
- async close(): Promise<void> {
1204
- if (this.connection) {
1205
- await this.connection.close();
1206
- this.connection = null;
1207
- }
1208
- }
1209
-
1210
- getEnvironment(): string {
1211
- return this.manager.getEnvironmentInfo();
1212
- }
1213
-
1214
- // Convenience methods
1215
- async createTable(name: string, schema: Record<string, string>): Promise<void> {
1216
- const fields = Object.entries(schema)
1217
- .map(([field, type]) => `${field} ${type}`)
1218
- .join(', ');
1219
-
1220
- await this.query(`CREATE TABLE IF NOT EXISTS ${name} (${fields})`);
1221
- }
1222
-
1223
- async insert(table: string, data: Record<string, any>) {
1224
- const sql = QueryBuilder.insert(table, data);
1225
- return await this.query(sql, Object.values(data));
1226
- }
1227
-
1228
- async select(table: string, where?: string, params?: any[]) {
1229
- let sql = `SELECT * FROM ${table}`;
1230
- if (where) {
1231
- sql += ` WHERE ${where}`;
1232
- }
1233
- return await this.query(sql, params);
1234
- }
1235
-
1236
- async update(table: string, data: Record<string, any>, where: string, whereParams?: any[]) {
1237
- const sql = QueryBuilder.update(table, data, where);
1238
- const params = [...Object.values(data), ...(whereParams || [])];
1239
- return await this.query(sql, params);
1240
- }
1241
-
1242
- async delete(table: string, where: string, params?: any[]) {
1243
- const sql = QueryBuilder.delete(table, where);
1244
- return await this.query(sql, params);
1245
- }
1246
- }
1247
-
1248
- ```