@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.
- package/README.md +1491 -2328
- package/lib/adapters/base-adapter.d.ts.map +1 -1
- package/lib/adapters/index.d.ts +2 -0
- package/lib/adapters/index.d.ts.map +1 -0
- package/lib/core/base-service.d.ts +119 -0
- package/lib/core/base-service.d.ts.map +1 -0
- package/lib/core/database-factory.d.ts +98 -0
- package/lib/core/database-factory.d.ts.map +1 -0
- package/lib/core/database-manager.d.ts +208 -0
- package/lib/core/database-manager.d.ts.map +1 -0
- package/lib/core/index.d.ts +5 -0
- package/lib/core/index.d.ts.map +1 -0
- package/lib/core/universal-dao.d.ts +64 -0
- package/lib/core/universal-dao.d.ts.map +1 -0
- package/lib/index.d.ts +324 -14
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +45 -1
- package/lib/index.mjs.map +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/index.umd.js.map +1 -1
- package/lib/query/query-builder.d.ts +120 -0
- package/lib/query/query-builder.d.ts.map +1 -0
- package/lib/types.d.ts +142 -4
- package/lib/types.d.ts.map +1 -1
- package/lib/utils/csv-import.d.ts +102 -0
- package/lib/utils/csv-import.d.ts.map +1 -0
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/migration-manager.d.ts +184 -0
- package/lib/utils/migration-manager.d.ts.map +1 -0
- package/package.json +82 -63
- package/README-all-source.md +0 -1248
- package/README-ps-gemini.md +0 -1180
- package/lib/adapters/browser-adapter.d.ts +0 -17
- package/lib/adapters/browser-adapter.d.ts.map +0 -1
- package/lib/adapters/bun-adapter.d.ts +0 -7
- package/lib/adapters/bun-adapter.d.ts.map +0 -1
- package/lib/adapters/deno-adapter.d.ts +0 -7
- package/lib/adapters/deno-adapter.d.ts.map +0 -1
- package/lib/adapters/node-adapter.d.ts +0 -7
- package/lib/adapters/node-adapter.d.ts.map +0 -1
- package/lib/adapters/react-native-adapter.d.ts +0 -20
- package/lib/adapters/react-native-adapter.d.ts.map +0 -1
- package/lib/query-builder.d.ts +0 -19
- package/lib/query-builder.d.ts.map +0 -1
- package/lib/sqlite-manager.d.ts +0 -11
- package/lib/sqlite-manager.d.ts.map +0 -1
- package/scripts/obfuscate.mjs +0 -155
- package/scripts/version-manager.js +0 -317
package/README-all-source.md
DELETED
|
@@ -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
|
-
```
|