@procedures/sqlite 0.4.0 → 0.6.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/CHANGELOG.md +42 -0
- package/README.md +60 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/engines.d.ts +68 -0
- package/dist/engines.d.ts.map +1 -0
- package/dist/engines.js +268 -0
- package/dist/engines.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +2 -11
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +1 -25
- package/dist/module.js.map +1 -1
- package/dist/storage.d.ts +57 -16
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +255 -148
- package/dist/storage.js.map +1 -1
- package/package.json +6 -5
- package/src/engines.ts +360 -0
- package/src/index.ts +10 -2
- package/src/module.ts +1 -27
- package/src/storage.ts +335 -176
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @procedures/sqlite
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 72a8c65: The deprecated `db.*` aliases are gone: `db.query`, `db.execute`, `db.tables`, `db.schema`, `db.transaction`, `db.migrate` and `db.backup`. Use `sqlite.query` and the other `sqlite.*` names. Version 0.4.0 renamed them and kept the old names for one release.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- @procedures/client@0.6.0
|
|
12
|
+
|
|
13
|
+
## 0.5.0
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 89481b0: `SqliteStorage` has two engines. The new option `engine` is `"auto"` (the default), `"node"` or `"sqljs"`.
|
|
18
|
+
|
|
19
|
+
- The engine `node` is `node:sqlite` of Node.js 22.13 and later. SQLite writes only the changed pages of the file. Before, sql.js wrote the whole file after each write, so a write to a large collection was slow.
|
|
20
|
+
- In the nightly benchmark, 1,000 writes one after the other to a file of 1,000 items take 141 ms with `node` and 1,246 ms with `sqljs`.
|
|
21
|
+
- With `node`, the file is in WAL mode with a busy timeout (the new option `busyTimeout`, 5 seconds by default). Two processes can use one file: a write waits for the lock of the other process, and the versions of the change log agree.
|
|
22
|
+
- The engine `sqljs` is sql.js, as before. The engine `auto` uses it when the import of `node:sqlite` fails, for example on Node.js 22.12 and before.
|
|
23
|
+
- The two engines use one file format: a file of one engine opens with the other, with its items and its versions.
|
|
24
|
+
- The guarantees stay for each engine:
|
|
25
|
+
- Each write runs in one transaction.
|
|
26
|
+
- The storages of one file in one process share one database.
|
|
27
|
+
- The change log and its versions stay valid after a restart.
|
|
28
|
+
- A storage that only reads does not make the file.
|
|
29
|
+
- The contract of `close()` does not change.
|
|
30
|
+
- The storages of one file in one process share one engine. A storage with the other engine refuses the open database.
|
|
31
|
+
- sql.js refuses a file with a write-ahead log of the engine `node` that holds changes.
|
|
32
|
+
- New: `getEngine()`, `getIds()` (the ids without the values), `isNodeSqliteAvailable()`, and `stats.engine` in `getMetadata()`.
|
|
33
|
+
- `getChangesSince()` reads in one read transaction, and it reads the epoch from the file at each call.
|
|
34
|
+
- bc74091: After `close()`, each operation of `SqliteStorage` and `MongoStorage` rejects with an error that says "closed", and a second `close()` does nothing. Before, `SqliteStorage.flush()` and an empty batch resolved after `close()`, and `MongoStorage` kept working.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [0f75644]
|
|
39
|
+
- Updated dependencies [09e5566]
|
|
40
|
+
- Updated dependencies [ab77f75]
|
|
41
|
+
- Updated dependencies [a47bee6]
|
|
42
|
+
- Updated dependencies [bc74091]
|
|
43
|
+
- @procedures/client@0.5.0
|
|
44
|
+
|
|
3
45
|
## 0.4.0
|
|
4
46
|
|
|
5
47
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -25,9 +25,9 @@ The package has 9 procedures. Its register entry (`import "@procedures/sqlite/re
|
|
|
25
25
|
|
|
26
26
|
In version 0.3.0 and before, the database procedures had the namespace `db`. The general name `db` did not tell the database. Since version 0.4.0, the namespace is `sqlite`.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Version 0.4.0 kept the old names for one release as deprecated aliases. Version 0.6.0 removed them: `db.query`, `db.execute`, `db.tables`, `db.schema`, `db.transaction`, `db.migrate` and `db.backup` give `NOT_FOUND`. Use the `sqlite.*` names.
|
|
29
29
|
|
|
30
|
-
The MCP server (`bundle-mcp`)
|
|
30
|
+
The tools of the MCP server (`bundle-mcp`) are `sqlite.query`, `sqlite.execute`, `sqlite.tables` and `sqlite.schema`. The `logs.*` procedures keep their names.
|
|
31
31
|
|
|
32
32
|
## Architecture
|
|
33
33
|
|
|
@@ -504,11 +504,38 @@ const users = new SqliteStorage<User>({ path: "./data/app.db", table: "users" })
|
|
|
504
504
|
await users.set("u1", { name: "Ada" });
|
|
505
505
|
await users.setBatch([["u2", { name: "Bob" }], ["u3", { name: "Cy" }]]);
|
|
506
506
|
const changes = await users.getChangesSince(); // { version, full: true, changed, deleted: [] }
|
|
507
|
+
await users.getEngine(); // "node" on Node.js 22.13 or later
|
|
507
508
|
await users.close();
|
|
508
509
|
```
|
|
509
510
|
|
|
510
511
|
The entry `@procedures/sqlite/storage` has the storage alone. The package root exports it too.
|
|
511
512
|
|
|
513
|
+
### The engines
|
|
514
|
+
|
|
515
|
+
The storage has two SQLite engines. The option `engine` selects one.
|
|
516
|
+
|
|
517
|
+
| `engine` | The engine | The file write |
|
|
518
|
+
|---|---|---|
|
|
519
|
+
| `node` | `node:sqlite` of Node.js, without a native build | SQLite writes only the changed pages. The file is in WAL mode. |
|
|
520
|
+
| `sqljs` | sql.js (SQLite in WebAssembly), through `@mark1russell7/docker-sqlite` | The storage keeps the database in memory, and it writes the whole file after a write. |
|
|
521
|
+
| `auto` (the default) | `node` when Node.js has `node:sqlite`, and else `sqljs` | |
|
|
522
|
+
|
|
523
|
+
- Node.js 22.13 and later have `node:sqlite` without a flag. Node.js 22.5 to 22.12 have it only with the flag `--experimental-sqlite`. Node.js 22.0 to 22.4 do not have it.
|
|
524
|
+
- Node.js 22 shows the warning `ExperimentalWarning: SQLite is an experimental feature` at the first open of the engine `node`. Node.js 24 and 26 do not show it.
|
|
525
|
+
- Node.js 26.11 renamed the class `DatabaseSync` to `Database`. The storage uses `Database` when the module has it, and else `DatabaseSync`.
|
|
526
|
+
- With `auto`, the storage uses `sqljs` when the import of `node:sqlite` fails. With `node`, the open fails then, and each operation gives the error.
|
|
527
|
+
- The two engines use the same file format. A file that one engine writes opens with the other engine, with its items and its versions.
|
|
528
|
+
- `getEngine()` gives the engine of the storage. `getMetadata()` gives it in `stats.engine`. `isNodeSqliteAvailable()` tells if this Node.js has `node:sqlite`.
|
|
529
|
+
|
|
530
|
+
The nightly benchmarks (`bench/storage.bench.ts`) measure the two engines in a file. These are the mean times of one CI run with Node.js 26 on Linux:
|
|
531
|
+
|
|
532
|
+
| The benchmark | `node` | `sqljs` |
|
|
533
|
+
|---|---|---|
|
|
534
|
+
| 1,000 calls of `set()`, one after the other, in a table of 1,000 items | 141 ms | 1,246 ms |
|
|
535
|
+
| One `getAll()` of a table of 10,000 items | 20 ms | 28 ms |
|
|
536
|
+
|
|
537
|
+
The engine `sqljs` writes the whole file at each write, so its write time grows with the size of the file.
|
|
538
|
+
|
|
512
539
|
### The table and the values
|
|
513
540
|
|
|
514
541
|
- The option `path` is a database file, or `:memory:` for a database that lives as long as the storage.
|
|
@@ -516,6 +543,7 @@ The entry `@procedures/sqlite/storage` has the storage alone. The package root e
|
|
|
516
543
|
- The table has the columns `id` and `value`. The value is the JSON text of the item.
|
|
517
544
|
- A value without JSON text, for example `undefined` or a `BigInt`, is a `TypeError`. The storage writes nothing then.
|
|
518
545
|
- A read gives a new copy of the item.
|
|
546
|
+
- `getIds()` gives the ids in the order of the items, and it does not read the values. A full sync of `HybridStorage` uses it.
|
|
519
547
|
- The order of the items is the order of the first write of each id, as in a `Map`.
|
|
520
548
|
|
|
521
549
|
### The change log and the versions
|
|
@@ -524,17 +552,40 @@ The entry `@procedures/sqlite/storage` has the storage alone. The package root e
|
|
|
524
552
|
- A version is `<epoch>:<seq>`, as in `InMemoryStorage`. The table `_storage_versions` keeps the epoch, so a version stays valid after a restart.
|
|
525
553
|
- The table `_storage_log` keeps the last change of each id. The option `maxTombstones` (10000 by default) limits the deletes that it keeps.
|
|
526
554
|
- A version that is older than the dropped deletes gives all items (`full: true`).
|
|
555
|
+
- A delta follows the order of the last change of each id, as in each other storage. A full result follows the order of the items.
|
|
527
556
|
- Write the table only through the storage. A change from other SQL is not in the change log.
|
|
528
557
|
|
|
529
558
|
### The transactions and the file
|
|
530
559
|
|
|
531
560
|
- Each write runs in a transaction with the change log. A batch that fails changes nothing.
|
|
532
|
-
-
|
|
533
|
-
-
|
|
534
|
-
-
|
|
535
|
-
- A storage that only reads does not make the file.
|
|
561
|
+
- A write ends after its change is in the file.
|
|
562
|
+
- A storage that only reads does not make the file. The storage makes the file, and its folder, at the first write that changes an item.
|
|
563
|
+
- `close()` writes the file and releases the database. After it, each operation rejects, also `flush()` and an empty batch. A second `close()` does nothing.
|
|
536
564
|
- The storages of one file in one process share one database. Thus the storages of two tables in one file keep their items.
|
|
537
|
-
-
|
|
565
|
+
- The first storage of a file sets the engine of the shared database. A later storage with the engine `auto` uses it. A later storage with the other engine refuses it.
|
|
566
|
+
|
|
567
|
+
The engine `node`:
|
|
568
|
+
|
|
569
|
+
- The commit of a transaction writes the change to the file. `flush()` has nothing to write.
|
|
570
|
+
- The file is in WAL mode, with `synchronous = NORMAL`. After a crash, the file stays consistent. A power loss can lose the last commits.
|
|
571
|
+
- A write that cannot make the file fails, and it changes nothing.
|
|
572
|
+
- A read-only file opens. The reads work, and each write fails.
|
|
573
|
+
|
|
574
|
+
The engine `sqljs`:
|
|
575
|
+
|
|
576
|
+
- The storage keeps the database in memory, and it writes the whole file after each write. When several writes wait, one file write holds their changes.
|
|
577
|
+
- A failed file write fails the write. The change stays in memory, and `flush()` or the next write writes it.
|
|
578
|
+
- The storage refuses a file with a write-ahead log (`<file>-wal`) of the engine `node`. The log can hold changes that are not in the file. Open the file with the engine `node` one time, or close the process that has it open.
|
|
579
|
+
|
|
580
|
+
### Two processes on one file
|
|
581
|
+
|
|
582
|
+
- With the engine `node` in each process, two processes can use one file at the same time.
|
|
583
|
+
- SQLite locks the file for each write. A write waits for the lock of another process for `busyTimeout` (5 seconds by default). After it, the write fails with `database is locked`.
|
|
584
|
+
- In WAL mode, a read does not wait for a write of another process.
|
|
585
|
+
- Each write reads the version counter in its transaction, so the versions of the two processes agree. `getChangesSince()` reads in one read transaction.
|
|
586
|
+
- A storage that opened before the file existed opens the file when another process makes it.
|
|
587
|
+
- The engine `sqljs` does not coordinate with another process. Its whole-file write replaces the writes of the other process. Do not use it on a file that another process writes.
|
|
588
|
+
- The procedures `sqlite.query` and `sqlite.execute` use sql.js, and they do not share the database of the storages. Do not write the file from them while a storage has it open.
|
|
538
589
|
|
|
539
590
|
### The durable local copy of `HybridStorage`
|
|
540
591
|
|
|
@@ -838,8 +889,10 @@ client-sqlite/
|
|
|
838
889
|
│ ├── module.ts # The procedures as data, and the sqlite.query, sqlite.execute and logs.* handlers
|
|
839
890
|
│ ├── register.ts # The ./register entry: registers the module
|
|
840
891
|
│ ├── storage.ts # SqliteStorage, the collection storage (the ./storage entry)
|
|
892
|
+
│ ├── engines.ts # The engines of SqliteStorage: node:sqlite and sql.js
|
|
841
893
|
│ ├── procedures/db/ # sqlite.tables, sqlite.schema, sqlite.transaction, sqlite.migrate, sqlite.backup
|
|
842
894
|
│ └── index.ts # Main entry point
|
|
895
|
+
├── bench/ # The benchmarks of the engines (the nightly workflow runs them)
|
|
843
896
|
├── dist/ # Compiled output
|
|
844
897
|
├── package.json
|
|
845
898
|
├── tsconfig.json
|