@dqcai/sqlite 1.0.0 → 2.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.
Files changed (51) hide show
  1. package/README.md +434 -2522
  2. package/lib/adapters/base-adapter.d.ts.map +1 -1
  3. package/lib/adapters/index.d.ts +2 -0
  4. package/lib/adapters/index.d.ts.map +1 -0
  5. package/lib/core/base-service.d.ts +132 -0
  6. package/lib/core/base-service.d.ts.map +1 -0
  7. package/lib/core/database-factory.d.ts +98 -0
  8. package/lib/core/database-factory.d.ts.map +1 -0
  9. package/lib/core/database-manager.d.ts +208 -0
  10. package/lib/core/database-manager.d.ts.map +1 -0
  11. package/lib/core/index.d.ts +5 -0
  12. package/lib/core/index.d.ts.map +1 -0
  13. package/lib/core/universal-dao.d.ts +64 -0
  14. package/lib/core/universal-dao.d.ts.map +1 -0
  15. package/lib/index.d.ts +324 -14
  16. package/lib/index.d.ts.map +1 -1
  17. package/lib/index.js +1 -1
  18. package/lib/index.js.map +1 -1
  19. package/lib/index.mjs +45 -1
  20. package/lib/index.mjs.map +1 -1
  21. package/lib/index.umd.js +1 -1
  22. package/lib/index.umd.js.map +1 -1
  23. package/lib/query/query-builder.d.ts +120 -0
  24. package/lib/query/query-builder.d.ts.map +1 -0
  25. package/lib/types.d.ts +126 -4
  26. package/lib/types.d.ts.map +1 -1
  27. package/lib/utils/csv-import.d.ts +102 -0
  28. package/lib/utils/csv-import.d.ts.map +1 -0
  29. package/lib/utils/index.d.ts +3 -0
  30. package/lib/utils/index.d.ts.map +1 -0
  31. package/lib/utils/migration-manager.d.ts +184 -0
  32. package/lib/utils/migration-manager.d.ts.map +1 -0
  33. package/package.json +83 -63
  34. package/README-all-source.md +0 -1248
  35. package/README-ps-gemini.md +0 -1180
  36. package/lib/adapters/browser-adapter.d.ts +0 -17
  37. package/lib/adapters/browser-adapter.d.ts.map +0 -1
  38. package/lib/adapters/bun-adapter.d.ts +0 -7
  39. package/lib/adapters/bun-adapter.d.ts.map +0 -1
  40. package/lib/adapters/deno-adapter.d.ts +0 -7
  41. package/lib/adapters/deno-adapter.d.ts.map +0 -1
  42. package/lib/adapters/node-adapter.d.ts +0 -7
  43. package/lib/adapters/node-adapter.d.ts.map +0 -1
  44. package/lib/adapters/react-native-adapter.d.ts +0 -20
  45. package/lib/adapters/react-native-adapter.d.ts.map +0 -1
  46. package/lib/query-builder.d.ts +0 -19
  47. package/lib/query-builder.d.ts.map +0 -1
  48. package/lib/sqlite-manager.d.ts +0 -11
  49. package/lib/sqlite-manager.d.ts.map +0 -1
  50. package/scripts/obfuscate.mjs +0 -155
  51. package/scripts/version-manager.js +0 -317
package/README.md CHANGED
@@ -1,2637 +1,548 @@
1
- # @dqcai/sqlite - Universal SQLite Library
1
+ # @dqcai/sqlite - A Universal SQLite Library (@dqcai/sqlite v2.0.0)
2
2
 
3
3
  ![Universal SQLite](https://img.shields.io/badge/SQLite-Universal-blue)
4
4
  ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)
5
5
  ![Cross Platform](https://img.shields.io/badge/Platform-Universal-green)
6
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.
7
+ UniversalSQLite là một thư viện SQLite toàn diện, hỗ trợ đa nền tảng, được thiết kế để hoạt động mượt mà trên các môi trường như Browser, Node.js, Deno, Bun và React Native. Thư viện cung cấp giao diện thống nhất để quản lý cơ sở dữ liệu SQLite, bao gồm tạo schema, CRUD, query nâng cao, migration, import/export dữ liệu, và quản lý transaction. Nó sử dụng mô hình DAO (Data Access Object) để tách biệt logic truy cập dữ liệu, hỗ trợ role-based access control, và tích hợp dễ dàng với các framework.
8
8
 
9
- ## 🌟 Tính năng nổi bật
9
+ ## Features
10
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
11
+ - **Cross-Platform Support**: Hoạt động trên Browser, Node.js, Deno, Bun, React Native (iOS/Android/Windows).
12
+ - **Schema-Based Management**: Tạo quản database từ JSON schema.
13
+ - **DAO Pattern**: UniversalDAO để thực hiện CRUD, query, transaction.
14
+ - **Query Builder**: Xây dựng query phức tạp với join, where, group by, having, union, CTE.
15
+ - **Migration System**: Quản migration với up/down scripts.
16
+ - **Data Import/Export**: Hỗ trợ import từ CSV/JSON với mapping, validation, và export to CSV.
17
+ - **Role-Based Access**: Quản lý kết nối dựa trên role người dùng.
18
+ - **Transaction Management**: Hỗ trợ transaction đơn và cross-schema.
19
+ - **Adapters**: Tự động detect môi trường, hỗ trợ register adapter tùy chỉnh.
20
+ - **Type-Safe**: Đầy đủ types TypeScript cho schema, query, và operations.
21
+ - **Utilities**: CSVImporter, MigrationManager, BaseService cho service layer.
17
22
 
18
- ## 🚀 Cài đặt
23
+ ## Installation
19
24
 
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
25
+ Cài đặt qua npm hoặc yarn:
36
26
 
37
- #### Bun
38
- Sử dụng built-in SQLite của Bun
39
-
40
- #### React Native
41
27
  ```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
28
+ npm install @dqcai/sqlite@2.0.0
49
29
  # 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);
30
+ yarn add @dqcai/sqlite@2.0.0
169
31
  ```
170
32
 
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
- ```
33
+ Đối với React Native, đảm bảo cài đặt các dependencies cần thiết cho adapter (nếu sử dụng adapter cụ thể như react-native-sqlite-storage).
182
34
 
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
- );
35
+ ## Quick Start
190
36
 
191
- console.log('Updated rows:', result.rowsAffected);
192
- ```
37
+ ### Bước 1: Định nghĩa Schema
38
+ Schema là một object JSON mô tả cấu trúc database.
193
39
 
194
- ##### `delete(table: string, where: string, params?: any[]): Promise<SQLiteResult>`
195
40
  ```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
41
+ import { DatabaseSchema } from '@dqcai/sqlite';
201
42
 
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
- }
43
+ const exampleSchema: DatabaseSchema = {
44
+ version: '1.0.0',
45
+ database_name: 'example_db',
46
+ description: 'Example database',
47
+ schemas: {
48
+ users: {
49
+ description: 'Users table',
50
+ cols: [
51
+ { name: 'id', type: 'integer', primary_key: true, auto_increment: true },
52
+ { name: 'name', type: 'string', nullable: false },
53
+ { name: 'email', type: 'string', unique: true },
54
+ ],
55
+ indexes: [{ name: 'idx_email', columns: ['email'], unique: true }],
56
+ foreign_keys: [],
57
+ },
58
+ },
59
+ };
60
+ ```
61
+
62
+ ```json
63
+ // schema.json dụ kiểu json
64
+ {
65
+ "version": "1.0.0",
66
+ "database_name": "example.db",
67
+ "description": "Schema for example application.",
68
+ "schemas": {
69
+ "users": {
70
+ "description": "User accounts table.",
71
+ "cols": [
72
+ {
73
+ "name": "id",
74
+ "type": "integer",
75
+ "constraints": "PRIMARY KEY AUTOINCREMENT"
76
+ },
77
+ {
78
+ "name": "name",
79
+ "type": "text",
80
+ "constraints": "NOT NULL"
81
+ },
82
+ {
83
+ "name": "email",
84
+ "type": "text",
85
+ "constraints": "UNIQUE NOT NULL"
86
+ },
87
+ {
88
+ "name": "created_at",
89
+ "type": "timestamp",
90
+ "constraints": "DEFAULT CURRENT_TIMESTAMP"
355
91
  }
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);
92
+ ]
93
+ },
94
+ "posts": {
95
+ "description": "Posts created by users.",
96
+ "cols": [
97
+ {
98
+ "name": "id",
99
+ "type": "integer",
100
+ "constraints": "PRIMARY KEY AUTOINCREMENT"
101
+ },
102
+ {
103
+ "name": "title",
104
+ "type": "text",
105
+ "constraints": "NOT NULL"
106
+ },
107
+ {
108
+ "name": "content",
109
+ "type": "text"
110
+ },
111
+ {
112
+ "name": "user_id",
113
+ "type": "integer",
114
+ "constraints": "NOT NULL"
115
+ }
116
+ ],
117
+ "foreign_keys": [
118
+ {
119
+ "name": "fk_posts_user_id",
120
+ "column": "user_id",
121
+ "references": {
122
+ "table": "users",
123
+ "column": "id"
124
+ },
125
+ "on_delete": "CASCADE"
126
+ }
127
+ ]
428
128
  }
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
129
  }
495
130
  }
496
-
497
- // Chạy với: deno run --allow-read --allow-write main.ts
498
- denoExample();
499
131
  ```
500
132
 
501
- ### Bun Application
133
+ ### Bước 2: Khởi tạo và Sử dụng
134
+ Sử dụng singleton UniversalSQLite.
502
135
 
503
136
  ```typescript
504
- // app.ts
505
137
  import UniversalSQLite from '@dqcai/sqlite';
506
138
 
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
- ```
139
+ const sqlite = UniversalSQLite.getInstance();
550
140
 
551
- ## 🏗️ dụ nâng cao
141
+ async function init() {
142
+ await sqlite.initializeFromSchema(exampleSchema);
143
+ const dao = await sqlite.connect('example_db');
552
144
 
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
- }
145
+ // CRUD example
146
+ await dao.insert({ name: 'users', cols: [{ name: 'name', value: 'John' }, { name: 'email', value: 'john@example.com' }] });
147
+ const users = await dao.selectAll({ name: 'users' });
148
+ console.log(users);
615
149
  }
616
150
 
617
- transactionExample();
151
+ init();
618
152
  ```
619
153
 
620
- ### 2. Complex Queries với JOIN
154
+ ## Adapters and Environment Setup
621
155
 
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
- }
156
+ UniversalSQLite tự động detect môi trường và chọn adapter phù hợp. Tuy nhiên, bạn có thể register adapter tùy chỉnh nếu cần.
695
157
 
696
- complexQueryExample();
697
- ```
158
+ ### Common Adapters
159
+ - **Browser**: Sử dụng sql.js (cần import sql.js library).
160
+ - **Node.js**: Sử dụng better-sqlite3 hoặc sqlite3 (cần cài đặt package tương ứng).
161
+ - **React Native**: Sử dụng react-native-sqlite-storage (cần cài đặt package).
162
+ - **Deno/Bun**: Sử dụng native SQLite support.
698
163
 
699
- ### 3. Real-time Data Synchronization
164
+ ### Register Adapter
165
+ Khai báo adapter tùy chỉnh bằng cách extend BaseAdapter và register với DatabaseFactory.
700
166
 
701
167
  ```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
- }
168
+ import { BaseAdapter, SQLiteAdapter } from '@dqcai/sqlite';
169
+ import DatabaseFactory from '@dqcai/sqlite';
788
170
 
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
- }
171
+ class CustomNodeAdapter extends BaseAdapter implements SQLiteAdapter {
172
+ // Implement connect and isSupported
173
+ async connect(path: string) { /* ... */ }
174
+ isSupported() { return true; }
887
175
  }
888
176
 
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();
177
+ DatabaseFactory.registerAdapter(new CustomNodeAdapter());
920
178
  ```
921
179
 
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
- ```
180
+ ## Usage Guide
1004
181
 
1005
- ### 6. Data Analytics và Reporting
182
+ ### 1. Khởi tạo Database
183
+ Sử dụng DatabaseFactory để tạo hoặc mở database.
1006
184
 
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
- }
185
+ - **Tạo từ Schema**:
186
+ ```typescript
187
+ const dao = await DatabaseFactory.createFromConfig(exampleSchema);
188
+ ```
1103
189
 
1104
- analyticsExample();
1105
- ```
190
+ - **Mở Existing Database**:
191
+ ```typescript
192
+ const dao = await DatabaseFactory.openExisting('example_db.db');
193
+ ```
1106
194
 
1107
- ## 🔍 Debugging và Monitoring
195
+ - **Create DAO Directly**:
196
+ ```typescript
197
+ const dao = DatabaseFactory.createDAO('example_db.db', { createIfNotExists: true });
198
+ await dao.connect();
199
+ ```
1108
200
 
1109
- ### 1. Query Performance Monitoring
201
+ ### 2. CRUD Operations với UniversalDAO
202
+ UniversalDAO cung cấp các method CRUD cơ bản.
1110
203
 
1111
204
  ```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
- }
205
+ // Insert
206
+ await dao.insert({ name: 'users', cols: [{ name: 'name', value: 'Alice' }] });
1138
207
 
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
- ```
208
+ // Update
209
+ await dao.update({ name: 'users', cols: [{ name: 'name', value: 'Bob' }], wheres: [{ name: 'id', value: 1 }] });
1180
210
 
1181
- ### 2. Connection Pool (Advanced)
211
+ // Delete
212
+ await dao.delete({ name: 'users', wheres: [{ name: 'id', value: 1 }] });
1182
213
 
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();
214
+ // Select
215
+ const row = await dao.select({ name: 'users', wheres: [{ name: 'id', value: 1 }] });
216
+ const allRows = await dao.selectAll({ name: 'users' });
1256
217
  ```
1257
218
 
1258
- ## 🛠️ Configuration và Customization
1259
-
1260
- ### Environment Detection
219
+ ### 3. Query Builder
220
+ Xây dựng query phức tạp.
1261
221
 
1262
222
  ```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
- }
223
+ import { QueryBuilder } from '@dqcai/sqlite';
1322
224
 
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
- }
225
+ const qb = new QueryBuilder(dao).table('users')
226
+ .select('id', 'name')
227
+ .where('age', '>', 18)
228
+ .orderBy('name', 'ASC')
229
+ .limit(10);
1347
230
 
1348
- errorHandlingExample();
231
+ const results = await qb.get();
1349
232
  ```
1350
233
 
1351
- ## 📱 Platform-Specific Examples
1352
-
1353
- ### React Native với Expo
234
+ ### 4. Migration
235
+ Sử dụng MigrationManager.
1354
236
 
1355
237
  ```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
- },
238
+ const migrationManager = new MigrationManager(dao);
239
+ migrationManager.addMigration({
240
+ version: '1.1.0',
241
+ description: 'Add column',
242
+ up: async (dao) => { await dao.execute('ALTER TABLE users ADD COLUMN age INTEGER'); },
243
+ down: async (dao) => { /* rollback */ },
1490
244
  });
245
+ await migrationManager.migrate();
1491
246
  ```
1492
247
 
1493
- ### Next.js API Routes
248
+ ### 5. Import/Export Data
249
+ Sử dụng CSVImporter hoặc importData.
1494
250
 
1495
251
  ```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
- }
252
+ const importer = new CSVImporter(dao);
253
+ await importer.importFromCSV('users', csvString, { hasHeader: true });
1519
254
 
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
- });
255
+ // Hoặc import JSON
256
+ await dao.importData({ tableName: 'users', data: [{ name: 'John' }] });
1647
257
  ```
1648
258
 
1649
- ## 🔐 Best Practices
1650
-
1651
- ### 1. Security
1652
-
259
+ ### 6. Transaction
1653
260
  ```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
- }
261
+ await dao.beginTransaction();
262
+ try {
263
+ // operations
264
+ await dao.commitTransaction();
265
+ } catch {
266
+ await dao.rollbackTransaction();
1701
267
  }
1702
268
  ```
1703
269
 
1704
- ### 2. Performance Optimization
270
+ ### 7. BaseService
271
+ Tạo service layer.
1705
272
 
1706
273
  ```typescript
1707
- class OptimizedDatabase {
1708
- private db: UniversalSQLite;
1709
-
274
+ class UserService extends BaseService<{ id: number; name: string }> {
1710
275
  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
- }
276
+ super('example_db', 'users');
1745
277
  }
1746
278
  }
1747
- ```
1748
279
 
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
- }
280
+ const userService = new UserService();
281
+ await userService.create({ name: 'John' });
1773
282
  ```
1774
283
 
1775
- #### 2. React Native Windows Setup
284
+ ### 8. Role-Based Management
285
+ Sử dụng DatabaseManager để quản lý role.
1776
286
 
1777
287
  ```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
- }
288
+ DatabaseManager.registerRole({ roleName: 'admin', requiredDatabases: ['example_db'] });
289
+ await DatabaseManager.setCurrentUserRoles(['admin']);
290
+ const dao = DatabaseManager.get('example_db');
1800
291
  ```
1801
292
 
1802
- #### 3. Browser CORS Issues
293
+ ## Examples
1803
294
 
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
- ```
295
+ ### Example for React Native
1873
296
 
1874
- ## 📚 Advanced Use Cases
297
+ 1. Cài đặt dependencies:
298
+ ```bash
299
+ npm install react-native-sqlite-storage
300
+ ```
1875
301
 
1876
- ### 1. Multi-Database Application
302
+ 2. Khai báo Adapter (nếu cần tùy chỉnh, nhưng thư viện tự detect):
303
+ ```typescript
304
+ import { enablePromise, openDatabase} from 'react-native-sqlite-storage';
305
+ import { BaseAdapter } from '@dqcai/sqlite';
306
+ enablePromise(true);
307
+ class ReactNativeAdapter extends BaseAdapter {
308
+ isSupported() {
309
+ return typeof Platform !== 'undefined' && Platform.OS !== 'web';
310
+ }
1877
311
 
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;
312
+ connect(path) {
313
+ return new Promise((resolve, reject) => {
314
+ const db = openDatabase(
315
+ { name: path, location: 'default' },
316
+ () => {
317
+ resolve({
318
+ execute: (sql, params) => {
319
+ return new Promise((execResolve, execReject) => {
320
+ db.transaction(tx => {
321
+ tx.executeSql(
322
+ sql,
323
+ params,
324
+ (tx, results) => {
325
+ const rowsAffected = results.rowsAffected;
326
+ let rows = [];
327
+ for (let i = 0; i < results.rows.length; i++) {
328
+ rows.push(results.rows.item(i));
329
+ }
330
+ execResolve({ rows, rowsAffected, lastInsertRowId: results.insertId });
331
+ },
332
+ (tx, error) => {
333
+ execReject(error);
334
+ }
335
+ );
336
+ });
337
+ });
338
+ },
339
+ close: () => {
340
+ return new Promise((closeResolve, closeReject) => {
341
+ db.close((err) => {
342
+ if (err) return closeReject(err);
343
+ closeResolve();
344
+ });
345
+ });
346
+ }
347
+ });
348
+ },
349
+ (error) => reject(error)
350
+ );
351
+ });
352
+ }
1972
353
  }
354
+ export default ReactNativeAdapter;
1973
355
 
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
- }
356
+ ```
2043
357
 
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
- }
358
+ 3. Tạo và Mở Database, CRUD:
359
+ ```typescript
360
+ import { DatabaseFactory } from '@dqcai/sqlite';
361
+ import ReactNativeAdapter from './adapters/react-native-adapter'; // Hoặc NodejsAdapter
362
+ import schema from './schema.json';
2075
363
 
2076
- multiDbExample();
2077
- ```
364
+ async function setupDatabase() {
365
+ // Đăng ký adapter để thư viện biết cách kết nối
366
+ DatabaseFactory.registerAdapter(new ReactNativeAdapter()); // Đăng ký adapter cho môi trường
2078
367
 
2079
- ### 2. Real-time Sync với WebSocket
368
+ // Tạo hoặc mở database. Nếu file chưa tồn tại, nó sẽ được tạo mới và init schema.
369
+ // Nếu đã tồn tại, nó sẽ được mở.
370
+ const dao = await DatabaseFactory.createOrOpen({ config: schema });
2080
371
 
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
- }
372
+ console.log(`Kết nối thành công tới database: ${dao.getDatabaseInfo().name}`);
373
+ return dao;
2197
374
  }
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
375
 
2227
- realtimeSyncExample();
2228
- ```
376
+ async function crudOperations(dao) {
377
+ // 1. CREATE (Insert)
378
+ console.log('Inserting a new user...');
379
+ const userToInsert = {
380
+ name: 'John Doe',
381
+ email: 'john.doe@example.com',
382
+ };
383
+ const insertResult = await dao.insert({
384
+ name: 'users',
385
+ cols: Object.entries(userToInsert).map(([name, value]) => ({ name, value })),
386
+ });
387
+ const newUserId = insertResult.lastInsertRowId;
388
+ console.log(`User inserted with ID: ${newUserId}`);
389
+
390
+ // 2. READ (Select)
391
+ console.log('Selecting user by email...');
392
+ const selectedUser = await dao.select({
393
+ name: 'users',
394
+ cols: [], // Chọn tất cả các cột
395
+ wheres: [{ name: 'email', value: 'john.doe@example.com' }],
396
+ });
397
+ console.log('Selected user:', selectedUser);
398
+
399
+ // 3. UPDATE
400
+ console.log('Updating user name...');
401
+ await dao.update({
402
+ name: 'users',
403
+ cols: [{ name: 'name', value: 'John Smith' }],
404
+ wheres: [{ name: 'id', value: newUserId }],
405
+ });
406
+ const updatedUser = await dao.select({
407
+ name: 'users',
408
+ cols: ['id', 'name'],
409
+ wheres: [{ name: 'id', value: newUserId }],
410
+ });
411
+ console.log('Updated user:', updatedUser);
2229
412
 
2230
- ### 3. Database Backup và Restore
413
+ // 4. DELETE
414
+ console.log('Deleting the user...');
415
+ const deleteResult = await dao.delete({
416
+ name: 'users',
417
+ wheres: [{ name: 'id', value: newUserId }],
418
+ });
419
+ console.log(`Rows deleted: ${deleteResult.rowsAffected}`);
2231
420
 
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`;
421
+ const checkUser = await dao.select({
422
+ name: 'users',
423
+ wheres: [{ name: 'id', value: newUserId }],
424
+ });
425
+ console.log('User exists after delete?', Object.keys(checkUser).length > 0);
2278
426
  }
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
-
427
+
428
+ // Chạy ví dụ
429
+ async function main() {
2294
430
  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');
431
+ const dao = await setupDatabase();
432
+ await crudOperations(dao);
433
+ await dao.close();
434
+ console.log('Đã đóng kết nối database.');
2302
435
  } catch (error) {
2303
- await this.db.query('ROLLBACK');
2304
- throw new Error(`Restore failed: ${error}`);
436
+ console.error('Đã xảy ra lỗi:', error);
2305
437
  }
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
438
  }
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
439
 
2503
- ### Core Types
440
+ main();
441
+ ```
442
+
443
+ ### Example for Node.js
444
+
445
+ 1. Cài đặt dependencies:
446
+ ```bash
447
+ npm install better-sqlite3
448
+ ```
449
+
450
+ 2. Khai báo Adapter (tự detect, nhưng có thể tùy chỉnh):
451
+ ```typescript
452
+ class NodeAdapter extends BaseAdapter {
453
+ async connect(path: string) {
454
+ const SQLite3 = require('better-sqlite3');
455
+ const db = new SQLite3(path);
456
+ return {
457
+ execute: (sql: string, params = []) => {
458
+ const stmt = db.prepare(sql);
459
+ const result = stmt.run(params);
460
+ return { rows: stmt.all(params), rowsAffected: result.changes };
461
+ },
462
+ close: () => db.close(),
463
+ };
464
+ }
465
+ isSupported() { return true; }
466
+ }
467
+
468
+ DatabaseFactory.registerAdapter(new NodeAdapter());
469
+ ```
470
+
471
+ 3. Tạo và Mở Database, CRUD:
472
+ ```typescript
473
+ import UniversalSQLite from '@dqcai/sqlite';
474
+
475
+ const sqlite = UniversalSQLite.getInstance();
476
+
477
+ async function nodeExample() {
478
+ await sqlite.initializeFromSchema(exampleSchema);
479
+ const dao = await sqlite.connect('example_db');
480
+
481
+ // CRUD
482
+ await dao.insert({ name: 'users', cols: [{ name: 'name', value: 'NodeUser' }, { name: 'email', value: 'node@example.com' }] });
483
+ const users = await dao.selectAll({ name: 'users' });
484
+ console.log('Users:', users);
485
+
486
+ // Query Builder
487
+ const qb = sqlite.query('users');
488
+ const results = await qb.select('*').where('name', 'LIKE', '%User%').get();
489
+
490
+ // Migration
491
+ const migrationManager = sqlite.createMigrationManager();
492
+ migrationManager.addMigration({
493
+ version: '1.0.1',
494
+ description: 'Add age',
495
+ up: async (dao) => await dao.execute('ALTER TABLE users ADD COLUMN age INTEGER'),
496
+ down: async (dao) => await dao.execute('ALTER TABLE users DROP COLUMN age'),
497
+ });
498
+ await migrationManager.migrate();
499
+
500
+ // Import from File
501
+ const importer = sqlite.createCSVImporter();
502
+ await importer.importFromFile('users', './data.csv', { hasHeader: true });
503
+
504
+ await sqlite.closeAll();
505
+ }
506
+
507
+ nodeExample();
508
+ ```
509
+
510
+ ## API Reference
511
+
512
+ - **UniversalSQLite**: Singleton chính, methods: initialize, connect, getDAO, query, execute, importData, etc.
513
+ - **UniversalDAO**: Core DAO cho CRUD, execute, importData.
514
+ - **QueryBuilder**: Xây dựng query với fluent API.
515
+ - **MigrationManager**: Quản lý migration.
516
+ - **CSVImporter**: Import/export CSV.
517
+ - **BaseService**: Base cho service layer.
518
+ - **DatabaseFactory**: Factory để tạo DAO.
519
+ - **DatabaseManager**: Quản lý connections, roles.
520
+
521
+ Xem source code để biết chi tiết types và methods.
522
+
523
+
524
+ ## Best Practices
525
+
526
+ 1. **Always use transactions for multi-step operations**
527
+ 2. **Define schemas for type safety and validation**
528
+ 3. **Use parameterized queries to prevent SQL injection**
529
+ 4. **Implement proper error handling**
530
+ 5. **Close connections when done**
531
+ 6. **Use migrations for schema changes**
532
+ 7. **Batch large operations for better performance**
533
+
534
+ ## Contributing
535
+
536
+ 1. Fork the repository
537
+ 2. Create a feature branch
538
+ 3. Make your changes
539
+ 4. Add tests
540
+ 5. Submit a pull request
2504
541
 
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
542
 
2632
543
  ## 📄 License
2633
544
 
2634
- MIT License - see LICENSE file for details.
545
+ MIT © [Cuong Doan](https://github.com/cuongdqpayment)
2635
546
 
2636
547
  ## 🙏 Acknowledgments
2637
548
 
@@ -2646,8 +557,9 @@ MIT License - see LICENSE file for details.
2646
557
  - [Documentation](https://github.com/cuongdqpayment/dqcai-sqlite/docs)
2647
558
  - [Examples Repository](https://github.com/cuongdqpayment/dqcai-sqlite)
2648
559
  - [Issue Tracker](https://github.com/cuongdqpayment/dqcai-sqlite/issues)
560
+ - [Issue Facebook](https://www.facebook.com/share/p/19esHGbaGj/)
2649
561
  - [NPM Package](https://www.npmjs.com/package/@dqcai/sqlite)
2650
562
 
2651
563
  ---
2652
564
 
2653
- **Universal SQLite** - One library, all platforms! 🚀
565
+ 🔥 **@dqcai/sqlite** One library, all platforms! 🚀