web-sqlite-js 1.1.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,9 @@
13
13
  <a href="https://www.npmjs.com/package/web-sqlite-js" target="_blank">
14
14
  <img src="https://img.shields.io/npm/v/web-sqlite-js.svg" alt="NPM Version" />
15
15
  </a>
16
+ <a href="https://github.com/wuchuheng/web-sqlite-js/discussions" target="_blank">
17
+ <img src="https://img.shields.io/badge/v2.0.0-new%20features-blue" alt="v2.0.0" />
18
+ </a>
16
19
  <a href="https://github.com/wuchuheng/web-sqlite-js/blob/main/LICENSE" target="_blank">
17
20
  <img src="https://img.shields.io/github/license/wuchuheng/web-sqlite-js.svg" alt="License" />
18
21
  </a>
@@ -35,6 +38,9 @@ Designed to be truly effortless, it allows you to get a high-performance relatio
35
38
  - [Usage](#usage)
36
39
  - [Debug mode](#debug-mode)
37
40
  - [Transactions](#transactions)
41
+ - [Structured Logging (v2.0.0)](#structured-logging-v200)
42
+ - [Global Database Access (v2.0.0)](#global-database-access-v200)
43
+ - [Database Events (v2.0.0)](#database-events-v200)
38
44
 
39
45
  ## Features
40
46
 
@@ -43,6 +49,10 @@ Designed to be truly effortless, it allows you to get a high-performance relatio
43
49
  - **Concurrency Safe**: Built-in mutex ensures safe, sequential execution of commands.
44
50
  - **Type-Safe**: Written in TypeScript with full type definitions.
45
51
  - **Transactions**: Supports atomic transactions with automatic rollback on error.
52
+ - **Structured Logging** (v2.0.0): Subscribe to SQL execution logs via `onLog()`.
53
+ - **Global Namespace** (v2.0.0): Access databases from anywhere via `window.__web_sqlite`.
54
+ - **Database Events** (v2.0.0): Listen to database open/close events for UI synchronization.
55
+ - **Database Registry** (v2.0.0): Prevents duplicate database opens with automatic tracking.
46
56
 
47
57
  ## Quick start
48
58
 
@@ -66,8 +76,8 @@ For quick demos or plain HTML pages you can load the prebuilt module directly:
66
76
 
67
77
  ```html
68
78
  <script type="module">
69
- import openDB from "https://cdn.jsdelivr.net/npm/web-sqlite-js@1.0.9/dist/index.js";
70
- // ...
79
+ import openDB from "https://cdn.jsdelivr.net/npm/web-sqlite-js@1.0.9/dist/index.js";
80
+ // ...
71
81
  </script>
72
82
  ```
73
83
 
@@ -95,18 +105,18 @@ Update your `vite.config.ts`:
95
105
  import { defineConfig } from "vite";
96
106
 
97
107
  export default defineConfig({
98
- server: {
99
- headers: {
100
- "Cross-Origin-Opener-Policy": "same-origin",
101
- "Cross-Origin-Embedder-Policy": "require-corp",
102
- },
108
+ server: {
109
+ headers: {
110
+ "Cross-Origin-Opener-Policy": "same-origin",
111
+ "Cross-Origin-Embedder-Policy": "require-corp",
103
112
  },
104
- preview: {
105
- headers: {
106
- "Cross-Origin-Opener-Policy": "same-origin",
107
- "Cross-Origin-Embedder-Policy": "require-corp",
108
- },
113
+ },
114
+ preview: {
115
+ headers: {
116
+ "Cross-Origin-Opener-Policy": "same-origin",
117
+ "Cross-Origin-Embedder-Policy": "require-corp",
109
118
  },
119
+ },
110
120
  });
111
121
  ```
112
122
 
@@ -120,23 +130,23 @@ Update your `next.config.js`:
120
130
  ```javascript
121
131
  /** @type {import('next').NextConfig} */
122
132
  const nextConfig = {
123
- async headers() {
124
- return [
125
- {
126
- source: "/(.*)",
127
- headers: [
128
- {
129
- key: "Cross-Origin-Opener-Policy",
130
- value: "same-origin",
131
- },
132
- {
133
- key: "Cross-Origin-Embedder-Policy",
134
- value: "require-corp",
135
- },
136
- ],
137
- },
138
- ];
139
- },
133
+ async headers() {
134
+ return [
135
+ {
136
+ source: "/(.*)",
137
+ headers: [
138
+ {
139
+ key: "Cross-Origin-Opener-Policy",
140
+ value: "same-origin",
141
+ },
142
+ {
143
+ key: "Cross-Origin-Embedder-Policy",
144
+ value: "require-corp",
145
+ },
146
+ ],
147
+ },
148
+ ];
149
+ },
140
150
  };
141
151
 
142
152
  module.exports = nextConfig;
@@ -151,13 +161,13 @@ Update your `webpack.config.js`:
151
161
 
152
162
  ```javascript
153
163
  module.exports = {
154
- // ...
155
- devServer: {
156
- headers: {
157
- "Cross-Origin-Opener-Policy": "same-origin",
158
- "Cross-Origin-Embedder-Policy": "require-corp",
159
- },
164
+ // ...
165
+ devServer: {
166
+ headers: {
167
+ "Cross-Origin-Opener-Policy": "same-origin",
168
+ "Cross-Origin-Embedder-Policy": "require-corp",
160
169
  },
170
+ },
161
171
  };
162
172
  ```
163
173
 
@@ -189,9 +199,9 @@ const express = require("express");
189
199
  const app = express();
190
200
 
191
201
  app.use((req, res, next) => {
192
- res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
193
- res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
194
- next();
202
+ res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
203
+ res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
204
+ next();
195
205
  });
196
206
 
197
207
  // ...
@@ -227,12 +237,12 @@ await db.exec(`
227
237
 
228
238
  // 3. Insert data (Parameterized)
229
239
  await db.exec("INSERT INTO users (name, email) VALUES (?, ?)", [
230
- "Alice",
231
- "alice@example.com",
240
+ "Alice",
241
+ "alice@example.com",
232
242
  ]);
233
243
  await db.exec("INSERT INTO users (name, email) VALUES ($name, $email)", {
234
- $name: "Bob",
235
- $email: "bob@example.com",
244
+ $name: "Bob",
245
+ $email: "bob@example.com",
236
246
  });
237
247
 
238
248
  // 4. Query data
@@ -258,7 +268,7 @@ await db.query("SELECT * FROM notes WHERE id = ?", [1]);
258
268
 
259
269
  The console output highlights SQL keywords and shows how long each statement took (click to preview):
260
270
 
261
- [![Debug console output](docs/assets/debug.png)](docs/assets/debug.png)
271
+ [![Debug console output](vitepress-docs/assets/debug.png)](docs/assets/debug.png)
262
272
 
263
273
  #### Transactions
264
274
 
@@ -266,16 +276,151 @@ Transactions are atomic. If any command inside the callback fails, the entire tr
266
276
 
267
277
  ```typescript
268
278
  await db.transaction(async (tx) => {
269
- await tx.exec("INSERT INTO users (name) VALUES (?)", ["Charlie"]);
279
+ await tx.exec("INSERT INTO users (name) VALUES (?)", ["Charlie"]);
280
+
281
+ // You can perform multiple operations safely
282
+ await tx.exec("INSERT INTO logs (action) VALUES (?)", ["User Created"]);
283
+
284
+ // If you throw an error here, both INSERTs will be rolled back!
285
+ // throw new Error('Something went wrong');
286
+ });
287
+ ```
288
+
289
+ ## Structured Logging (v2.0.0)
290
+
291
+ Subscribe to structured log events for monitoring, debugging, and analytics. The `onLog()` API allows you to capture SQL execution details, errors, and application events.
292
+
293
+ ```typescript
294
+ const db = await openDB("myapp");
295
+
296
+ // Register log listener
297
+ const cancelLog = db.onLog((log) => {
298
+ if (log.level === "error") {
299
+ // Send errors to tracking service
300
+ errorTracking.capture(log.data);
301
+ } else if (log.level === "debug") {
302
+ // Log SQL execution details
303
+ console.log(`SQL: ${log.data.sql}, Duration: ${log.data.duration}ms`);
304
+ } else if (log.level === "info") {
305
+ // Track application events (open, close, transactions)
306
+ console.log(`Event: ${log.data.action}`);
307
+ }
308
+ });
309
+
310
+ // Execute some SQL to generate logs
311
+ await db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
312
+ await db.exec("INSERT INTO users (name) VALUES (?)", ["Alice"]);
313
+
314
+ // Later: stop listening
315
+ cancelLog();
316
+ ```
317
+
318
+ **Log Levels**:
270
319
 
271
- // You can perform multiple operations safely
272
- await tx.exec("INSERT INTO logs (action) VALUES (?)", ["User Created"]);
320
+ - `"debug"` - SQL execution details (sql, duration, bind parameters)
321
+ - `"info"` - Application events (open, close, commit, rollback)
322
+ - `"error"` - SQL errors and exceptions
273
323
 
274
- // If you throw an error here, both INSERTs will be rolled back!
275
- // throw new Error('Something went wrong');
324
+ **Multiple Callbacks**: You can register multiple log listeners simultaneously:
325
+
326
+ ```typescript
327
+ const cancel1 = db.onLog((log) => console.log("Logger 1:", log));
328
+ const cancel2 = db.onLog((log) => {
329
+ if (log.level === "error") sendToAlerting(log.data);
276
330
  });
277
331
  ```
278
332
 
333
+ ---
334
+
335
+ ## Global Database Access (v2.0.0)
336
+
337
+ Access opened databases from anywhere in your application without imports. The `window.__web_sqlite` global namespace provides direct references to all opened database instances.
338
+
339
+ ```typescript
340
+ // Open database in module A
341
+ const db = await openDB("app");
342
+
343
+ // In module B (no import needed):
344
+ const db = window.__web_sqlite.databases["app.sqlite3"];
345
+ const users = await db.query("SELECT * FROM users");
346
+
347
+ // List all opened databases
348
+ console.log(Object.keys(window.__web_sqlite.databases));
349
+ // Output: ["app.sqlite3", "users.sqlite3"]
350
+ ```
351
+
352
+ **Use Cases**:
353
+
354
+ - **DevTools Integration**: Access databases from browser console for debugging
355
+ - **Cross-Module Communication**: Share database state without prop drilling
356
+ - **Debugging**: Inspect and query databases directly from DevTools console
357
+
358
+ **Browser Console Example**:
359
+
360
+ ```javascript
361
+ // From browser DevTools console:
362
+ window.__web_sqlite.databases["app.sqlite3"]
363
+ .query("SELECT * FROM users")
364
+ .then((users) => console.table(users));
365
+ ```
366
+
367
+ ---
368
+
369
+ ## Database Events (v2.0.0)
370
+
371
+ Subscribe to database open/close events for UI synchronization and monitoring. The `onDatabaseChange()` API notifies you when databases are opened or closed.
372
+
373
+ ```typescript
374
+ // Subscribe to database changes
375
+ const unsubscribe = window.__web_sqlite.onDatabaseChange((event) => {
376
+ if (event.action === "opened") {
377
+ console.log(`Database opened: ${event.dbName}`);
378
+ updateDatabaseList(event.databases);
379
+ } else if (event.action === "closed") {
380
+ console.log(`Database closed: ${event.dbName}`);
381
+ updateDatabaseList(event.databases);
382
+ }
383
+ console.log("Current databases:", event.databases);
384
+ });
385
+
386
+ // Open a database
387
+ await openDB("app");
388
+ // Output: Database opened: app.sqlite3
389
+ // Output: Current databases: ["app.sqlite3"]
390
+
391
+ // Open another database
392
+ await openDB("users");
393
+ // Output: Database opened: users.sqlite3
394
+ // Output: Current databases: ["app.sqlite3", "users.sqlite3"]
395
+
396
+ // Close first database
397
+ await window.__web_sqlite.databases["app.sqlite3"].close();
398
+ // Output: Database closed: app.sqlite3
399
+ // Output: Current databases: ["users.sqlite3"]
400
+
401
+ // Unsubscribe when done
402
+ // unsubscribe();
403
+ ```
404
+
405
+ **Event Structure**:
406
+
407
+ ```typescript
408
+ interface DatabaseChangeEvent {
409
+ action: "opened" | "closed"; // What happened
410
+ dbName: string; // Which database (normalized name)
411
+ databases: string[]; // All currently opened database names
412
+ }
413
+ ```
414
+
415
+ **Use Cases**:
416
+
417
+ - **DevTools Panels**: Show active databases in browser DevTools
418
+ - **UI Updates**: Refresh database list when databases open/close
419
+ - **Monitoring**: Track database lifecycle for debugging
420
+ - **Multi-Window Sync**: Coordinate database access across browser windows
421
+
422
+ ---
423
+
279
424
  ## Star History
280
425
 
281
426
  <p align="left">
package/dist/index.d.ts CHANGED
@@ -21,6 +21,20 @@ declare interface DBInterface {
21
21
  transaction<T>(fn: transactionCallback<T>): Promise<T>;
22
22
  /** Close the database and release resources. */
23
23
  close(): Promise<void>;
24
+ /**
25
+ * Subscribe to log events
26
+ * Logs include SQL execution, timing, errors, and application events
27
+ *
28
+ * @param callback - Called for each log entry
29
+ * @returns Unsubscribe function
30
+ *
31
+ * @example
32
+ * const unsubscribe = db.onLog((log) => {
33
+ * console.log(`[${log.level}]`, log.data);
34
+ * });
35
+ * // Later: unsubscribe();
36
+ */
37
+ onLog(callback: (log: LogEntry) => void): () => void;
24
38
  /** Dev tooling APIs for release testing. */
25
39
  devTool: DevTool;
26
40
  }
@@ -46,6 +60,20 @@ declare type ExecResult = {
46
60
  lastInsertRowid?: number | bigint;
47
61
  };
48
62
 
63
+ /**
64
+ * Log entry with level and structured data
65
+ */
66
+ declare type LogEntry = {
67
+ /**
68
+ * Log level: 'info' | 'debug' | 'error'
69
+ */
70
+ level: "info" | "debug" | "error";
71
+ /**
72
+ * Log data (SQL, timing, errors, events, etc.)
73
+ */
74
+ data: unknown;
75
+ };
76
+
49
77
  /**
50
78
  * Opens a SQLite database connection with release-versioning support.
51
79
  *