@dqcai/sqlite 1.0.0

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 ADDED
@@ -0,0 +1,2653 @@
1
+ # @dqcai/sqlite - Universal SQLite Library
2
+
3
+ ![Universal SQLite](https://img.shields.io/badge/SQLite-Universal-blue)
4
+ ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
5
+ ![Cross Platform](https://img.shields.io/badge/Platform-Universal-green)
6
+
7
+ Một thư viện SQLite đa nền tảng hỗ trợ **tất cả** các môi trường JavaScript/TypeScript phổ biến với API thống nhất.
8
+
9
+ ## 🌟 Tính năng nổi bật
10
+
11
+ - ✅ **Universal**: Hoạt động trên Node.js, Browser, Deno, Bun, React Native
12
+ - ✅ **TypeScript**: Hỗ trợ đầy đủ TypeScript với type safety
13
+ - ✅ **Auto-detection**: Tự động phát hiện môi trường và chọn adapter phù hợp
14
+ - ✅ **Query Builder**: Công cụ xây dựng truy vấn mạnh mẽ
15
+ - ✅ **React Native Windows**: Hỗ trợ đặc biệt cho React Native Windows
16
+ - ✅ **Zero Configuration**: Không cần cấu hình phức tạp
17
+
18
+ ## 🚀 Cài đặt
19
+
20
+ ```bash
21
+ npm install @dqcai/sqlite
22
+ ```
23
+
24
+ ### Cài đặt dependencies theo môi trường
25
+
26
+ #### Node.js
27
+ ```bash
28
+ npm install sqlite3
29
+ ```
30
+
31
+ #### Browser
32
+ Không cần cài đặt thêm - sử dụng sql.js từ CDN
33
+
34
+ #### Deno
35
+ Không cần cài đặt - sử dụng Deno SQLite module
36
+
37
+ #### Bun
38
+ Sử dụng built-in SQLite của Bun
39
+
40
+ #### React Native
41
+ ```bash
42
+ # Standard React Native
43
+ npm install react-native-sqlite-storage
44
+ # hoặc
45
+ npm install expo-sqlite
46
+
47
+ # React Native Windows
48
+ npm install react-native-sqlite-2
49
+ # hoặc
50
+ npm install react-native-windows-sqlite
51
+ ```
52
+
53
+ ## 📖 Sử dụng cơ bản
54
+
55
+ ### Import thư viện
56
+
57
+ ```typescript
58
+ import UniversalSQLite, { SQLiteManager, QueryBuilder } from '@dqcai/sqlite';
59
+ ```
60
+
61
+ ### Kết nối và sử dụng đơn giản
62
+
63
+ ```typescript
64
+ const db = new UniversalSQLite();
65
+
66
+ async function basicExample() {
67
+ try {
68
+ // Kết nối đến database
69
+ await db.connect('myapp.db');
70
+
71
+ // Tạo bảng
72
+ await db.createTable('users', {
73
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
74
+ name: 'TEXT NOT NULL',
75
+ email: 'TEXT UNIQUE',
76
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
77
+ });
78
+
79
+ // Thêm dữ liệu
80
+ await db.insert('users', {
81
+ name: 'John Doe',
82
+ email: 'john@example.com'
83
+ });
84
+
85
+ // Truy vấn dữ liệu
86
+ const users = await db.select('users');
87
+ console.log('All users:', users.rows);
88
+
89
+ // Tìm kiếm có điều kiện
90
+ const john = await db.select('users', 'name = ?', ['John Doe']);
91
+ console.log('John:', john.rows[0]);
92
+
93
+ // Cập nhật
94
+ await db.update('users',
95
+ { email: 'john.doe@example.com' },
96
+ 'name = ?',
97
+ ['John Doe']
98
+ );
99
+
100
+ // Xóa
101
+ await db.delete('users', 'id = ?', [1]);
102
+
103
+ } catch (error) {
104
+ console.error('Database error:', error);
105
+ } finally {
106
+ await db.close();
107
+ }
108
+ }
109
+
110
+ basicExample();
111
+ ```
112
+
113
+ ## 🔧 API Chi tiết
114
+
115
+ ### UniversalSQLite Class
116
+
117
+ #### Khởi tạo
118
+ ```typescript
119
+ const db = new UniversalSQLite();
120
+ ```
121
+
122
+ #### Phương thức chính
123
+
124
+ ##### `connect(path: string): Promise<void>`
125
+ ```typescript
126
+ // In-memory database
127
+ await db.connect(':memory:');
128
+
129
+ // File database
130
+ await db.connect('database.db');
131
+
132
+ // Browser (file hoặc in-memory)
133
+ await db.connect('myapp.db');
134
+ ```
135
+
136
+ ##### `query(sql: string, params?: any[]): Promise<SQLiteResult>`
137
+ ```typescript
138
+ // Truy vấn đơn giản
139
+ const result = await db.query('SELECT * FROM users');
140
+
141
+ // Truy vấn với parameters
142
+ const result = await db.query('SELECT * FROM users WHERE age > ?', [18]);
143
+
144
+ console.log(result.rows); // Dữ liệu trả về
145
+ console.log(result.rowsAffected); // Số dòng bị ảnh hưởng
146
+ console.log(result.lastInsertRowId); // ID của dòng mới insert
147
+ ```
148
+
149
+ ##### `createTable(name: string, schema: Record<string, string>): Promise<void>`
150
+ ```typescript
151
+ await db.createTable('products', {
152
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
153
+ name: 'TEXT NOT NULL',
154
+ price: 'REAL',
155
+ category_id: 'INTEGER',
156
+ 'FOREIGN KEY (category_id)': 'REFERENCES categories(id)'
157
+ });
158
+ ```
159
+
160
+ ##### `insert(table: string, data: Record<string, any>): Promise<SQLiteResult>`
161
+ ```typescript
162
+ const result = await db.insert('users', {
163
+ name: 'Alice',
164
+ email: 'alice@example.com',
165
+ age: 25
166
+ });
167
+
168
+ console.log('New user ID:', result.lastInsertRowId);
169
+ ```
170
+
171
+ ##### `select(table: string, where?: string, params?: any[]): Promise<SQLiteResult>`
172
+ ```typescript
173
+ // Lấy tất cả
174
+ const allUsers = await db.select('users');
175
+
176
+ // Có điều kiện
177
+ const adults = await db.select('users', 'age >= ?', [18]);
178
+
179
+ // Điều kiện phức tạp
180
+ const result = await db.select('users', 'age BETWEEN ? AND ? AND name LIKE ?', [18, 65, 'A%']);
181
+ ```
182
+
183
+ ##### `update(table: string, data: Record<string, any>, where: string, whereParams?: any[]): Promise<SQLiteResult>`
184
+ ```typescript
185
+ const result = await db.update('users',
186
+ { email: 'newemail@example.com', age: 26 },
187
+ 'id = ?',
188
+ [1]
189
+ );
190
+
191
+ console.log('Updated rows:', result.rowsAffected);
192
+ ```
193
+
194
+ ##### `delete(table: string, where: string, params?: any[]): Promise<SQLiteResult>`
195
+ ```typescript
196
+ const result = await db.delete('users', 'age < ?', [18]);
197
+ console.log('Deleted rows:', result.rowsAffected);
198
+ ```
199
+
200
+ ### QueryBuilder - Xây dựng truy vấn nâng cao
201
+
202
+ ```typescript
203
+ import { QueryBuilder } from '@dqcai/sqlite';
204
+
205
+ // SELECT với QueryBuilder
206
+ const selectSQL = QueryBuilder
207
+ .table('users')
208
+ .select(['id', 'name', 'email'])
209
+ .where('age > 18')
210
+ .where('status = "active"')
211
+ .orderBy('name', 'ASC')
212
+ .limit(10)
213
+ .offset(0)
214
+ .toSQL();
215
+
216
+ console.log(selectSQL);
217
+ // Output: SELECT id, name, email FROM users WHERE age > 18 AND status = "active" ORDER BY name ASC LIMIT 10 OFFSET 0
218
+
219
+ const result = await db.query(selectSQL);
220
+
221
+ // INSERT với QueryBuilder
222
+ const insertSQL = QueryBuilder.insert('users', {
223
+ name: 'Bob',
224
+ email: 'bob@example.com',
225
+ age: 30
226
+ });
227
+
228
+ await db.query(insertSQL, ['Bob', 'bob@example.com', 30]);
229
+
230
+ // UPDATE với QueryBuilder
231
+ const updateSQL = QueryBuilder.update('users',
232
+ { email: 'bob.updated@example.com' },
233
+ 'id = 1'
234
+ );
235
+
236
+ await db.query(updateSQL, ['bob.updated@example.com']);
237
+
238
+ // DELETE với QueryBuilder
239
+ const deleteSQL = QueryBuilder.delete('users', 'age < 18');
240
+ await db.query(deleteSQL);
241
+ ```
242
+
243
+ ### SQLiteManager - Quản lý kết nối thủ công
244
+
245
+ ```typescript
246
+ import { SQLiteManager } from '@dqcai/sqlite';
247
+
248
+ const manager = new SQLiteManager();
249
+
250
+ // Kiểm tra môi trường
251
+ console.log('Environment:', manager.getEnvironmentInfo());
252
+
253
+ // Kết nối thủ công
254
+ const connection = await manager.connect('database.db');
255
+
256
+ // Thực thi truy vấn
257
+ const result = await connection.execute('SELECT * FROM users');
258
+
259
+ // Đóng kết nối
260
+ await connection.close();
261
+ ```
262
+
263
+ ## 🌍 Ví dụ theo môi trường
264
+
265
+ ### Node.js Application
266
+
267
+ ```typescript
268
+ // server.js
269
+ import UniversalSQLite from '@dqcai/sqlite';
270
+
271
+ const db = new UniversalSQLite();
272
+
273
+ async function setupUserSystem() {
274
+ await db.connect('./users.db');
275
+
276
+ // Tạo bảng users
277
+ await db.createTable('users', {
278
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
279
+ username: 'TEXT UNIQUE NOT NULL',
280
+ password_hash: 'TEXT NOT NULL',
281
+ email: 'TEXT UNIQUE',
282
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
283
+ last_login: 'DATETIME'
284
+ });
285
+
286
+ // Tạo index cho performance
287
+ await db.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
288
+
289
+ console.log('User system ready!');
290
+ }
291
+
292
+ async function createUser(username: string, email: string, passwordHash: string) {
293
+ return await db.insert('users', {
294
+ username,
295
+ email,
296
+ password_hash: passwordHash
297
+ });
298
+ }
299
+
300
+ async function getUserByEmail(email: string) {
301
+ const result = await db.select('users', 'email = ?', [email]);
302
+ return result.rows[0] || null;
303
+ }
304
+
305
+ setupUserSystem();
306
+ ```
307
+
308
+ ### Browser Application
309
+
310
+ ```html
311
+ <!DOCTYPE html>
312
+ <html>
313
+ <head>
314
+ <title>Universal SQLite Browser Demo</title>
315
+ </head>
316
+ <body>
317
+ <div id="app"></div>
318
+
319
+ <script type="module">
320
+ import UniversalSQLite from './dist/index.js';
321
+
322
+ const db = new UniversalSQLite();
323
+
324
+ async function browserDemo() {
325
+ try {
326
+ // Kết nối in-memory database
327
+ await db.connect(':memory:');
328
+
329
+ // Tạo bảng todos
330
+ await db.createTable('todos', {
331
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
332
+ title: 'TEXT NOT NULL',
333
+ completed: 'BOOLEAN DEFAULT 0',
334
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
335
+ });
336
+
337
+ // Thêm một số todos
338
+ await db.insert('todos', { title: 'Learn SQLite', completed: 0 });
339
+ await db.insert('todos', { title: 'Build awesome app', completed: 0 });
340
+
341
+ // Hiển thị todos
342
+ const todos = await db.select('todos');
343
+ document.getElementById('app').innerHTML = `
344
+ <h1>My Todos</h1>
345
+ <ul>
346
+ ${todos.rows.map(todo =>
347
+ `<li>${todo.title} - ${todo.completed ? '✅' : '⏳'}</li>`
348
+ ).join('')}
349
+ </ul>
350
+ `;
351
+
352
+ } catch (error) {
353
+ console.error('Error:', error);
354
+ }
355
+ }
356
+
357
+ browserDemo();
358
+ </script>
359
+ </body>
360
+ </html>
361
+ ```
362
+
363
+ ### React Native Application
364
+
365
+ ```typescript
366
+ // App.tsx
367
+ import React, { useState, useEffect } from 'react';
368
+ import { View, Text, Button, FlatList } from 'react-native';
369
+ import UniversalSQLite from '@dqcai/sqlite';
370
+
371
+ const db = new UniversalSQLite();
372
+
373
+ interface User {
374
+ id: number;
375
+ name: string;
376
+ email: string;
377
+ }
378
+
379
+ export default function App() {
380
+ const [users, setUsers] = useState<User[]>([]);
381
+ const [environment, setEnvironment] = useState<string>('');
382
+
383
+ useEffect(() => {
384
+ initDatabase();
385
+ }, []);
386
+
387
+ const initDatabase = async () => {
388
+ try {
389
+ // Kết nối database
390
+ await db.connect('myapp.db');
391
+
392
+ // Hiển thị môi trường
393
+ setEnvironment(db.getEnvironment());
394
+
395
+ // Tạo bảng
396
+ await db.createTable('users', {
397
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
398
+ name: 'TEXT NOT NULL',
399
+ email: 'TEXT UNIQUE'
400
+ });
401
+
402
+ // Load dữ liệu
403
+ loadUsers();
404
+
405
+ } catch (error) {
406
+ console.error('Database init error:', error);
407
+ }
408
+ };
409
+
410
+ const loadUsers = async () => {
411
+ try {
412
+ const result = await db.select('users');
413
+ setUsers(result.rows);
414
+ } catch (error) {
415
+ console.error('Load users error:', error);
416
+ }
417
+ };
418
+
419
+ const addUser = async () => {
420
+ try {
421
+ await db.insert('users', {
422
+ name: `User ${Date.now()}`,
423
+ email: `user${Date.now()}@example.com`
424
+ });
425
+ loadUsers();
426
+ } catch (error) {
427
+ console.error('Add user error:', error);
428
+ }
429
+ };
430
+
431
+ return (
432
+ <View style={{ flex: 1, padding: 20 }}>
433
+ <Text>Environment: {environment}</Text>
434
+ <Button title="Add User" onPress={addUser} />
435
+
436
+ <FlatList
437
+ data={users}
438
+ keyExtractor={item => item.id.toString()}
439
+ renderItem={({ item }) => (
440
+ <View style={{ padding: 10, borderBottomWidth: 1 }}>
441
+ <Text>{item.name}</Text>
442
+ <Text>{item.email}</Text>
443
+ </View>
444
+ )}
445
+ />
446
+ </View>
447
+ );
448
+ }
449
+ ```
450
+
451
+ ### Deno Application
452
+
453
+ ```typescript
454
+ // main.ts
455
+ import UniversalSQLite from "https://esm.sh/@dqcai/sqlite";
456
+
457
+ const db = new UniversalSQLite();
458
+
459
+ async function denoExample() {
460
+ try {
461
+ await db.connect('./deno_database.db');
462
+
463
+ // Tạo bảng logs
464
+ await db.createTable('logs', {
465
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
466
+ level: 'TEXT NOT NULL',
467
+ message: 'TEXT NOT NULL',
468
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
469
+ });
470
+
471
+ // Thêm log entries
472
+ await db.insert('logs', { level: 'INFO', message: 'Application started' });
473
+ await db.insert('logs', { level: 'DEBUG', message: 'Database connected' });
474
+ await db.insert('logs', { level: 'ERROR', message: 'Something went wrong' });
475
+
476
+ // Truy vấn logs theo level
477
+ const errorLogs = await db.select('logs', 'level = ?', ['ERROR']);
478
+ console.log('Error logs:', errorLogs.rows);
479
+
480
+ // Sử dụng QueryBuilder
481
+ const recentLogs = QueryBuilder
482
+ .table('logs')
483
+ .select(['level', 'message', 'timestamp'])
484
+ .where('timestamp > datetime("now", "-1 hour")')
485
+ .orderBy('timestamp', 'DESC')
486
+ .limit(10)
487
+ .toSQL();
488
+
489
+ const result = await db.query(recentLogs);
490
+ console.log('Recent logs:', result.rows);
491
+
492
+ } finally {
493
+ await db.close();
494
+ }
495
+ }
496
+
497
+ // Chạy với: deno run --allow-read --allow-write main.ts
498
+ denoExample();
499
+ ```
500
+
501
+ ### Bun Application
502
+
503
+ ```typescript
504
+ // app.ts
505
+ import UniversalSQLite from '@dqcai/sqlite';
506
+
507
+ const db = new UniversalSQLite();
508
+
509
+ async function bunExample() {
510
+ console.log('Environment:', db.getEnvironment()); // "Bun"
511
+
512
+ await db.connect('./bun_database.db');
513
+
514
+ // Tạo bảng products
515
+ await db.createTable('products', {
516
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
517
+ name: 'TEXT NOT NULL',
518
+ price: 'REAL NOT NULL',
519
+ category: 'TEXT',
520
+ in_stock: 'BOOLEAN DEFAULT 1'
521
+ });
522
+
523
+ // Bulk insert
524
+ const products = [
525
+ { name: 'Laptop', price: 999.99, category: 'Electronics' },
526
+ { name: 'Mouse', price: 29.99, category: 'Electronics' },
527
+ { name: 'Desk', price: 199.99, category: 'Furniture' }
528
+ ];
529
+
530
+ for (const product of products) {
531
+ await db.insert('products', product);
532
+ }
533
+
534
+ // Truy vấn phức tạp
535
+ const expensiveProducts = await db.query(`
536
+ SELECT name, price, category
537
+ FROM products
538
+ WHERE price > ? AND in_stock = 1
539
+ ORDER BY price DESC
540
+ `, [100]);
541
+
542
+ console.log('Expensive products:', expensiveProducts.rows);
543
+
544
+ await db.close();
545
+ }
546
+
547
+ // Chạy với: bun run app.ts
548
+ bunExample();
549
+ ```
550
+
551
+ ## 🏗️ Ví dụ nâng cao
552
+
553
+ ### 1. Transaction Management
554
+
555
+ ```typescript
556
+ import { SQLiteManager } from '@dqcai/sqlite';
557
+
558
+ async function transactionExample() {
559
+ const manager = new SQLiteManager();
560
+ const connection = await manager.connect('transactions.db');
561
+
562
+ try {
563
+ // Bắt đầu transaction
564
+ await connection.execute('BEGIN TRANSACTION');
565
+
566
+ // Tạo bảng accounts
567
+ await connection.execute(`
568
+ CREATE TABLE IF NOT EXISTS accounts (
569
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
570
+ name TEXT NOT NULL,
571
+ balance REAL DEFAULT 0.00
572
+ )
573
+ `);
574
+
575
+ // Thêm accounts
576
+ await connection.execute('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Alice', 1000]);
577
+ await connection.execute('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Bob', 500]);
578
+
579
+ // Chuyển tiền từ Alice sang Bob
580
+ const transferAmount = 200;
581
+
582
+ // Trừ tiền Alice
583
+ await connection.execute(
584
+ 'UPDATE accounts SET balance = balance - ? WHERE name = ?',
585
+ [transferAmount, 'Alice']
586
+ );
587
+
588
+ // Cộng tiền Bob
589
+ await connection.execute(
590
+ 'UPDATE accounts SET balance = balance + ? WHERE name = ?',
591
+ [transferAmount, 'Bob']
592
+ );
593
+
594
+ // Kiểm tra balance không âm
595
+ const aliceBalance = await connection.execute('SELECT balance FROM accounts WHERE name = ?', ['Alice']);
596
+ if (aliceBalance.rows[0].balance < 0) {
597
+ throw new Error('Insufficient funds');
598
+ }
599
+
600
+ // Commit transaction
601
+ await connection.execute('COMMIT');
602
+ console.log('Transfer successful!');
603
+
604
+ // Hiển thị kết quả
605
+ const allAccounts = await connection.execute('SELECT * FROM accounts');
606
+ console.log('Final balances:', allAccounts.rows);
607
+
608
+ } catch (error) {
609
+ // Rollback nếu có lỗi
610
+ await connection.execute('ROLLBACK');
611
+ console.error('Transaction failed:', error);
612
+ } finally {
613
+ await connection.close();
614
+ }
615
+ }
616
+
617
+ transactionExample();
618
+ ```
619
+
620
+ ### 2. Complex Queries với JOIN
621
+
622
+ ```typescript
623
+ async function complexQueryExample() {
624
+ const db = new UniversalSQLite();
625
+ await db.connect('ecommerce.db');
626
+
627
+ // Tạo schema phức tạp
628
+ await db.createTable('categories', {
629
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
630
+ name: 'TEXT NOT NULL UNIQUE',
631
+ description: 'TEXT'
632
+ });
633
+
634
+ await db.createTable('products', {
635
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
636
+ name: 'TEXT NOT NULL',
637
+ price: 'REAL NOT NULL',
638
+ category_id: 'INTEGER NOT NULL',
639
+ 'FOREIGN KEY (category_id)': 'REFERENCES categories(id)'
640
+ });
641
+
642
+ await db.createTable('orders', {
643
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
644
+ product_id: 'INTEGER NOT NULL',
645
+ quantity: 'INTEGER NOT NULL',
646
+ order_date: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
647
+ 'FOREIGN KEY (product_id)': 'REFERENCES products(id)'
648
+ });
649
+
650
+ // Thêm dữ liệu mẫu
651
+ await db.insert('categories', { name: 'Electronics', description: 'Electronic devices' });
652
+ await db.insert('categories', { name: 'Books', description: 'Books and literature' });
653
+
654
+ await db.insert('products', { name: 'iPhone 15', price: 999, category_id: 1 });
655
+ await db.insert('products', { name: 'MacBook Pro', price: 2499, category_id: 1 });
656
+ await db.insert('products', { name: 'JavaScript Guide', price: 29.99, category_id: 2 });
657
+
658
+ await db.insert('orders', { product_id: 1, quantity: 2 });
659
+ await db.insert('orders', { product_id: 2, quantity: 1 });
660
+ await db.insert('orders', { product_id: 1, quantity: 1 });
661
+
662
+ // Query phức tạp với JOIN
663
+ const salesReport = await db.query(`
664
+ SELECT
665
+ c.name as category,
666
+ p.name as product,
667
+ SUM(o.quantity) as total_sold,
668
+ SUM(o.quantity * p.price) as total_revenue,
669
+ AVG(p.price) as avg_price
670
+ FROM orders o
671
+ JOIN products p ON o.product_id = p.id
672
+ JOIN categories c ON p.category_id = c.id
673
+ GROUP BY c.id, p.id
674
+ ORDER BY total_revenue DESC
675
+ `);
676
+
677
+ console.log('Sales Report:', salesReport.rows);
678
+
679
+ // Subquery example
680
+ const topSellingCategory = await db.query(`
681
+ SELECT
682
+ c.name,
683
+ (SELECT SUM(quantity) FROM orders o
684
+ JOIN products p ON o.product_id = p.id
685
+ WHERE p.category_id = c.id) as total_quantity
686
+ FROM categories c
687
+ ORDER BY total_quantity DESC
688
+ LIMIT 1
689
+ `);
690
+
691
+ console.log('Top selling category:', topSellingCategory.rows[0]);
692
+
693
+ await db.close();
694
+ }
695
+
696
+ complexQueryExample();
697
+ ```
698
+
699
+ ### 3. Real-time Data Synchronization
700
+
701
+ ```typescript
702
+ async function realtimeExample() {
703
+ const db = new UniversalSQLite();
704
+ await db.connect('realtime.db');
705
+
706
+ // Tạo bảng cho sync
707
+ await db.createTable('sync_queue', {
708
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
709
+ table_name: 'TEXT NOT NULL',
710
+ operation: 'TEXT NOT NULL', // INSERT, UPDATE, DELETE
711
+ data: 'TEXT', // JSON data
712
+ synced: 'BOOLEAN DEFAULT 0',
713
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
714
+ });
715
+
716
+ await db.createTable('users', {
717
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
718
+ name: 'TEXT NOT NULL',
719
+ email: 'TEXT UNIQUE',
720
+ last_modified: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
721
+ });
722
+
723
+ // Helper function để track changes
724
+ async function trackChange(table: string, operation: string, data: any) {
725
+ await db.insert('sync_queue', {
726
+ table_name: table,
727
+ operation: operation,
728
+ data: JSON.stringify(data)
729
+ });
730
+ }
731
+
732
+ // Wrapper functions với tracking
733
+ async function insertUser(userData: any) {
734
+ const result = await db.insert('users', userData);
735
+ await trackChange('users', 'INSERT', { ...userData, id: result.lastInsertRowId });
736
+ return result;
737
+ }
738
+
739
+ async function updateUser(id: number, userData: any) {
740
+ const result = await db.update('users',
741
+ { ...userData, last_modified: new Date().toISOString() },
742
+ 'id = ?',
743
+ [id]
744
+ );
745
+ await trackChange('users', 'UPDATE', { id, ...userData });
746
+ return result;
747
+ }
748
+
749
+ async function deleteUser(id: number) {
750
+ const result = await db.delete('users', 'id = ?', [id]);
751
+ await trackChange('users', 'DELETE', { id });
752
+ return result;
753
+ }
754
+
755
+ // Sync function
756
+ async function syncToServer() {
757
+ const unsyncedChanges = await db.select('sync_queue', 'synced = 0');
758
+
759
+ for (const change of unsyncedChanges.rows) {
760
+ try {
761
+ // Giả lập API call
762
+ console.log(`Syncing ${change.operation} on ${change.table_name}:`,
763
+ JSON.parse(change.data));
764
+
765
+ // Đánh dấu đã sync
766
+ await db.update('sync_queue',
767
+ { synced: 1 },
768
+ 'id = ?',
769
+ [change.id]
770
+ );
771
+
772
+ } catch (error) {
773
+ console.error('Sync failed for change:', change.id, error);
774
+ }
775
+ }
776
+ }
777
+
778
+ // Demo usage
779
+ await insertUser({ name: 'Alice', email: 'alice@example.com' });
780
+ await insertUser({ name: 'Bob', email: 'bob@example.com' });
781
+ await updateUser(1, { email: 'alice.updated@example.com' });
782
+
783
+ // Sync changes
784
+ await syncToServer();
785
+
786
+ await db.close();
787
+ }
788
+
789
+ realtimeExample();
790
+ ```
791
+
792
+ ### 4. Database Migration System
793
+
794
+ ```typescript
795
+ class DatabaseMigration {
796
+ private db: UniversalSQLite;
797
+ private currentVersion: number = 0;
798
+
799
+ constructor(db: UniversalSQLite) {
800
+ this.db = db;
801
+ }
802
+
803
+ async getCurrentVersion(): Promise<number> {
804
+ try {
805
+ const result = await this.db.query('PRAGMA user_version');
806
+ return result.rows[0].user_version || 0;
807
+ } catch {
808
+ return 0;
809
+ }
810
+ }
811
+
812
+ async setVersion(version: number): Promise<void> {
813
+ await this.db.query(`PRAGMA user_version = ${version}`);
814
+ }
815
+
816
+ async migrate() {
817
+ this.currentVersion = await this.getCurrentVersion();
818
+ console.log(`Current database version: ${this.currentVersion}`);
819
+
820
+ const migrations = [
821
+ this.migration1_initial,
822
+ this.migration2_addUserProfiles,
823
+ this.migration3_addIndexes,
824
+ this.migration4_addAuditLog
825
+ ];
826
+
827
+ for (let i = this.currentVersion; i < migrations.length; i++) {
828
+ console.log(`Running migration ${i + 1}...`);
829
+ await migrations[i].call(this);
830
+ await this.setVersion(i + 1);
831
+ console.log(`Migration ${i + 1} completed`);
832
+ }
833
+
834
+ console.log('All migrations completed!');
835
+ }
836
+
837
+ // Migration 1: Initial schema
838
+ private async migration1_initial() {
839
+ await this.db.createTable('users', {
840
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
841
+ username: 'TEXT UNIQUE NOT NULL',
842
+ email: 'TEXT UNIQUE NOT NULL',
843
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
844
+ });
845
+ }
846
+
847
+ // Migration 2: Add user profiles
848
+ private async migration2_addUserProfiles() {
849
+ await this.db.query('ALTER TABLE users ADD COLUMN first_name TEXT');
850
+ await this.db.query('ALTER TABLE users ADD COLUMN last_name TEXT');
851
+ await this.db.query('ALTER TABLE users ADD COLUMN avatar_url TEXT');
852
+ }
853
+
854
+ // Migration 3: Add performance indexes
855
+ private async migration3_addIndexes() {
856
+ await this.db.query('CREATE INDEX idx_users_email ON users(email)');
857
+ await this.db.query('CREATE INDEX idx_users_username ON users(username)');
858
+ await this.db.query('CREATE INDEX idx_users_created_at ON users(created_at)');
859
+ }
860
+
861
+ // Migration 4: Add audit log
862
+ private async migration4_addAuditLog() {
863
+ await this.db.createTable('audit_logs', {
864
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
865
+ table_name: 'TEXT NOT NULL',
866
+ record_id: 'INTEGER NOT NULL',
867
+ action: 'TEXT NOT NULL',
868
+ old_values: 'TEXT',
869
+ new_values: 'TEXT',
870
+ user_id: 'INTEGER',
871
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
872
+ });
873
+
874
+ // Tạo trigger cho audit
875
+ await this.db.query(`
876
+ CREATE TRIGGER audit_users_update
877
+ AFTER UPDATE ON users
878
+ FOR EACH ROW
879
+ BEGIN
880
+ INSERT INTO audit_logs (table_name, record_id, action, old_values, new_values)
881
+ VALUES ('users', NEW.id, 'UPDATE',
882
+ json_object('username', OLD.username, 'email', OLD.email),
883
+ json_object('username', NEW.username, 'email', NEW.email));
884
+ END
885
+ `);
886
+ }
887
+ }
888
+
889
+ // Sử dụng migration
890
+ async function migrationExample() {
891
+ const db = new UniversalSQLite();
892
+ await db.connect('app_with_migrations.db');
893
+
894
+ const migration = new DatabaseMigration(db);
895
+ await migration.migrate();
896
+
897
+ // Test data sau migration
898
+ await db.insert('users', {
899
+ username: 'testuser',
900
+ email: 'test@example.com',
901
+ first_name: 'Test',
902
+ last_name: 'User'
903
+ });
904
+
905
+ // Update để trigger audit
906
+ await db.update('users',
907
+ { email: 'test.updated@example.com' },
908
+ 'username = ?',
909
+ ['testuser']
910
+ );
911
+
912
+ // Kiểm tra audit log
913
+ const auditLogs = await db.select('audit_logs');
914
+ console.log('Audit logs:', auditLogs.rows);
915
+
916
+ await db.close();
917
+ }
918
+
919
+ migrationExample();
920
+ ```
921
+
922
+ ### 5. Full-Text Search
923
+
924
+ ```typescript
925
+ async function fullTextSearchExample() {
926
+ const db = new UniversalSQLite();
927
+ await db.connect('search.db');
928
+
929
+ // Tạo FTS table
930
+ await db.query(`
931
+ CREATE VIRTUAL TABLE IF NOT EXISTS articles_fts
932
+ USING fts5(id, title, content, author)
933
+ `);
934
+
935
+ // Tạo bảng articles thường
936
+ await db.createTable('articles', {
937
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
938
+ title: 'TEXT NOT NULL',
939
+ content: 'TEXT NOT NULL',
940
+ author: 'TEXT NOT NULL',
941
+ published_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
942
+ });
943
+
944
+ // Thêm dữ liệu mẫu
945
+ const articles = [
946
+ {
947
+ title: 'Introduction to SQLite',
948
+ content: 'SQLite is a lightweight database engine that is perfect for mobile and web applications. It provides ACID compliance and supports most of the SQL standard.',
949
+ author: 'John Doe'
950
+ },
951
+ {
952
+ title: 'Advanced TypeScript Patterns',
953
+ content: 'TypeScript provides powerful type system features including generics, conditional types, and mapped types that enable building robust applications.',
954
+ author: 'Jane Smith'
955
+ },
956
+ {
957
+ title: 'React Native Performance Tips',
958
+ content: 'Optimizing React Native apps requires understanding of the bridge, native modules, and proper state management patterns.',
959
+ author: 'Bob Johnson'
960
+ }
961
+ ];
962
+
963
+ for (const article of articles) {
964
+ const result = await db.insert('articles', article);
965
+ // Thêm vào FTS index
966
+ await db.query(`
967
+ INSERT INTO articles_fts (id, title, content, author)
968
+ VALUES (?, ?, ?, ?)
969
+ `, [result.lastInsertRowId, article.title, article.content, article.author]);
970
+ }
971
+
972
+ // Full-text search
973
+ const searchResults = await db.query(`
974
+ SELECT
975
+ a.id, a.title, a.author, a.published_at,
976
+ snippet(articles_fts, 1, '<b>', '</b>', '...', 50) as title_snippet,
977
+ snippet(articles_fts, 2, '<b>', '</b>', '...', 100) as content_snippet
978
+ FROM articles_fts
979
+ JOIN articles a ON articles_fts.id = a.id
980
+ WHERE articles_fts MATCH ?
981
+ ORDER BY rank
982
+ `, ['SQLite OR TypeScript']);
983
+
984
+ console.log('Search results:', searchResults.rows);
985
+
986
+ // Advanced search với bxm25 ranking
987
+ const advancedSearch = await db.query(`
988
+ SELECT
989
+ a.*,
990
+ bm25(articles_fts) as relevance_score
991
+ FROM articles_fts
992
+ JOIN articles a ON articles_fts.id = a.id
993
+ WHERE articles_fts MATCH ?
994
+ ORDER BY relevance_score
995
+ `, ['application AND (mobile OR web)']);
996
+
997
+ console.log('Advanced search:', advancedSearch.rows);
998
+
999
+ await db.close();
1000
+ }
1001
+
1002
+ fullTextSearchExample();
1003
+ ```
1004
+
1005
+ ### 6. Data Analytics và Reporting
1006
+
1007
+ ```typescript
1008
+ async function analyticsExample() {
1009
+ const db = new UniversalSQLite();
1010
+ await db.connect('analytics.db');
1011
+
1012
+ // Tạo schema cho analytics
1013
+ await db.createTable('events', {
1014
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1015
+ user_id: 'INTEGER NOT NULL',
1016
+ event_type: 'TEXT NOT NULL',
1017
+ event_data: 'TEXT', // JSON
1018
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1019
+ });
1020
+
1021
+ await db.createTable('users_analytics', {
1022
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1023
+ user_id: 'INTEGER UNIQUE NOT NULL',
1024
+ total_events: 'INTEGER DEFAULT 0',
1025
+ first_seen: 'DATETIME',
1026
+ last_seen: 'DATETIME',
1027
+ session_count: 'INTEGER DEFAULT 0'
1028
+ });
1029
+
1030
+ // Thêm dữ liệu events mẫu
1031
+ const sampleEvents = [
1032
+ { user_id: 1, event_type: 'page_view', event_data: '{"page": "/home"}' },
1033
+ { user_id: 1, event_type: 'click', event_data: '{"element": "signup_button"}' },
1034
+ { user_id: 2, event_type: 'page_view', event_data: '{"page": "/products"}' },
1035
+ { user_id: 1, event_type: 'purchase', event_data: '{"product_id": 123, "amount": 99.99}' },
1036
+ { user_id: 3, event_type: 'page_view', event_data: '{"page": "/home"}' },
1037
+ { user_id: 2, event_type: 'purchase', event_data: '{"product_id": 456, "amount": 149.99}' }
1038
+ ];
1039
+
1040
+ for (const event of sampleEvents) {
1041
+ await db.insert('events', event);
1042
+ }
1043
+
1044
+ // Analytics queries
1045
+
1046
+ // 1. User engagement report
1047
+ const userEngagement = await db.query(`
1048
+ SELECT
1049
+ user_id,
1050
+ COUNT(*) as total_events,
1051
+ COUNT(CASE WHEN event_type = 'purchase' THEN 1 END) as purchases,
1052
+ MIN(timestamp) as first_seen,
1053
+ MAX(timestamp) as last_seen,
1054
+ ROUND(
1055
+ JULIANDAY(MAX(timestamp)) - JULIANDAY(MIN(timestamp))
1056
+ ) as days_active
1057
+ FROM events
1058
+ GROUP BY user_id
1059
+ ORDER BY total_events DESC
1060
+ `);
1061
+
1062
+ console.log('User Engagement Report:', userEngagement.rows);
1063
+
1064
+ // 2. Revenue analytics
1065
+ const revenueReport = await db.query(`
1066
+ SELECT
1067
+ DATE(timestamp) as date,
1068
+ COUNT(*) as purchase_count,
1069
+ SUM(CAST(json_extract(event_data, '$.amount') AS REAL)) as total_revenue,
1070
+ AVG(CAST(json_extract(event_data, '$.amount') AS REAL)) as avg_order_value
1071
+ FROM events
1072
+ WHERE event_type = 'purchase'
1073
+ GROUP BY DATE(timestamp)
1074
+ ORDER BY date DESC
1075
+ `);
1076
+
1077
+ console.log('Revenue Report:', revenueReport.rows);
1078
+
1079
+ // 3. Funnel analysis
1080
+ const funnelAnalysis = await db.query(`
1081
+ WITH user_funnel AS (
1082
+ SELECT
1083
+ user_id,
1084
+ MAX(CASE WHEN event_type = 'page_view' THEN 1 ELSE 0 END) as viewed,
1085
+ MAX(CASE WHEN event_type = 'click' THEN 1 ELSE 0 END) as clicked,
1086
+ MAX(CASE WHEN event_type = 'purchase' THEN 1 ELSE 0 END) as purchased
1087
+ FROM events
1088
+ GROUP BY user_id
1089
+ )
1090
+ SELECT
1091
+ SUM(viewed) as total_users,
1092
+ SUM(clicked) as users_clicked,
1093
+ SUM(purchased) as users_purchased,
1094
+ ROUND(SUM(clicked) * 100.0 / SUM(viewed), 2) as click_rate_percent,
1095
+ ROUND(SUM(purchased) * 100.0 / SUM(clicked), 2) as conversion_rate_percent
1096
+ FROM user_funnel
1097
+ `);
1098
+
1099
+ console.log('Funnel Analysis:', funnelAnalysis.rows[0]);
1100
+
1101
+ await db.close();
1102
+ }
1103
+
1104
+ analyticsExample();
1105
+ ```
1106
+
1107
+ ## 🔍 Debugging và Monitoring
1108
+
1109
+ ### 1. Query Performance Monitoring
1110
+
1111
+ ```typescript
1112
+ class PerformanceMonitor {
1113
+ private db: UniversalSQLite;
1114
+
1115
+ constructor(db: UniversalSQLite) {
1116
+ this.db = db;
1117
+ }
1118
+
1119
+ async queryWithTiming(sql: string, params?: any[]): Promise<{result: any, duration: number}> {
1120
+ const startTime = performance.now();
1121
+ const result = await this.db.query(sql, params);
1122
+ const endTime = performance.now();
1123
+ const duration = endTime - startTime;
1124
+
1125
+ console.log(`Query executed in ${duration.toFixed(2)}ms: ${sql.substring(0, 100)}...`);
1126
+
1127
+ return { result, duration };
1128
+ }
1129
+
1130
+ async explainQuery(sql: string): Promise<void> {
1131
+ const explanation = await this.db.query(`EXPLAIN QUERY PLAN ${sql}`);
1132
+ console.log('Query Plan:');
1133
+ explanation.rows.forEach(row => {
1134
+ console.log(` ${row.detail}`);
1135
+ });
1136
+ }
1137
+ }
1138
+
1139
+ async function performanceExample() {
1140
+ const db = new UniversalSQLite();
1141
+ await db.connect('performance_test.db');
1142
+ const monitor = new PerformanceMonitor(db);
1143
+
1144
+ // Tạo bảng với nhiều dữ liệu
1145
+ await db.createTable('large_table', {
1146
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1147
+ name: 'TEXT',
1148
+ value: 'INTEGER',
1149
+ category: 'TEXT'
1150
+ });
1151
+
1152
+ // Insert bulk data
1153
+ console.log('Inserting test data...');
1154
+ for (let i = 0; i < 1000; i++) {
1155
+ await db.insert('large_table', {
1156
+ name: `Item ${i}`,
1157
+ value: Math.floor(Math.random() * 1000),
1158
+ category: `Category ${i % 10}`
1159
+ });
1160
+ }
1161
+
1162
+ // Test query performance
1163
+ const { result, duration } = await monitor.queryWithTiming(`
1164
+ SELECT category, COUNT(*) as count, AVG(value) as avg_value
1165
+ FROM large_table
1166
+ GROUP BY category
1167
+ ORDER BY count DESC
1168
+ `);
1169
+
1170
+ console.log('Aggregation result:', result.rows);
1171
+
1172
+ // Explain query
1173
+ await monitor.explainQuery('SELECT * FROM large_table WHERE value > 500');
1174
+
1175
+ await db.close();
1176
+ }
1177
+
1178
+ performanceExample();
1179
+ ```
1180
+
1181
+ ### 2. Connection Pool (Advanced)
1182
+
1183
+ ```typescript
1184
+ class ConnectionPool {
1185
+ private connections: Map<string, UniversalSQLite> = new Map();
1186
+ private maxConnections: number = 5;
1187
+
1188
+ async getConnection(dbPath: string): Promise<UniversalSQLite> {
1189
+ if (this.connections.has(dbPath)) {
1190
+ return this.connections.get(dbPath)!;
1191
+ }
1192
+
1193
+ if (this.connections.size >= this.maxConnections) {
1194
+ throw new Error('Connection pool exhausted');
1195
+ }
1196
+
1197
+ const db = new UniversalSQLite();
1198
+ await db.connect(dbPath);
1199
+ this.connections.set(dbPath, db);
1200
+
1201
+ return db;
1202
+ }
1203
+
1204
+ async closeAll(): Promise<void> {
1205
+ for (const [path, db] of this.connections) {
1206
+ await db.close();
1207
+ }
1208
+ this.connections.clear();
1209
+ }
1210
+ }
1211
+
1212
+ async function connectionPoolExample() {
1213
+ const pool = new ConnectionPool();
1214
+
1215
+ try {
1216
+ // Sử dụng multiple databases
1217
+ const mainDb = await pool.getConnection('main.db');
1218
+ const cacheDb = await pool.getConnection('cache.db');
1219
+ const logsDb = await pool.getConnection('logs.db');
1220
+
1221
+ // Setup schemas
1222
+ await mainDb.createTable('users', {
1223
+ id: 'INTEGER PRIMARY KEY',
1224
+ name: 'TEXT',
1225
+ email: 'TEXT'
1226
+ });
1227
+
1228
+ await cacheDb.createTable('cache_entries', {
1229
+ key: 'TEXT PRIMARY KEY',
1230
+ value: 'TEXT',
1231
+ expires_at: 'DATETIME'
1232
+ });
1233
+
1234
+ await logsDb.createTable('access_logs', {
1235
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1236
+ ip: 'TEXT',
1237
+ path: 'TEXT',
1238
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1239
+ });
1240
+
1241
+ // Concurrent operations
1242
+ await Promise.all([
1243
+ mainDb.insert('users', { name: 'Alice', email: 'alice@test.com' }),
1244
+ cacheDb.insert('cache_entries', { key: 'user:1', value: '{"name":"Alice"}', expires_at: '2024-12-31 23:59:59' }),
1245
+ logsDb.insert('access_logs', { ip: '192.168.1.1', path: '/api/users' })
1246
+ ]);
1247
+
1248
+ console.log('All operations completed successfully');
1249
+
1250
+ } finally {
1251
+ await pool.closeAll();
1252
+ }
1253
+ }
1254
+
1255
+ connectionPoolExample();
1256
+ ```
1257
+
1258
+ ## 🛠️ Configuration và Customization
1259
+
1260
+ ### Environment Detection
1261
+
1262
+ ```typescript
1263
+ import { SQLiteManager } from '@dqcai/sqlite';
1264
+
1265
+ const manager = new SQLiteManager();
1266
+
1267
+ // Kiểm tra môi trường hiện tại
1268
+ const env = manager.getEnvironmentInfo();
1269
+ console.log('Running on:', env);
1270
+
1271
+ // Conditional logic based on environment
1272
+ switch (env) {
1273
+ case 'Node.js':
1274
+ console.log('Server-side database with file system access');
1275
+ break;
1276
+ case 'Browser':
1277
+ console.log('Client-side database with local storage');
1278
+ break;
1279
+ case 'React Native':
1280
+ console.log('Mobile database with native SQLite');
1281
+ break;
1282
+ case 'React Native Windows':
1283
+ console.log('Windows mobile database');
1284
+ break;
1285
+ case 'Deno':
1286
+ console.log('Deno runtime with modern SQLite');
1287
+ break;
1288
+ case 'Bun':
1289
+ console.log('Bun runtime with built-in SQLite');
1290
+ break;
1291
+ }
1292
+ ```
1293
+
1294
+ ### Custom Error Handling
1295
+
1296
+ ```typescript
1297
+ class DatabaseErrorHandler {
1298
+ static async safeExecute<T>(
1299
+ operation: () => Promise<T>,
1300
+ fallback?: T
1301
+ ): Promise<T | null> {
1302
+ try {
1303
+ return await operation();
1304
+ } catch (error) {
1305
+ console.error('Database operation failed:', error);
1306
+
1307
+ if (error.message.includes('UNIQUE constraint failed')) {
1308
+ console.log('Duplicate entry detected');
1309
+ } else if (error.message.includes('no such table')) {
1310
+ console.log('Table does not exist');
1311
+ } else if (error.message.includes('database is locked')) {
1312
+ console.log('Database is busy, retrying...');
1313
+ // Implement retry logic
1314
+ await new Promise(resolve => setTimeout(resolve, 100));
1315
+ return await operation();
1316
+ }
1317
+
1318
+ return fallback || null;
1319
+ }
1320
+ }
1321
+ }
1322
+
1323
+ async function errorHandlingExample() {
1324
+ const db = new UniversalSQLite();
1325
+ await db.connect('error_test.db');
1326
+
1327
+ await db.createTable('users', {
1328
+ id: 'INTEGER PRIMARY KEY',
1329
+ email: 'TEXT UNIQUE NOT NULL'
1330
+ });
1331
+
1332
+ // Safe insert với error handling
1333
+ const user1 = await DatabaseErrorHandler.safeExecute(async () => {
1334
+ return await db.insert('users', { email: 'test@example.com' });
1335
+ });
1336
+
1337
+ // Duplicate insert sẽ fail gracefully
1338
+ const user2 = await DatabaseErrorHandler.safeExecute(async () => {
1339
+ return await db.insert('users', { email: 'test@example.com' });
1340
+ });
1341
+
1342
+ console.log('User1 result:', user1);
1343
+ console.log('User2 result:', user2); // null due to unique constraint
1344
+
1345
+ await db.close();
1346
+ }
1347
+
1348
+ errorHandlingExample();
1349
+ ```
1350
+
1351
+ ## 📱 Platform-Specific Examples
1352
+
1353
+ ### React Native với Expo
1354
+
1355
+ ```typescript
1356
+ // expo-sqlite-example.ts
1357
+ import { StatusBar } from 'expo-status-bar';
1358
+ import { StyleSheet, Text, View, Button, FlatList } from 'react-native';
1359
+ import { useState, useEffect } from 'react';
1360
+ import UniversalSQLite from '@dqcai/sqlite';
1361
+
1362
+ const db = new UniversalSQLite();
1363
+
1364
+ export default function App() {
1365
+ const [todos, setTodos] = useState([]);
1366
+ const [isLoading, setIsLoading] = useState(true);
1367
+
1368
+ useEffect(() => {
1369
+ initDatabase();
1370
+ }, []);
1371
+
1372
+ const initDatabase = async () => {
1373
+ try {
1374
+ await db.connect('todos.db');
1375
+
1376
+ await db.createTable('todos', {
1377
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1378
+ title: 'TEXT NOT NULL',
1379
+ completed: 'BOOLEAN DEFAULT 0',
1380
+ priority: 'INTEGER DEFAULT 1',
1381
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1382
+ });
1383
+
1384
+ loadTodos();
1385
+ } catch (error) {
1386
+ console.error('Database setup error:', error);
1387
+ } finally {
1388
+ setIsLoading(false);
1389
+ }
1390
+ };
1391
+
1392
+ const loadTodos = async () => {
1393
+ try {
1394
+ const result = await db.select('todos', null, []);
1395
+ setTodos(result.rows);
1396
+ } catch (error) {
1397
+ console.error('Load todos error:', error);
1398
+ }
1399
+ };
1400
+
1401
+ const addTodo = async () => {
1402
+ try {
1403
+ await db.insert('todos', {
1404
+ title: `Todo ${Date.now()}`,
1405
+ priority: Math.floor(Math.random() * 3) + 1
1406
+ });
1407
+ loadTodos();
1408
+ } catch (error) {
1409
+ console.error('Add todo error:', error);
1410
+ }
1411
+ };
1412
+
1413
+ const toggleTodo = async (id: number, completed: boolean) => {
1414
+ try {
1415
+ await db.update('todos',
1416
+ { completed: completed ? 0 : 1 },
1417
+ 'id = ?',
1418
+ [id]
1419
+ );
1420
+ loadTodos();
1421
+ } catch (error) {
1422
+ console.error('Toggle todo error:', error);
1423
+ }
1424
+ };
1425
+
1426
+ if (isLoading) {
1427
+ return (
1428
+ <View style={styles.container}>
1429
+ <Text>Loading database...</Text>
1430
+ </View>
1431
+ );
1432
+ }
1433
+
1434
+ return (
1435
+ <View style={styles.container}>
1436
+ <Text style={styles.title}>Universal SQLite Todos</Text>
1437
+ <Text>Environment: {db.getEnvironment()}</Text>
1438
+
1439
+ <Button title="Add Todo" onPress={addTodo} />
1440
+
1441
+ <FlatList
1442
+ data={todos}
1443
+ keyExtractor={item => item.id.toString()}
1444
+ renderItem={({ item }) => (
1445
+ <View style={styles.todoItem}>
1446
+ <Text style={[styles.todoText, item.completed && styles.completed]}>
1447
+ {item.title}
1448
+ </Text>
1449
+ <Button
1450
+ title={item.completed ? "Undo" : "Done"}
1451
+ onPress={() => toggleTodo(item.id, item.completed)}
1452
+ />
1453
+ </View>
1454
+ )}
1455
+ />
1456
+
1457
+ <StatusBar style="auto" />
1458
+ </View>
1459
+ );
1460
+ }
1461
+
1462
+ const styles = StyleSheet.create({
1463
+ container: {
1464
+ flex: 1,
1465
+ backgroundColor: '#fff',
1466
+ alignItems: 'center',
1467
+ justifyContent: 'center',
1468
+ padding: 20,
1469
+ },
1470
+ title: {
1471
+ fontSize: 24,
1472
+ fontWeight: 'bold',
1473
+ marginBottom: 20,
1474
+ },
1475
+ todoItem: {
1476
+ flexDirection: 'row',
1477
+ alignItems: 'center',
1478
+ padding: 10,
1479
+ borderBottomWidth: 1,
1480
+ borderBottomColor: '#ccc',
1481
+ },
1482
+ todoText: {
1483
+ flex: 1,
1484
+ fontSize: 16,
1485
+ },
1486
+ completed: {
1487
+ textDecorationLine: 'line-through',
1488
+ color: '#888',
1489
+ },
1490
+ });
1491
+ ```
1492
+
1493
+ ### Next.js API Routes
1494
+
1495
+ ```typescript
1496
+ // pages/api/users/[id].ts
1497
+ import { NextApiRequest, NextApiResponse } from 'next';
1498
+ import UniversalSQLite from '@dqcai/sqlite';
1499
+
1500
+ const db = new UniversalSQLite();
1501
+ let isConnected = false;
1502
+
1503
+ async function ensureConnection() {
1504
+ if (!isConnected) {
1505
+ await db.connect(process.env.DATABASE_PATH || './api_database.db');
1506
+
1507
+ await db.createTable('users', {
1508
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1509
+ name: 'TEXT NOT NULL',
1510
+ email: 'TEXT UNIQUE NOT NULL',
1511
+ avatar: 'TEXT',
1512
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
1513
+ updated_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1514
+ });
1515
+
1516
+ isConnected = true;
1517
+ }
1518
+ }
1519
+
1520
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
1521
+ const { id } = req.query;
1522
+
1523
+ try {
1524
+ await ensureConnection();
1525
+
1526
+ switch (req.method) {
1527
+ case 'GET':
1528
+ if (id === 'all') {
1529
+ const users = await db.select('users');
1530
+ res.status(200).json(users.rows);
1531
+ } else {
1532
+ const user = await db.select('users', 'id = ?', [Number(id)]);
1533
+ if (user.rows.length === 0) {
1534
+ res.status(404).json({ error: 'User not found' });
1535
+ } else {
1536
+ res.status(200).json(user.rows[0]);
1537
+ }
1538
+ }
1539
+ break;
1540
+
1541
+ case 'POST':
1542
+ const { name, email, avatar } = req.body;
1543
+ const newUser = await db.insert('users', { name, email, avatar });
1544
+ res.status(201).json({
1545
+ id: newUser.lastInsertRowId,
1546
+ message: 'User created successfully'
1547
+ });
1548
+ break;
1549
+
1550
+ case 'PUT':
1551
+ const updateData = { ...req.body, updated_at: new Date().toISOString() };
1552
+ const updateResult = await db.update('users', updateData, 'id = ?', [Number(id)]);
1553
+
1554
+ if (updateResult.rowsAffected === 0) {
1555
+ res.status(404).json({ error: 'User not found' });
1556
+ } else {
1557
+ res.status(200).json({ message: 'User updated successfully' });
1558
+ }
1559
+ break;
1560
+
1561
+ case 'DELETE':
1562
+ const deleteResult = await db.delete('users', 'id = ?', [Number(id)]);
1563
+
1564
+ if (deleteResult.rowsAffected === 0) {
1565
+ res.status(404).json({ error: 'User not found' });
1566
+ } else {
1567
+ res.status(200).json({ message: 'User deleted successfully' });
1568
+ }
1569
+ break;
1570
+
1571
+ default:
1572
+ res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']);
1573
+ res.status(405).end(`Method ${req.method} Not Allowed`);
1574
+ }
1575
+
1576
+ } catch (error) {
1577
+ console.error('API Error:', error);
1578
+ res.status(500).json({ error: 'Internal server error' });
1579
+ }
1580
+ }
1581
+ ```
1582
+
1583
+ ## 🧪 Testing
1584
+
1585
+ ### Unit Tests với Jest
1586
+
1587
+ ```typescript
1588
+ // __tests__/universal-sqlite.test.ts
1589
+ import UniversalSQLite from '../src/index';
1590
+
1591
+ describe('UniversalSQLite', () => {
1592
+ let db: UniversalSQLite;
1593
+
1594
+ beforeEach(async () => {
1595
+ db = new UniversalSQLite();
1596
+ await db.connect(':memory:');
1597
+ });
1598
+
1599
+ afterEach(async () => {
1600
+ await db.close();
1601
+ });
1602
+
1603
+ test('should create and query table', async () => {
1604
+ await db.createTable('test_table', {
1605
+ id: 'INTEGER PRIMARY KEY',
1606
+ name: 'TEXT'
1607
+ });
1608
+
1609
+ await db.insert('test_table', { name: 'Test Item' });
1610
+
1611
+ const result = await db.select('test_table');
1612
+ expect(result.rows).toHaveLength(1);
1613
+ expect(result.rows[0].name).toBe('Test Item');
1614
+ });
1615
+
1616
+ test('should handle parameters correctly', async () => {
1617
+ await db.createTable('users', {
1618
+ id: 'INTEGER PRIMARY KEY',
1619
+ email: 'TEXT UNIQUE'
1620
+ });
1621
+
1622
+ await db.insert('users', { email: 'test@example.com' });
1623
+
1624
+ const user = await db.select('users', 'email = ?', ['test@example.com']);
1625
+ expect(user.rows).toHaveLength(1);
1626
+
1627
+ const noUser = await db.select('users', 'email = ?', ['nonexistent@example.com']);
1628
+ expect(noUser.rows).toHaveLength(0);
1629
+ });
1630
+
1631
+ test('should handle SQL injection attempts', async () => {
1632
+ await db.createTable('users', {
1633
+ id: 'INTEGER PRIMARY KEY',
1634
+ name: 'TEXT'
1635
+ });
1636
+
1637
+ await db.insert('users', { name: 'Regular User' });
1638
+
1639
+ // Attempt SQL injection
1640
+ const maliciousInput = "'; DROP TABLE users; --";
1641
+ await db.insert('users', { name: maliciousInput });
1642
+
1643
+ const users = await db.select('users');
1644
+ expect(users.rows).toHaveLength(2); // Table should still exist
1645
+ });
1646
+ });
1647
+ ```
1648
+
1649
+ ## 🔐 Best Practices
1650
+
1651
+ ### 1. Security
1652
+
1653
+ ```typescript
1654
+ class SecureDatabase {
1655
+ private db: UniversalSQLite;
1656
+
1657
+ constructor() {
1658
+ this.db = new UniversalSQLite();
1659
+ }
1660
+
1661
+ async connect(path: string) {
1662
+ await this.db.connect(path);
1663
+
1664
+ // Enable foreign key constraints
1665
+ await this.db.query('PRAGMA foreign_keys = ON');
1666
+
1667
+ // Set secure defaults
1668
+ await this.db.query('PRAGMA journal_mode = WAL'); // Write-Ahead Logging
1669
+ await this.db.query('PRAGMA synchronous = NORMAL');
1670
+ }
1671
+
1672
+ // Validate input before queries
1673
+ validateEmail(email: string): boolean {
1674
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1675
+ return emailRegex.test(email);
1676
+ }
1677
+
1678
+ validateUsername(username: string): boolean {
1679
+ return /^[a-zA-Z0-9_]{3,20}$/.test(username);
1680
+ }
1681
+
1682
+ async createUser(userData: {name: string, email: string, username: string}) {
1683
+ // Validate input
1684
+ if (!this.validateEmail(userData.email)) {
1685
+ throw new Error('Invalid email format');
1686
+ }
1687
+
1688
+ if (!this.validateUsername(userData.username)) {
1689
+ throw new Error('Invalid username format');
1690
+ }
1691
+
1692
+ // Sanitize input
1693
+ const sanitizedData = {
1694
+ name: userData.name.trim(),
1695
+ email: userData.email.toLowerCase().trim(),
1696
+ username: userData.username.trim()
1697
+ };
1698
+
1699
+ return await this.db.insert('users', sanitizedData);
1700
+ }
1701
+ }
1702
+ ```
1703
+
1704
+ ### 2. Performance Optimization
1705
+
1706
+ ```typescript
1707
+ class OptimizedDatabase {
1708
+ private db: UniversalSQLite;
1709
+
1710
+ constructor() {
1711
+ this.db = new UniversalSQLite();
1712
+ }
1713
+
1714
+ async connect(path: string) {
1715
+ await this.db.connect(path);
1716
+
1717
+ // Performance tuning
1718
+ await this.db.query('PRAGMA cache_size = 10000');
1719
+ await this.db.query('PRAGMA temp_store = memory');
1720
+ await this.db.query('PRAGMA mmap_size = 268435456'); // 256MB
1721
+ await this.db.query('PRAGMA optimize');
1722
+ }
1723
+
1724
+ async bulkInsert(table: string, records: Record<string, any>[]) {
1725
+ await this.db.query('BEGIN TRANSACTION');
1726
+
1727
+ try {
1728
+ for (const record of records) {
1729
+ await this.db.insert(table, record);
1730
+ }
1731
+ await this.db.query('COMMIT');
1732
+ } catch (error) {
1733
+ await this.db.query('ROLLBACK');
1734
+ throw error;
1735
+ }
1736
+ }
1737
+
1738
+ async createOptimizedIndexes(table: string, columns: string[]) {
1739
+ for (const column of columns) {
1740
+ await this.db.query(`
1741
+ CREATE INDEX IF NOT EXISTS idx_${table}_${column}
1742
+ ON ${table}(${column})
1743
+ `);
1744
+ }
1745
+ }
1746
+ }
1747
+ ```
1748
+
1749
+ ## 🚨 Troubleshooting
1750
+
1751
+ ### Common Issues
1752
+
1753
+ #### 1. "No supported SQLite adapter found"
1754
+ ```typescript
1755
+ // Kiểm tra adapter availability
1756
+ const manager = new SQLiteManager();
1757
+ const adapters = [
1758
+ 'NodeAdapter',
1759
+ 'BrowserAdapter',
1760
+ 'DenoAdapter',
1761
+ 'BunAdapter',
1762
+ 'ReactNativeAdapter'
1763
+ ];
1764
+
1765
+ for (const adapterName of adapters) {
1766
+ try {
1767
+ const adapter = new (require(`./adapters/${adapterName.toLowerCase()}`))();
1768
+ console.log(`${adapterName}: ${adapter.isSupported() ? '✅' : '❌'}`);
1769
+ } catch {
1770
+ console.log(`${adapterName}: ❌ (not available)`);
1771
+ }
1772
+ }
1773
+ ```
1774
+
1775
+ #### 2. React Native Windows Setup
1776
+
1777
+ ```typescript
1778
+ // Kiểm tra Windows environment
1779
+ async function checkReactNativeWindows() {
1780
+ const db = new UniversalSQLite();
1781
+
1782
+ console.log('Environment:', db.getEnvironment());
1783
+
1784
+ if (db.getEnvironment() === 'React Native Windows') {
1785
+ console.log('✅ React Native Windows detected');
1786
+
1787
+ try {
1788
+ await db.connect('windows_test.db');
1789
+ console.log('✅ Database connection successful');
1790
+
1791
+ await db.createTable('test', { id: 'INTEGER', name: 'TEXT' });
1792
+ console.log('✅ Table creation successful');
1793
+
1794
+ } catch (error) {
1795
+ console.error('❌ Windows SQLite error:', error);
1796
+ console.log('💡 Try installing: npm install react-native-sqlite-2');
1797
+ }
1798
+ }
1799
+ }
1800
+ ```
1801
+
1802
+ #### 3. Browser CORS Issues
1803
+
1804
+ ```typescript
1805
+ // browser-cors-fix.js
1806
+ async function setupBrowserDatabase() {
1807
+ const db = new UniversalSQLite();
1808
+
1809
+ try {
1810
+ // In-memory database (không cần file access)
1811
+ await db.connect(':memory:');
1812
+ console.log('✅ Browser in-memory database ready');
1813
+
1814
+ } catch (error) {
1815
+ console.error('❌ Browser setup failed:', error);
1816
+ console.log('💡 Fallback: Using in-memory database');
1817
+
1818
+ // Fallback strategy
1819
+ await db.connect(':memory:');
1820
+ }
1821
+ }
1822
+ ```
1823
+
1824
+ ## 📊 Performance Benchmarks
1825
+
1826
+ ```typescript
1827
+ async function benchmarkExample() {
1828
+ const db = new UniversalSQLite();
1829
+ await db.connect('benchmark.db');
1830
+
1831
+ await db.createTable('benchmark_table', {
1832
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1833
+ data: 'TEXT',
1834
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1835
+ });
1836
+
1837
+ // Benchmark bulk insert
1838
+ console.log('Starting bulk insert benchmark...');
1839
+ const insertStart = performance.now();
1840
+
1841
+ await db.query('BEGIN TRANSACTION');
1842
+ for (let i = 0; i < 10000; i++) {
1843
+ await db.insert('benchmark_table', {
1844
+ data: `Sample data ${i}`
1845
+ });
1846
+ }
1847
+ await db.query('COMMIT');
1848
+
1849
+ const insertEnd = performance.now();
1850
+ console.log(`Bulk insert (10k records): ${(insertEnd - insertStart).toFixed(2)}ms`);
1851
+
1852
+ // Benchmark select
1853
+ const selectStart = performance.now();
1854
+ const allRecords = await db.select('benchmark_table');
1855
+ const selectEnd = performance.now();
1856
+
1857
+ console.log(`Select all (${allRecords.rows.length} records): ${(selectEnd - selectStart).toFixed(2)}ms`);
1858
+
1859
+ // Benchmark with index
1860
+ await db.query('CREATE INDEX idx_data ON benchmark_table(data)');
1861
+
1862
+ const indexedSelectStart = performance.now();
1863
+ const searchResult = await db.select('benchmark_table', 'data LIKE ?', ['Sample data 50%']);
1864
+ const indexedSelectEnd = performance.now();
1865
+
1866
+ console.log(`Indexed search (${searchResult.rows.length} results): ${(indexedSelectEnd - indexedSelectStart).toFixed(2)}ms`);
1867
+
1868
+ await db.close();
1869
+ }
1870
+
1871
+ benchmarkExample();
1872
+ ```
1873
+
1874
+ ## 📚 Advanced Use Cases
1875
+
1876
+ ### 1. Multi-Database Application
1877
+
1878
+ ```typescript
1879
+ class MultiDatabaseApp {
1880
+ private userDb: UniversalSQLite;
1881
+ private logDb: UniversalSQLite;
1882
+ private cacheDb: UniversalSQLite;
1883
+
1884
+ constructor() {
1885
+ this.userDb = new UniversalSQLite();
1886
+ this.logDb = new UniversalSQLite();
1887
+ this.cacheDb = new UniversalSQLite();
1888
+ }
1889
+
1890
+ async initialize() {
1891
+ // Separate databases for different concerns
1892
+ await this.userDb.connect('users.db');
1893
+ await this.logDb.connect('logs.db');
1894
+ await this.cacheDb.connect(':memory:'); // Cache in memory
1895
+
1896
+ // Setup schemas
1897
+ await this.setupUserDatabase();
1898
+ await this.setupLogDatabase();
1899
+ await this.setupCacheDatabase();
1900
+ }
1901
+
1902
+ private async setupUserDatabase() {
1903
+ await this.userDb.createTable('users', {
1904
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1905
+ username: 'TEXT UNIQUE NOT NULL',
1906
+ email: 'TEXT UNIQUE NOT NULL',
1907
+ profile_data: 'TEXT', // JSON
1908
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1909
+ });
1910
+
1911
+ await this.userDb.createTable('user_sessions', {
1912
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1913
+ user_id: 'INTEGER NOT NULL',
1914
+ session_token: 'TEXT UNIQUE NOT NULL',
1915
+ expires_at: 'DATETIME NOT NULL',
1916
+ 'FOREIGN KEY (user_id)': 'REFERENCES users(id) ON DELETE CASCADE'
1917
+ });
1918
+ }
1919
+
1920
+ private async setupLogDatabase() {
1921
+ await this.logDb.createTable('access_logs', {
1922
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1923
+ user_id: 'INTEGER',
1924
+ action: 'TEXT NOT NULL',
1925
+ ip_address: 'TEXT',
1926
+ user_agent: 'TEXT',
1927
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1928
+ });
1929
+
1930
+ await this.logDb.createTable('error_logs', {
1931
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
1932
+ error_type: 'TEXT NOT NULL',
1933
+ error_message: 'TEXT NOT NULL',
1934
+ stack_trace: 'TEXT',
1935
+ context: 'TEXT', // JSON
1936
+ timestamp: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
1937
+ });
1938
+ }
1939
+
1940
+ private async setupCacheDatabase() {
1941
+ await this.cacheDb.createTable('cache_entries', {
1942
+ key: 'TEXT PRIMARY KEY',
1943
+ value: 'TEXT NOT NULL',
1944
+ expires_at: 'DATETIME NOT NULL'
1945
+ });
1946
+ }
1947
+
1948
+ // User operations
1949
+ async createUser(userData: any) {
1950
+ const result = await this.userDb.insert('users', userData);
1951
+
1952
+ // Log the action
1953
+ await this.logAction(result.lastInsertRowId!, 'USER_CREATED', 'User registration');
1954
+
1955
+ return result;
1956
+ }
1957
+
1958
+ async getUserById(id: number) {
1959
+ // Check cache first
1960
+ const cached = await this.getFromCache(`user:${id}`);
1961
+ if (cached) {
1962
+ return JSON.parse(cached);
1963
+ }
1964
+
1965
+ // Query from database
1966
+ const result = await this.userDb.select('users', 'id = ?', [id]);
1967
+ if (result.rows.length > 0) {
1968
+ const user = result.rows[0];
1969
+ // Cache for 1 hour
1970
+ await this.setCache(`user:${id}`, JSON.stringify(user), 3600);
1971
+ return user;
1972
+ }
1973
+
1974
+ return null;
1975
+ }
1976
+
1977
+ // Logging operations
1978
+ async logAction(userId: number, action: string, details: string) {
1979
+ await this.logDb.insert('access_logs', {
1980
+ user_id: userId,
1981
+ action: action,
1982
+ ip_address: '127.0.0.1', // Would get from request
1983
+ user_agent: 'Universal SQLite App'
1984
+ });
1985
+ }
1986
+
1987
+ async logError(error: Error, context?: any) {
1988
+ await this.logDb.insert('error_logs', {
1989
+ error_type: error.name,
1990
+ error_message: error.message,
1991
+ stack_trace: error.stack,
1992
+ context: context ? JSON.stringify(context) : null
1993
+ });
1994
+ }
1995
+
1996
+ // Cache operations
1997
+ async setCache(key: string, value: string, ttlSeconds: number) {
1998
+ const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
1999
+
2000
+ await this.cacheDb.query(`
2001
+ INSERT OR REPLACE INTO cache_entries (key, value, expires_at)
2002
+ VALUES (?, ?, ?)
2003
+ `, [key, value, expiresAt]);
2004
+ }
2005
+
2006
+ async getFromCache(key: string): Promise<string | null> {
2007
+ // Clean expired entries
2008
+ await this.cacheDb.delete('cache_entries', 'expires_at < ?', [new Date().toISOString()]);
2009
+
2010
+ const result = await this.cacheDb.select('cache_entries', 'key = ?', [key]);
2011
+ return result.rows.length > 0 ? result.rows[0].value : null;
2012
+ }
2013
+
2014
+ // Analytics across databases
2015
+ async getUserAnalytics(userId: number) {
2016
+ const user = await this.getUserById(userId);
2017
+ if (!user) return null;
2018
+
2019
+ const logs = await this.logDb.query(`
2020
+ SELECT
2021
+ action,
2022
+ COUNT(*) as count,
2023
+ MAX(timestamp) as last_action
2024
+ FROM access_logs
2025
+ WHERE user_id = ?
2026
+ GROUP BY action
2027
+ ORDER BY count DESC
2028
+ `, [userId]);
2029
+
2030
+ return {
2031
+ user: user,
2032
+ activity: logs.rows,
2033
+ total_actions: logs.rows.reduce((sum, row) => sum + row.count, 0)
2034
+ };
2035
+ }
2036
+
2037
+ async cleanup() {
2038
+ await this.userDb.close();
2039
+ await this.logDb.close();
2040
+ await this.cacheDb.close();
2041
+ }
2042
+ }
2043
+
2044
+ // Usage
2045
+ async function multiDbExample() {
2046
+ const app = new MultiDatabaseApp();
2047
+
2048
+ try {
2049
+ await app.initialize();
2050
+
2051
+ // Create user
2052
+ const newUser = await app.createUser({
2053
+ username: 'johndoe',
2054
+ email: 'john@example.com',
2055
+ profile_data: JSON.stringify({ bio: 'Software developer' })
2056
+ });
2057
+
2058
+ const userId = newUser.lastInsertRowId!;
2059
+
2060
+ // Simulate user activity
2061
+ await app.logAction(userId, 'LOGIN', 'User logged in');
2062
+ await app.logAction(userId, 'VIEW_PROFILE', 'Viewed profile page');
2063
+ await app.logAction(userId, 'UPDATE_PROFILE', 'Updated profile');
2064
+
2065
+ // Get analytics
2066
+ const analytics = await app.getUserAnalytics(userId);
2067
+ console.log('User Analytics:', analytics);
2068
+
2069
+ } catch (error) {
2070
+ console.error('Multi-db error:', error);
2071
+ } finally {
2072
+ await app.cleanup();
2073
+ }
2074
+ }
2075
+
2076
+ multiDbExample();
2077
+ ```
2078
+
2079
+ ### 2. Real-time Sync với WebSocket
2080
+
2081
+ ```typescript
2082
+ // realtime-sync.ts
2083
+ import UniversalSQLite, { QueryBuilder } from '@dqcai/sqlite';
2084
+ import { WebSocket } from 'ws'; // Node.js example
2085
+
2086
+ class RealtimeSync {
2087
+ private db: UniversalSQLite;
2088
+ private ws: WebSocket | null = null;
2089
+ private syncQueue: any[] = [];
2090
+
2091
+ constructor() {
2092
+ this.db = new UniversalSQLite();
2093
+ }
2094
+
2095
+ async initialize() {
2096
+ await this.db.connect('realtime.db');
2097
+
2098
+ // Setup sync tables
2099
+ await this.db.createTable('messages', {
2100
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2101
+ content: 'TEXT NOT NULL',
2102
+ sender_id: 'INTEGER NOT NULL',
2103
+ room_id: 'INTEGER NOT NULL',
2104
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2105
+ synced: 'BOOLEAN DEFAULT 0'
2106
+ });
2107
+
2108
+ await this.db.createTable('sync_log', {
2109
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2110
+ operation: 'TEXT NOT NULL',
2111
+ table_name: 'TEXT NOT NULL',
2112
+ record_id: 'INTEGER',
2113
+ data: 'TEXT',
2114
+ sync_status: 'TEXT DEFAULT "pending"',
2115
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP'
2116
+ });
2117
+
2118
+ this.startSyncProcess();
2119
+ }
2120
+
2121
+ async addMessage(content: string, senderId: number, roomId: number) {
2122
+ const result = await this.db.insert('messages', {
2123
+ content,
2124
+ sender_id: senderId,
2125
+ room_id: roomId
2126
+ });
2127
+
2128
+ // Queue for sync
2129
+ await this.queueSync('INSERT', 'messages', result.lastInsertRowId!, {
2130
+ id: result.lastInsertRowId,
2131
+ content,
2132
+ sender_id: senderId,
2133
+ room_id: roomId
2134
+ });
2135
+
2136
+ return result;
2137
+ }
2138
+
2139
+ async getMessages(roomId: number, limit: number = 50) {
2140
+ return await this.db.query(`
2141
+ SELECT m.*, u.username
2142
+ FROM messages m
2143
+ LEFT JOIN users u ON m.sender_id = u.id
2144
+ WHERE m.room_id = ?
2145
+ ORDER BY m.created_at DESC
2146
+ LIMIT ?
2147
+ `, [roomId, limit]);
2148
+ }
2149
+
2150
+ private async queueSync(operation: string, tableName: string, recordId: number, data: any) {
2151
+ await this.db.insert('sync_log', {
2152
+ operation,
2153
+ table_name: tableName,
2154
+ record_id: recordId,
2155
+ data: JSON.stringify(data)
2156
+ });
2157
+ }
2158
+
2159
+ private async startSyncProcess() {
2160
+ setInterval(async () => {
2161
+ await this.processSyncQueue();
2162
+ }, 5000); // Sync every 5 seconds
2163
+ }
2164
+
2165
+ private async processSyncQueue() {
2166
+ const pendingSyncs = await this.db.select('sync_log', 'sync_status = ?', ['pending']);
2167
+
2168
+ for (const sync of pendingSyncs.rows) {
2169
+ try {
2170
+ // Simulate API call
2171
+ const response = await this.syncToServer(sync);
2172
+
2173
+ if (response.success) {
2174
+ await this.db.update('sync_log',
2175
+ { sync_status: 'completed' },
2176
+ 'id = ?',
2177
+ [sync.id]
2178
+ );
2179
+
2180
+ // Mark original record as synced
2181
+ if (sync.table_name === 'messages') {
2182
+ await this.db.update('messages',
2183
+ { synced: 1 },
2184
+ 'id = ?',
2185
+ [sync.record_id]
2186
+ );
2187
+ }
2188
+ }
2189
+ } catch (error) {
2190
+ await this.db.update('sync_log',
2191
+ { sync_status: 'failed' },
2192
+ 'id = ?',
2193
+ [sync.id]
2194
+ );
2195
+ console.error('Sync failed:', error);
2196
+ }
2197
+ }
2198
+ }
2199
+
2200
+ private async syncToServer(syncData: any): Promise<{success: boolean}> {
2201
+ // Simulate server sync
2202
+ return new Promise((resolve) => {
2203
+ setTimeout(() => {
2204
+ resolve({ success: Math.random() > 0.1 }); // 90% success rate
2205
+ }, 100);
2206
+ });
2207
+ }
2208
+ }
2209
+
2210
+ // Usage
2211
+ async function realtimeSyncExample() {
2212
+ const sync = new RealtimeSync();
2213
+ await sync.initialize();
2214
+
2215
+ // Simulate adding messages
2216
+ await sync.addMessage('Hello World!', 1, 1);
2217
+ await sync.addMessage('How are you?', 2, 1);
2218
+
2219
+ // Get messages
2220
+ const messages = await sync.getMessages(1);
2221
+ console.log('Room messages:', messages.rows);
2222
+
2223
+ // Let sync process run
2224
+ await new Promise(resolve => setTimeout(resolve, 6000));
2225
+ }
2226
+
2227
+ realtimeSyncExample();
2228
+ ```
2229
+
2230
+ ### 3. Database Backup và Restore
2231
+
2232
+ ```typescript
2233
+ class DatabaseBackup {
2234
+ private db: UniversalSQLite;
2235
+
2236
+ constructor() {
2237
+ this.db = new UniversalSQLite();
2238
+ }
2239
+
2240
+ async connect(path: string) {
2241
+ await this.db.connect(path);
2242
+ }
2243
+
2244
+ async createBackup(): Promise<string> {
2245
+ // Get all table schemas
2246
+ const tables = await this.db.query(`
2247
+ SELECT name, sql FROM sqlite_master
2248
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
2249
+ `);
2250
+
2251
+ let backupSQL = '';
2252
+ backupSQL += '-- Database Backup\n';
2253
+ backupSQL += `-- Generated: ${new Date().toISOString()}\n\n`;
2254
+
2255
+ // Add schema
2256
+ for (const table of tables.rows) {
2257
+ backupSQL += `${table.sql};\n\n`;
2258
+ }
2259
+
2260
+ // Add data
2261
+ for (const table of tables.rows) {
2262
+ const data = await this.db.select(table.name);
2263
+
2264
+ if (data.rows.length > 0) {
2265
+ // Get column names
2266
+ const columns = Object.keys(data.rows[0]);
2267
+
2268
+ backupSQL += `-- Data for ${table.name}\n`;
2269
+ for (const row of data.rows) {
2270
+ const values = columns.map(col => {
2271
+ const value = row[col];
2272
+ if (value === null) return 'NULL';
2273
+ if (typeof value === 'string') return `'${value.replace(/'/g, "''")}'`;
2274
+ return String(value);
2275
+ });
2276
+
2277
+ backupSQL += `INSERT INTO ${table.name} (${columns.join(', ')}) VALUES (${values.join(', ')});\n`;
2278
+ }
2279
+ backupSQL += '\n';
2280
+ }
2281
+ }
2282
+
2283
+ return backupSQL;
2284
+ }
2285
+
2286
+ async restoreFromBackup(backupSQL: string): Promise<void> {
2287
+ const statements = backupSQL
2288
+ .split(';')
2289
+ .map(stmt => stmt.trim())
2290
+ .filter(stmt => stmt && !stmt.startsWith('--'));
2291
+
2292
+ await this.db.query('BEGIN TRANSACTION');
2293
+
2294
+ try {
2295
+ for (const statement of statements) {
2296
+ if (statement) {
2297
+ await this.db.query(statement);
2298
+ }
2299
+ }
2300
+ await this.db.query('COMMIT');
2301
+ console.log('Backup restored successfully');
2302
+ } catch (error) {
2303
+ await this.db.query('ROLLBACK');
2304
+ throw new Error(`Restore failed: ${error}`);
2305
+ }
2306
+ }
2307
+
2308
+ async scheduleBackups(intervalMinutes: number = 60) {
2309
+ setInterval(async () => {
2310
+ try {
2311
+ const backup = await this.createBackup();
2312
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
2313
+ const filename = `backup_${timestamp}.sql`;
2314
+
2315
+ // In Node.js environment, save to file
2316
+ if (typeof require !== 'undefined' && this.db.getEnvironment() === 'Node.js') {
2317
+ const fs = require('fs').promises;
2318
+ await fs.writeFile(filename, backup);
2319
+ console.log(`Backup saved: ${filename}`);
2320
+ } else {
2321
+ // In browser/other environments, log or send to server
2322
+ console.log('Backup created (implement save logic for your environment)');
2323
+ }
2324
+
2325
+ } catch (error) {
2326
+ console.error('Backup failed:', error);
2327
+ }
2328
+ }, intervalMinutes * 60 * 1000);
2329
+ }
2330
+
2331
+ async close() {
2332
+ await this.db.close();
2333
+ }
2334
+ }
2335
+
2336
+ // Usage
2337
+ async function backupExample() {
2338
+ const backup = new DatabaseBackup();
2339
+ await backup.connect('main_app.db');
2340
+
2341
+ // Create some test data
2342
+ const db = new UniversalSQLite();
2343
+ await db.connect('main_app.db');
2344
+
2345
+ await db.createTable('posts', {
2346
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2347
+ title: 'TEXT NOT NULL',
2348
+ content: 'TEXT',
2349
+ author_id: 'INTEGER'
2350
+ });
2351
+
2352
+ await db.insert('posts', { title: 'First Post', content: 'Hello World!', author_id: 1 });
2353
+ await db.insert('posts', { title: 'Second Post', content: 'More content', author_id: 2 });
2354
+
2355
+ // Create backup
2356
+ const backupSQL = await backup.createBackup();
2357
+ console.log('Backup created, size:', backupSQL.length, 'characters');
2358
+
2359
+ // Test restore (to new database)
2360
+ const restoreDb = new UniversalSQLite();
2361
+ await restoreDb.connect(':memory:');
2362
+
2363
+ const restoreBackup = new DatabaseBackup();
2364
+ await restoreBackup.connect(':memory:');
2365
+ await restoreBackup.restoreFromBackup(backupSQL);
2366
+
2367
+ // Verify restore
2368
+ const restoredData = await restoreDb.select('posts');
2369
+ console.log('Restored data:', restoredData.rows);
2370
+
2371
+ await db.close();
2372
+ await restoreDb.close();
2373
+ await backup.close();
2374
+ await restoreBackup.close();
2375
+ }
2376
+
2377
+ backupExample();
2378
+ ```
2379
+
2380
+ ## 🌐 Production Deployment
2381
+
2382
+ ### Environment Variables
2383
+
2384
+ ```typescript
2385
+ // config.ts
2386
+ interface DatabaseConfig {
2387
+ path: string;
2388
+ backupInterval?: number;
2389
+ maxConnections?: number;
2390
+ enableWAL?: boolean;
2391
+ }
2392
+
2393
+ function getDatabaseConfig(): DatabaseConfig {
2394
+ const env = process.env.NODE_ENV || 'development';
2395
+
2396
+ const configs: Record<string, DatabaseConfig> = {
2397
+ development: {
2398
+ path: './dev_database.db',
2399
+ backupInterval: 60, // 1 hour
2400
+ enableWAL: true
2401
+ },
2402
+ test: {
2403
+ path: ':memory:',
2404
+ enableWAL: false
2405
+ },
2406
+ production: {
2407
+ path: process.env.DATABASE_PATH || './prod_database.db',
2408
+ backupInterval: 15, // 15 minutes
2409
+ maxConnections: 10,
2410
+ enableWAL: true
2411
+ }
2412
+ };
2413
+
2414
+ return configs[env] || configs.development;
2415
+ }
2416
+
2417
+ // app.ts
2418
+ async function productionSetup() {
2419
+ const config = getDatabaseConfig();
2420
+ const db = new UniversalSQLite();
2421
+
2422
+ await db.connect(config.path);
2423
+
2424
+ if (config.enableWAL) {
2425
+ await db.query('PRAGMA journal_mode = WAL');
2426
+ await db.query('PRAGMA synchronous = NORMAL');
2427
+ await db.query('PRAGMA cache_size = 10000');
2428
+ await db.query('PRAGMA temp_store = memory');
2429
+ }
2430
+
2431
+ // Setup production tables
2432
+ await setupProductionSchema(db);
2433
+
2434
+ // Setup monitoring
2435
+ if (process.env.NODE_ENV === 'production') {
2436
+ setupProductionMonitoring(db);
2437
+ }
2438
+
2439
+ return db;
2440
+ }
2441
+
2442
+ async function setupProductionSchema(db: UniversalSQLite) {
2443
+ // Users table với full audit trail
2444
+ await db.createTable('users', {
2445
+ id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
2446
+ email: 'TEXT UNIQUE NOT NULL',
2447
+ password_hash: 'TEXT NOT NULL',
2448
+ status: 'TEXT DEFAULT "active" CHECK(status IN ("active", "inactive", "suspended"))',
2449
+ created_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2450
+ updated_at: 'DATETIME DEFAULT CURRENT_TIMESTAMP',
2451
+ last_login: 'DATETIME'
2452
+ });
2453
+
2454
+ // Indexes for performance
2455
+ await db.query('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)');
2456
+ await db.query('CREATE INDEX IF NOT EXISTS idx_users_status ON users(status)');
2457
+ await db.query('CREATE INDEX IF NOT EXISTS idx_users_last_login ON users(last_login)');
2458
+
2459
+ // Trigger for updated_at
2460
+ await db.query(`
2461
+ CREATE TRIGGER IF NOT EXISTS update_users_timestamp
2462
+ AFTER UPDATE ON users
2463
+ FOR EACH ROW
2464
+ BEGIN
2465
+ UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
2466
+ END
2467
+ `);
2468
+ }
2469
+
2470
+ function setupProductionMonitoring(db: UniversalSQLite) {
2471
+ // Monitor database size
2472
+ setInterval(async () => {
2473
+ const sizeInfo = await db.query('PRAGMA page_count; PRAGMA page_size;');
2474
+ const pages = sizeInfo.rows[0].page_count;
2475
+ const pageSize = sizeInfo.rows[1].page_size;
2476
+ const sizeBytes = pages * pageSize;
2477
+ const sizeMB = (sizeBytes / 1024 / 1024).toFixed(2);
2478
+
2479
+ console.log(`Database size: ${sizeMB} MB`);
2480
+
2481
+ if (sizeBytes > 500 * 1024 * 1024) { // 500MB warning
2482
+ console.warn('Database size exceeds 500MB, consider archiving old data');
2483
+ }
2484
+ }, 300000); // Check every 5 minutes
2485
+
2486
+ // Monitor query performance
2487
+ setInterval(async () => {
2488
+ const longQueries = await db.query(`
2489
+ SELECT * FROM pragma_stats
2490
+ WHERE name LIKE '%time%'
2491
+ `);
2492
+
2493
+ // Log slow queries for optimization
2494
+ console.log('Database stats:', longQueries.rows);
2495
+ }, 600000); // Check every 10 minutes
2496
+ }
2497
+
2498
+ productionSetup();
2499
+ ```
2500
+
2501
+ ## 📖 API Reference
2502
+
2503
+ ### Core Types
2504
+
2505
+ ```typescript
2506
+ interface SQLiteRow {
2507
+ [key: string]: any;
2508
+ }
2509
+
2510
+ interface SQLiteResult {
2511
+ rows: SQLiteRow[]; // Dữ liệu trả về
2512
+ rowsAffected: number; // Số dòng bị ảnh hưởng
2513
+ lastInsertRowId?: number; // ID của record mới insert
2514
+ }
2515
+
2516
+ interface SQLiteConnection {
2517
+ execute(sql: string, params?: any[]): Promise<SQLiteResult>;
2518
+ close(): Promise<void>;
2519
+ }
2520
+
2521
+ interface SQLiteConfig {
2522
+ path: string; // Đường dẫn database
2523
+ timeout?: number; // Timeout kết nối
2524
+ busyTimeout?: number; // Timeout khi database busy
2525
+ }
2526
+ ```
2527
+
2528
+ ### Error Codes
2529
+
2530
+ | Error | Description | Solution |
2531
+ |-------|-------------|----------|
2532
+ | `No supported SQLite adapter found` | Không tìm thấy adapter phù hợp | Cài đặt SQLite dependencies cho môi trường |
2533
+ | `Database connection not available` | Chưa kết nối database | Gọi `connect()` trước khi query |
2534
+ | `SQLite error: no such table` | Bảng không tồn tại | Tạo bảng bằng `createTable()` hoặc SQL |
2535
+ | `UNIQUE constraint failed` | Vi phạm ràng buộc unique | Kiểm tra dữ liệu trước khi insert |
2536
+ | `database is locked` | Database đang bị khóa | Retry sau một khoảng thời gian |
2537
+
2538
+ ## 🤝 Contributing
2539
+
2540
+ ### Development Setup
2541
+
2542
+ ```bash
2543
+ # Clone repository
2544
+ git clone https://github.com/dqcai/sqlite
2545
+ cd sqlite
2546
+
2547
+ # Install dependencies
2548
+ npm install
2549
+
2550
+ # Run tests
2551
+ npm test
2552
+
2553
+ # Build
2554
+ npm run build
2555
+
2556
+ # Test in different environments
2557
+ npm run test:node
2558
+ npm run test:browser
2559
+ npm run test:deno
2560
+ npm run test:bun
2561
+ ```
2562
+
2563
+ ### Testing New Adapters
2564
+
2565
+ ```typescript
2566
+ // tests/adapter-test.ts
2567
+ import { SQLiteAdapter } from '../src/types';
2568
+
2569
+ export async function testAdapter(adapter: SQLiteAdapter, testDbPath: string) {
2570
+ console.log(`Testing adapter: ${adapter.constructor.name}`);
2571
+
2572
+ // Test 1: Support detection
2573
+ console.log(`✓ isSupported(): ${adapter.isSupported()}`);
2574
+
2575
+ if (!adapter.isSupported()) {
2576
+ console.log('❌ Adapter not supported in current environment');
2577
+ return;
2578
+ }
2579
+
2580
+ try {
2581
+ // Test 2: Connection
2582
+ const connection = await adapter.connect(testDbPath);
2583
+ console.log('✓ Connection established');
2584
+
2585
+ // Test 3: Table creation
2586
+ await connection.execute(`
2587
+ CREATE TABLE IF NOT EXISTS test_table (
2588
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2589
+ name TEXT NOT NULL,
2590
+ value INTEGER
2591
+ )
2592
+ `);
2593
+ console.log('✓ Table created');
2594
+
2595
+ // Test 4: Insert
2596
+ const insertResult = await connection.execute(
2597
+ 'INSERT INTO test_table (name, value) VALUES (?, ?)',
2598
+ ['Test Item', 42]
2599
+ );
2600
+ console.log('✓ Insert successful, ID:', insertResult.lastInsertRowId);
2601
+
2602
+ // Test 5: Select
2603
+ const selectResult = await connection.execute('SELECT * FROM test_table');
2604
+ console.log('✓ Select successful, rows:', selectResult.rows.length);
2605
+
2606
+ // Test 6: Update
2607
+ const updateResult = await connection.execute(
2608
+ 'UPDATE test_table SET value = ? WHERE id = ?',
2609
+ [100, insertResult.lastInsertRowId]
2610
+ );
2611
+ console.log('✓ Update successful, affected:', updateResult.rowsAffected);
2612
+
2613
+ // Test 7: Delete
2614
+ const deleteResult = await connection.execute(
2615
+ 'DELETE FROM test_table WHERE id = ?',
2616
+ [insertResult.lastInsertRowId]
2617
+ );
2618
+ console.log('✓ Delete successful, affected:', deleteResult.rowsAffected);
2619
+
2620
+ // Test 8: Close
2621
+ await connection.close();
2622
+ console.log('✓ Connection closed');
2623
+
2624
+ console.log(`🎉 All tests passed for ${adapter.constructor.name}`);
2625
+
2626
+ } catch (error) {
2627
+ console.error(`❌ Test failed:`, error);
2628
+ }
2629
+ }
2630
+ ```
2631
+
2632
+ ## 📄 License
2633
+
2634
+ MIT License - see LICENSE file for details.
2635
+
2636
+ ## 🙏 Acknowledgments
2637
+
2638
+ - [sqlite3](https://www.npmjs.com/package/sqlite3) - Node.js SQLite bindings
2639
+ - [sql.js](https://github.com/sql-js/sql.js) - SQLite compiled to WebAssembly
2640
+ - [expo-sqlite](https://docs.expo.dev/versions/latest/sdk/sqlite/) - Expo SQLite support
2641
+ - [react-native-sqlite-storage](https://github.com/andpor/react-native-sqlite-storage) - React Native SQLite
2642
+ - [Deno SQLite](https://deno.land/x/sqlite) - Deno SQLite module
2643
+
2644
+ ## 🔗 Links
2645
+
2646
+ - [Documentation](https://github.com/cuongdqpayment/dqcai-sqlite/docs)
2647
+ - [Examples Repository](https://github.com/cuongdqpayment/dqcai-sqlite)
2648
+ - [Issue Tracker](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
2649
+ - [NPM Package](https://www.npmjs.com/package/@dqcai/sqlite)
2650
+
2651
+ ---
2652
+
2653
+ **Universal SQLite** - One library, all platforms! 🚀