@procedures/sqlite 0.2.3 → 0.3.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 (59) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +126 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +6 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/module.d.ts +2 -1
  9. package/dist/module.d.ts.map +1 -1
  10. package/dist/module.js +93 -9
  11. package/dist/module.js.map +1 -1
  12. package/dist/procedures/db/backup.d.ts +19 -0
  13. package/dist/procedures/db/backup.d.ts.map +1 -0
  14. package/dist/procedures/db/backup.js +61 -0
  15. package/dist/procedures/db/backup.js.map +1 -0
  16. package/dist/procedures/db/index.d.ts +6 -0
  17. package/dist/procedures/db/index.d.ts.map +1 -0
  18. package/dist/procedures/db/index.js +6 -0
  19. package/dist/procedures/db/index.js.map +1 -0
  20. package/dist/procedures/db/migrate.d.ts +24 -0
  21. package/dist/procedures/db/migrate.d.ts.map +1 -0
  22. package/dist/procedures/db/migrate.js +110 -0
  23. package/dist/procedures/db/migrate.js.map +1 -0
  24. package/dist/procedures/db/schema.d.ts +13 -0
  25. package/dist/procedures/db/schema.d.ts.map +1 -0
  26. package/dist/procedures/db/schema.js +33 -0
  27. package/dist/procedures/db/schema.js.map +1 -0
  28. package/dist/procedures/db/shared.d.ts +25 -0
  29. package/dist/procedures/db/shared.d.ts.map +1 -0
  30. package/dist/procedures/db/shared.js +105 -0
  31. package/dist/procedures/db/shared.js.map +1 -0
  32. package/dist/procedures/db/tables.d.ts +12 -0
  33. package/dist/procedures/db/tables.d.ts.map +1 -0
  34. package/dist/procedures/db/tables.js +22 -0
  35. package/dist/procedures/db/tables.js.map +1 -0
  36. package/dist/procedures/db/transaction.d.ts +14 -0
  37. package/dist/procedures/db/transaction.d.ts.map +1 -0
  38. package/dist/procedures/db/transaction.js +44 -0
  39. package/dist/procedures/db/transaction.js.map +1 -0
  40. package/dist/storage.d.ts +123 -0
  41. package/dist/storage.d.ts.map +1 -0
  42. package/dist/storage.js +525 -0
  43. package/dist/storage.js.map +1 -0
  44. package/dist/types.d.ts +131 -0
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/types.js +109 -0
  47. package/dist/types.js.map +1 -1
  48. package/package.json +9 -3
  49. package/src/index.ts +26 -0
  50. package/src/module.ts +120 -9
  51. package/src/procedures/db/backup.ts +62 -0
  52. package/src/procedures/db/index.ts +5 -0
  53. package/src/procedures/db/migrate.ts +121 -0
  54. package/src/procedures/db/schema.ts +71 -0
  55. package/src/procedures/db/shared.ts +109 -0
  56. package/src/procedures/db/tables.ts +27 -0
  57. package/src/procedures/db/transaction.ts +43 -0
  58. package/src/storage.ts +591 -0
  59. package/src/types.ts +268 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # @procedures/sqlite
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 89d8a4a: Three procedures only read now (CAP-1, CAP-2, CAP-3, roadmap R7), so a read-only session (`mark mcp --read-only`) serves them. `db.query` refuses a statement that changes data (`PRAGMA query_only`), and the file keeps its bytes. `mongo.documents.aggregate` refuses `$out` and `$merge`, also in the pipelines of `$facet`, `$lookup` and `$unionWith`. `logs.query` gives no entries for a missing file or table, and does not make them.
8
+ - 94dd2a0: Five database procedures (roadmap R44). `db.tables` lists the tables and views, and `db.schema` gives the columns, indexes and foreign keys of a table. Both only read, and a missing file is an error. `db.query` stays read only.
9
+
10
+ `db.transaction` puts a list of statements in one transaction, and rolls it back when one fails. `db.migrate` applies numbered migrations from a folder or a list, in one transaction, with a table of the applied migrations and their sha256. A second call applies nothing. `db.backup` copies a database to a new file and checks the copy. It does not replace a file. With `vacuum: true`, it compacts the copy.
11
+ - 4ef0d44: The new collection storage `SqliteStorage` (roadmap R45) keeps the items of one collection in one table of a SQLite database: a file, or `:memory:`. It implements `CollectionStorage`, so a `ProcedureServer`, an `ApiStorage` server and a `HybridStorage` use it as they use `InMemoryStorage`. The new entry `@procedures/sqlite/storage` has it alone, and the package root exports it too.
12
+
13
+ The table has the columns `id` and `value` (the JSON text of the item). The name of the table starts with a letter and has only letters, digits and underscores. `getChangesSince()` reads a change log in the tables `_storage_log` and `_storage_versions`, with versions `<epoch>:<seq>` as `InMemoryStorage` has. The epoch stays in the file, so a version stays valid after a restart. The option `maxTombstones` limits the deletes that the log keeps.
14
+
15
+ Each write runs in a transaction, so a batch that fails changes nothing. The storage keeps the database in memory and writes the file after each write. One file write can hold the changes of several writes. The storages of one file in one process share one database. A storage that only reads does not make the file.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [ddea529]
20
+ - Updated dependencies [b2a3e3a]
21
+ - Updated dependencies [a8c47f1]
22
+ - Updated dependencies [381ce3a]
23
+ - Updated dependencies [0c627f8]
24
+ - Updated dependencies [ae7f05f]
25
+ - Updated dependencies [7873940]
26
+ - Updated dependencies [a70d445]
27
+ - Updated dependencies [bc70357]
28
+ - Updated dependencies [07cc998]
29
+ - Updated dependencies [a6b01a9]
30
+ - Updated dependencies [c799cdd]
31
+ - Updated dependencies [540f8d9]
32
+ - Updated dependencies [b6daaec]
33
+ - Updated dependencies [5b9264e]
34
+ - Updated dependencies [956e88b]
35
+ - Updated dependencies [ff62281]
36
+ - Updated dependencies [d81d0ed]
37
+ - Updated dependencies [aa67cc7]
38
+ - Updated dependencies [061fa22]
39
+ - Updated dependencies [cfb9d69]
40
+ - Updated dependencies [43f7de5]
41
+ - Updated dependencies [b07bb50]
42
+ - Updated dependencies [4a3bfa1]
43
+ - Updated dependencies [bd0b626]
44
+ - Updated dependencies [a6be873]
45
+ - Updated dependencies [fb30718]
46
+ - Updated dependencies [c5528e4]
47
+ - Updated dependencies [fee5d9c]
48
+ - Updated dependencies [21f7743]
49
+ - Updated dependencies [31a0b46]
50
+ - @procedures/client@0.3.0
51
+
3
52
  ## 0.2.3
4
53
 
5
54
  ### Patch Changes
package/README.md CHANGED
@@ -15,9 +15,10 @@ SQLite database operations via client procedures. Execute queries and store stru
15
15
  - **Logging procedures**: Store and query structured application logs
16
16
  - **Migration support**: Schema versioning and migration utilities
17
17
  - **Default log database**: Pre-configured logging at `~/logs/cli/cli.db`
18
+ - **A collection storage**: `SqliteStorage` keeps a collection in a SQLite table, with a change log
18
19
 
19
- The package has 4 procedures. Its register entry (`import "@procedures/sqlite/register"`) registers them:
20
- - **Database**: `db.query`, `db.execute`
20
+ The package has 9 procedures. Its register entry (`import "@procedures/sqlite/register"`) registers them:
21
+ - **Database**: `db.query`, `db.execute`, `db.tables`, `db.schema`, `db.transaction`, `db.migrate`, `db.backup`
21
22
  - **Logs**: `logs.store`, `logs.query`
22
23
 
23
24
  ## Architecture
@@ -33,7 +34,7 @@ graph TB
33
34
  Register["register.ts<br/>(Auto-registers procedures)"]
34
35
 
35
36
  subgraph "Procedure Categories"
36
- DB["Database Procedures<br/>db.query, db.execute"]
37
+ DB["Database Procedures<br/>db.query, db.execute, db.tables, db.schema,<br/>db.transaction, db.migrate, db.backup"]
37
38
  Logs["Log Procedures<br/>logs.store, logs.query"]
38
39
  end
39
40
 
@@ -273,6 +274,65 @@ await client.exec(
273
274
  );
274
275
  ```
275
276
 
277
+ #### `db.tables`
278
+
279
+ The procedure lists the tables and the views of a database, without the internal `sqlite_` tables. Each item has the `name`, the `type` and the `sql` that made it. With `views: false`, it gives only the tables. The procedure only reads. A missing file is an error (`ENOENT`), and the procedure does not make it.
280
+
281
+ ```typescript
282
+ const { tables } = await client.exec(["db", "tables"], { dbPath: "./app.db" });
283
+ ```
284
+
285
+ #### `db.schema`
286
+
287
+ The procedure gives the `columns`, the `indexes` and the `foreignKeys` of a table, or the columns of a view. The name of the table goes to SQLite as a bound value, not as SQL text. The procedure only reads.
288
+
289
+ ```typescript
290
+ const schema = await client.exec(["db", "schema"], { dbPath: "./app.db", table: "users" });
291
+ // schema.columns: [{ name: "id", type: "INTEGER", notNull: false, defaultValue: null, primaryKey: 1 }, ...]
292
+ ```
293
+
294
+ #### `db.transaction`
295
+
296
+ The procedure puts a list of statements in one transaction. When one statement fails, it rolls back the transaction, and the file stays as it was. The `params` of a statement bind to one statement. Without `params`, one `sql` can hold several statements. A statement that gives rows (`SELECT`, or `RETURNING`) gives its `rows` in its result. The procedure refuses `BEGIN`, `COMMIT`, `ROLLBACK`, `SAVEPOINT` and `RELEASE`, because it controls the transaction itself.
297
+
298
+ ```typescript
299
+ const { results, changes } = await client.exec(["db", "transaction"], {
300
+ dbPath: "./app.db",
301
+ statements: [
302
+ { sql: "UPDATE accounts SET balance = balance - ? WHERE id = ?", params: [10, 1] },
303
+ { sql: "UPDATE accounts SET balance = balance + ? WHERE id = ?", params: [10, 2] },
304
+ { sql: "SELECT id, balance FROM accounts" },
305
+ ],
306
+ });
307
+ ```
308
+
309
+ #### `db.migrate`
310
+
311
+ The procedure applies the numbered migrations that the database does not have, in the order of their numbers. The migrations come from a `folder` or from a list (`migrations`). In a folder, each migration is a file `<number>_<name>.sql`, for example `001_create_users.sql`. The procedure ignores the files that do not end in `.sql`.
312
+
313
+ - The table `schema_migrations` (or the `table` of the input) records each applied migration and the sha256 of its SQL.
314
+ - The pending migrations are in one transaction. When one fails, the procedure applies none of them.
315
+ - A second call applies nothing, and does not write the file.
316
+ - When the SQL of an applied migration changes, the procedure refuses the call. The sha256 reads CRLF line ends as LF.
317
+ - A migration must not hold `BEGIN`, `COMMIT` or `ROLLBACK`.
318
+
319
+ ```typescript
320
+ const { applied, skipped, version } = await client.exec(["db", "migrate"], {
321
+ dbPath: "./app.db",
322
+ folder: "./migrations",
323
+ });
324
+ ```
325
+
326
+ #### `db.backup`
327
+
328
+ The procedure copies a database to a new file (`to`). It does not replace a file, and it does not write the source. With `vacuum: true`, it compacts the copy.
329
+
330
+ This package uses SQLite in WebAssembly (sql.js). The database is in the memory of the module. `VACUUM INTO` and the backup API of SQLite write into the file system of the module, not to the disk. Thus the procedure writes the image of the database (its `export()`). It takes the image in the file queue of the database, where no other call of the process changes it. It writes the image to a file next to the target, checks it with `PRAGMA integrity_check`, and then renames it.
331
+
332
+ ```typescript
333
+ const { path, bytes } = await client.exec(["db", "backup"], { dbPath: "./app.db", to: "./backups/app-2026-10-16.db" });
334
+ ```
335
+
276
336
  ### Log Procedures
277
337
 
278
338
  #### `logs.store`
@@ -425,6 +485,65 @@ const { logs } = await client.exec(
425
485
  );
426
486
  ```
427
487
 
488
+ ## The collection storage
489
+
490
+ `SqliteStorage` keeps the items of one collection in one table of a SQLite database. It implements `CollectionStorage` of `@procedures/client`. A `ProcedureServer`, an `ApiStorage` server and a `HybridStorage` use it as they use `InMemoryStorage`.
491
+
492
+ ```ts
493
+ import { SqliteStorage } from "@procedures/sqlite/storage";
494
+
495
+ const users = new SqliteStorage<User>({ path: "./data/app.db", table: "users" });
496
+ await users.set("u1", { name: "Ada" });
497
+ await users.setBatch([["u2", { name: "Bob" }], ["u3", { name: "Cy" }]]);
498
+ const changes = await users.getChangesSince(); // { version, full: true, changed, deleted: [] }
499
+ await users.close();
500
+ ```
501
+
502
+ The entry `@procedures/sqlite/storage` has the storage alone. The package root exports it too.
503
+
504
+ ### The table and the values
505
+
506
+ - The option `path` is a database file, or `:memory:` for a database that lives as long as the storage.
507
+ - The option `table` is the name of the table. It starts with a letter, and it has only letters, digits and underscores. The prefix `sqlite_` is not permitted.
508
+ - The table has the columns `id` and `value`. The value is the JSON text of the item.
509
+ - A value without JSON text, for example `undefined` or a `BigInt`, is a `TypeError`. The storage writes nothing then.
510
+ - A read gives a new copy of the item.
511
+ - The order of the items is the order of the first write of each id, as in a `Map`.
512
+
513
+ ### The change log and the versions
514
+
515
+ - `getChangesSince(version)` gives the items that changed and the ids that were deleted after a version. `HybridStorage` uses it for a delta sync.
516
+ - A version is `<epoch>:<seq>`, as in `InMemoryStorage`. The table `_storage_versions` keeps the epoch, so a version stays valid after a restart.
517
+ - The table `_storage_log` keeps the last change of each id. The option `maxTombstones` (10000 by default) limits the deletes that it keeps.
518
+ - A version that is older than the dropped deletes gives all items (`full: true`).
519
+ - Write the table only through the storage. A change from other SQL is not in the change log.
520
+
521
+ ### The transactions and the file
522
+
523
+ - Each write runs in a transaction with the change log. A batch that fails changes nothing.
524
+ - The storage keeps the database in memory, and it writes the file after each write. The write ends after the file write.
525
+ - When several writes wait, one file write holds their changes.
526
+ - A failed file write fails the write. The change stays in memory, and `flush()` or the next write writes it.
527
+ - A storage that only reads does not make the file.
528
+ - The storages of one file in one process share one database. Thus the storages of two tables in one file keep their items.
529
+ - Another process, and the procedures `db.query` and `db.execute`, do not share this database. Do not write the file from them while a storage has it open.
530
+
531
+ ### The durable local copy of `HybridStorage`
532
+
533
+ `HybridStorage` of `@procedures/store` takes the storage of its local copy in the option `local`. With a `SqliteStorage`, the local copy stays after a restart. A new storage in `local-only` mode then reads the items of the last run, without the remote.
534
+
535
+ ```ts
536
+ import { HybridStorage } from "@procedures/store";
537
+ import { FileQueueStore } from "@procedures/store/file-queue";
538
+ import { SqliteStorage } from "@procedures/sqlite/storage";
539
+
540
+ const users = new HybridStorage(remote, {
541
+ mode: "write-back",
542
+ local: new SqliteStorage({ path: "./users.db", table: "users" }),
543
+ queueStore: new FileQueueStore("./users.queue.jsonl"),
544
+ });
545
+ ```
546
+
428
547
  ## Usage Examples
429
548
 
430
549
  ### Basic Database Operations
@@ -708,7 +827,10 @@ export type {
708
827
  client-sqlite/
709
828
  ├── src/
710
829
  │ ├── types.ts # Type definitions
711
- │ ├── register.ts # Auto-registration + procedure implementations
830
+ │ ├── module.ts # The procedures as data, and the db.query, db.execute and logs.* handlers
831
+ │ ├── register.ts # The ./register entry: registers the module
832
+ │ ├── storage.ts # SqliteStorage, the collection storage (the ./storage entry)
833
+ │ ├── procedures/db/ # db.tables, db.schema, db.transaction, db.migrate, db.backup
712
834
  │ └── index.ts # Main entry point
713
835
  ├── dist/ # Compiled output
714
836
  ├── package.json