@procedures/sqlite 0.3.0 → 0.5.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 (52) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +93 -32
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/engines.d.ts +68 -0
  5. package/dist/engines.d.ts.map +1 -0
  6. package/dist/engines.js +268 -0
  7. package/dist/engines.js.map +1 -0
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -4
  11. package/dist/index.js.map +1 -1
  12. package/dist/module.d.ts +13 -3
  13. package/dist/module.d.ts.map +1 -1
  14. package/dist/module.js +60 -27
  15. package/dist/module.js.map +1 -1
  16. package/dist/procedures/db/backup.d.ts +1 -1
  17. package/dist/procedures/db/backup.js +6 -6
  18. package/dist/procedures/db/backup.js.map +1 -1
  19. package/dist/procedures/db/migrate.d.ts +1 -1
  20. package/dist/procedures/db/migrate.js +8 -8
  21. package/dist/procedures/db/migrate.js.map +1 -1
  22. package/dist/procedures/db/schema.d.ts +1 -1
  23. package/dist/procedures/db/schema.js +3 -3
  24. package/dist/procedures/db/schema.js.map +1 -1
  25. package/dist/procedures/db/shared.d.ts +1 -1
  26. package/dist/procedures/db/shared.js +1 -1
  27. package/dist/procedures/db/tables.d.ts +1 -1
  28. package/dist/procedures/db/tables.js +2 -2
  29. package/dist/procedures/db/tables.js.map +1 -1
  30. package/dist/procedures/db/transaction.d.ts +1 -1
  31. package/dist/procedures/db/transaction.js +2 -2
  32. package/dist/procedures/db/transaction.js.map +1 -1
  33. package/dist/storage.d.ts +57 -16
  34. package/dist/storage.d.ts.map +1 -1
  35. package/dist/storage.js +255 -148
  36. package/dist/storage.js.map +1 -1
  37. package/dist/types.d.ts +1 -1
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +2 -2
  40. package/dist/types.js.map +1 -1
  41. package/package.json +6 -5
  42. package/src/engines.ts +360 -0
  43. package/src/index.ts +12 -4
  44. package/src/module.ts +65 -28
  45. package/src/procedures/db/backup.ts +6 -6
  46. package/src/procedures/db/migrate.ts +8 -8
  47. package/src/procedures/db/schema.ts +3 -3
  48. package/src/procedures/db/shared.ts +1 -1
  49. package/src/procedures/db/tables.ts +2 -2
  50. package/src/procedures/db/transaction.ts +2 -2
  51. package/src/storage.ts +335 -176
  52. package/src/types.ts +4 -4
package/src/module.ts CHANGED
@@ -3,12 +3,14 @@
3
3
  *
4
4
  * Procedure Registration for SQLite
5
5
  *
6
- * Registers database and logging procedures with the client system: db.query, db.execute,
7
- * db.tables, db.schema, db.transaction, db.migrate, db.backup, logs.store and logs.query.
6
+ * The module has the database and logging procedures: sqlite.query, sqlite.execute,
7
+ * sqlite.tables, sqlite.schema, sqlite.transaction, sqlite.migrate, sqlite.backup, logs.store
8
+ * and logs.query. The old names db.* stay for one release as deprecated aliases (refer to
9
+ * `deprecatedDbAlias`).
8
10
  * This file is referenced by package.json's client.procedures field.
9
11
  */
10
12
 
11
- import { createProcedure, zodAdapter, defineModule, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
13
+ import { createProcedure, zodAdapter, defineModule, type AnyProcedure, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
12
14
  import { z } from "zod";
13
15
  import {
14
16
  withConnection,
@@ -64,10 +66,11 @@ import {
64
66
  // Real schemas (roadmap 2.1): the input is validated before the handler, and the MCP tool list
65
67
  // and the help of mark describe each field. zodAdapter<T> checks each schema against its type.
66
68
 
69
+ // The description is the same on each computer: it does not show the home folder of this one
67
70
  const dbPathField = z
68
71
  .string()
69
72
  .optional()
70
- .describe(`The SQLite database file (default: ${DEFAULT_DB_PATH}). A query does not make a missing file.`);
73
+ .describe("The SQLite database file (default: the CLI log database, ~/logs/cli/cli.db). A query does not make a missing file.");
71
74
  const paramsField = z
72
75
  .array(z.unknown())
73
76
  .optional()
@@ -190,11 +193,11 @@ async function ensureLogsTable(dbPath: string): Promise<void> {
190
193
  }
191
194
 
192
195
  // =============================================================================
193
- // db.query Procedure
196
+ // sqlite.query Procedure
194
197
  // =============================================================================
195
198
 
196
- const dbQueryProcedure = createProcedure()
197
- .path(["db", "query"])
199
+ const sqliteQueryProcedure = createProcedure()
200
+ .path(["sqlite", "query"])
198
201
  .input(dbQueryInputSchema)
199
202
  .output(dbQueryOutputSchema)
200
203
  .meta({
@@ -219,11 +222,11 @@ const dbQueryProcedure = createProcedure()
219
222
  .build();
220
223
 
221
224
  // =============================================================================
222
- // db.execute Procedure
225
+ // sqlite.execute Procedure
223
226
  // =============================================================================
224
227
 
225
- const dbExecuteProcedure = createProcedure()
226
- .path(["db", "execute"])
228
+ const sqliteExecuteProcedure = createProcedure()
229
+ .path(["sqlite", "execute"])
227
230
  .input(dbExecuteInputSchema)
228
231
  .output(dbExecuteOutputSchema)
229
232
  .meta({
@@ -412,11 +415,11 @@ const logsQueryProcedure = createProcedure()
412
415
  .build();
413
416
 
414
417
  // =============================================================================
415
- // db.tables, db.schema, db.transaction, db.migrate and db.backup (roadmap R44)
418
+ // sqlite.tables, sqlite.schema, sqlite.transaction, sqlite.migrate and sqlite.backup (roadmap R44)
416
419
  // =============================================================================
417
420
 
418
- const dbTablesProcedure = createProcedure()
419
- .path(["db", "tables"])
421
+ const sqliteTablesProcedure = createProcedure()
422
+ .path(["sqlite", "tables"])
420
423
  .input(zodAdapter<DbTablesInput>(DbTablesInputSchema))
421
424
  .output(zodAdapter<DbTablesOutput>(DbTablesOutputSchema))
422
425
  .meta({
@@ -429,8 +432,8 @@ const dbTablesProcedure = createProcedure()
429
432
  .handler(async (input: DbTablesInput): Promise<DbTablesOutput> => dbTables(input))
430
433
  .build();
431
434
 
432
- const dbSchemaProcedure = createProcedure()
433
- .path(["db", "schema"])
435
+ const sqliteSchemaProcedure = createProcedure()
436
+ .path(["sqlite", "schema"])
434
437
  .input(zodAdapter<DbSchemaInput>(DbSchemaInputSchema))
435
438
  .output(zodAdapter<DbSchemaOutput>(DbSchemaOutputSchema))
436
439
  .meta({
@@ -443,8 +446,8 @@ const dbSchemaProcedure = createProcedure()
443
446
  .handler(async (input: DbSchemaInput): Promise<DbSchemaOutput> => dbSchema(input))
444
447
  .build();
445
448
 
446
- const dbTransactionProcedure = createProcedure()
447
- .path(["db", "transaction"])
449
+ const sqliteTransactionProcedure = createProcedure()
450
+ .path(["sqlite", "transaction"])
448
451
  .input(zodAdapter<DbTransactionInput>(DbTransactionInputSchema))
449
452
  .output(zodAdapter<DbTransactionOutput>(DbTransactionOutputSchema))
450
453
  .meta({
@@ -458,8 +461,8 @@ const dbTransactionProcedure = createProcedure()
458
461
  .handler(async (input: DbTransactionInput): Promise<DbTransactionOutput> => dbTransaction(input))
459
462
  .build();
460
463
 
461
- const dbMigrateProcedure = createProcedure()
462
- .path(["db", "migrate"])
464
+ const sqliteMigrateProcedure = createProcedure()
465
+ .path(["sqlite", "migrate"])
463
466
  .input(zodAdapter<DbMigrateInput>(DbMigrateInputSchema))
464
467
  .output(zodAdapter<DbMigrateOutput>(DbMigrateOutputSchema))
465
468
  .meta({
@@ -473,8 +476,8 @@ const dbMigrateProcedure = createProcedure()
473
476
  .handler(async (input: DbMigrateInput): Promise<DbMigrateOutput> => dbMigrate(input))
474
477
  .build();
475
478
 
476
- const dbBackupProcedure = createProcedure()
477
- .path(["db", "backup"])
479
+ const sqliteBackupProcedure = createProcedure()
480
+ .path(["sqlite", "backup"])
478
481
  .input(zodAdapter<DbBackupInput>(DbBackupInputSchema))
479
482
  .output(zodAdapter<DbBackupOutput>(DbBackupOutputSchema))
480
483
  .meta({
@@ -488,6 +491,44 @@ const dbBackupProcedure = createProcedure()
488
491
  .handler(async (input: DbBackupInput): Promise<DbBackupOutput> => dbBackup(input))
489
492
  .build();
490
493
 
494
+ // =============================================================================
495
+ // The deprecated db.* aliases (one release)
496
+ // =============================================================================
497
+
498
+ /** The procedures of the `sqlite` namespace. Each one has a deprecated alias in `db`. */
499
+ const sqliteProcedures: readonly AnyProcedure[] = [
500
+ sqliteQueryProcedure,
501
+ sqliteExecuteProcedure,
502
+ sqliteTablesProcedure,
503
+ sqliteSchemaProcedure,
504
+ sqliteTransactionProcedure,
505
+ sqliteMigrateProcedure,
506
+ sqliteBackupProcedure,
507
+ ];
508
+
509
+ /**
510
+ * This function gives the deprecated alias `db.<name>` of the procedure `sqlite.<name>`. The alias
511
+ * has the same schemas, handler and effects. Its description names the new procedure. The aliases
512
+ * go away in the next release. The MCP server does not expose them (`bundle-mcp`).
513
+ */
514
+ export function deprecatedDbAlias(procedure: AnyProcedure): AnyProcedure {
515
+ const name = procedure.path.slice(1).join(".");
516
+ const description = typeof procedure.metadata.description === "string" ? ` ${procedure.metadata.description}` : "";
517
+ return {
518
+ ...procedure,
519
+ path: ["db", ...procedure.path.slice(1)],
520
+ metadata: {
521
+ ...procedure.metadata,
522
+ description: `Deprecated: use sqlite.${name}.${description}`,
523
+ deprecated: true,
524
+ deprecationMessage: `Use sqlite.${name}. The alias db.${name} goes away in the next release.`,
525
+ },
526
+ };
527
+ }
528
+
529
+ /** The deprecated aliases `db.query`, `db.execute`, `db.tables`, `db.schema`, `db.transaction`, `db.migrate` and `db.backup`. */
530
+ export const dbAliasProcedures: readonly AnyProcedure[] = sqliteProcedures.map(deprecatedDbAlias);
531
+
491
532
  // =============================================================================
492
533
  // Registration
493
534
  // =============================================================================
@@ -497,15 +538,11 @@ const dbBackupProcedure = createProcedure()
497
538
  * file registers nothing. (`register.ts` registers the same procedures in the global registry.)
498
539
  */
499
540
  export const sqliteModule: ProcedureModule = defineModule("@procedures/sqlite", [
500
- dbQueryProcedure,
501
- dbExecuteProcedure,
541
+ ...sqliteProcedures,
502
542
  logsStoreProcedure,
503
543
  logsQueryProcedure,
504
- dbTablesProcedure,
505
- dbSchemaProcedure,
506
- dbTransactionProcedure,
507
- dbMigrateProcedure,
508
- dbBackupProcedure,
544
+ // The old names, for one release
545
+ ...dbAliasProcedures,
509
546
  ]);
510
547
 
511
548
  export default sqliteModule;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * db.backup procedure (roadmap R44)
2
+ * sqlite.backup procedure (roadmap R44)
3
3
  *
4
4
  * A copy of a database in a new file. The package runs SQLite in WebAssembly (sql.js): the
5
5
  * database is in the memory of the module, and `VACUUM INTO` and the backup API write into the
@@ -30,13 +30,13 @@ function fileKey(path: string): string {
30
30
  */
31
31
  export async function dbBackup(input: DbBackupInput): Promise<DbBackupOutput> {
32
32
  const source = databasePath(input.dbPath);
33
- requireDatabaseFile("db.backup", source);
33
+ requireDatabaseFile("sqlite.backup", source);
34
34
  const target = resolve(input.to);
35
35
  if (source !== ":memory:" && fileKey(source) === fileKey(target)) {
36
- throw new Error("db.backup: the copy cannot be the database file itself");
36
+ throw new Error("sqlite.backup: the copy cannot be the database file itself");
37
37
  }
38
38
  if (existsSync(target)) {
39
- const error = new Error(`db.backup: the file exists already: ${target}. The procedure does not replace a file.`) as NodeJS.ErrnoException;
39
+ const error = new Error(`sqlite.backup: the file exists already: ${target}. The procedure does not replace a file.`) as NodeJS.ErrnoException;
40
40
  error.code = "EEXIST";
41
41
  throw error;
42
42
  }
@@ -51,8 +51,8 @@ export async function dbBackup(input: DbBackupInput): Promise<DbBackupOutput> {
51
51
  }
52
52
  const check = await withConnection({ dbPath: temporary }, (db) => query<{ integrity_check: string }>(db, "PRAGMA integrity_check").rows);
53
53
  const verdict = check.map((row) => row.integrity_check).join("; ");
54
- if (verdict !== "ok") throw new Error(`db.backup: the copy is not valid: ${verdict}`);
55
- if (existsSync(target)) throw new Error(`db.backup: the file exists already: ${target}. The procedure does not replace a file.`);
54
+ if (verdict !== "ok") throw new Error(`sqlite.backup: the copy is not valid: ${verdict}`);
55
+ if (existsSync(target)) throw new Error(`sqlite.backup: the file exists already: ${target}. The procedure does not replace a file.`);
56
56
  await rename(temporary, target);
57
57
  } catch (error) {
58
58
  await unlink(temporary).catch(() => undefined);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * db.migrate procedure (roadmap R44)
2
+ * sqlite.migrate procedure (roadmap R44)
3
3
  *
4
4
  * The procedure applies the numbered migrations that the database does not have, in the order
5
5
  * of their numbers. The migrations come from a folder (files named `<number>_<name>.sql`) or
@@ -44,7 +44,7 @@ export async function migrationsOfFolder(folder: string): Promise<DbMigration[]>
44
44
  for (const entry of entries) {
45
45
  if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".sql")) continue;
46
46
  const match = MIGRATION_FILE.exec(entry.name);
47
- if (!match) throw new Error(`db.migrate: the file ${entry.name} has no number: name it <number>_<name>.sql`);
47
+ if (!match) throw new Error(`sqlite.migrate: the file ${entry.name} has no number: name it <number>_<name>.sql`);
48
48
  migrations.push({ version: Number(match[1]), name: match[2]!, sql: await readFile(join(folder, entry.name), "utf8") });
49
49
  }
50
50
  return migrations;
@@ -55,9 +55,9 @@ function ordered(migrations: DbMigration[]): Migration[] {
55
55
  const byVersion = new Map<number, Migration>();
56
56
  for (const migration of migrations) {
57
57
  if (!Number.isSafeInteger(migration.version) || migration.version < 1) {
58
- throw new Error(`db.migrate: the version ${migration.version} is not a whole number of 1 or more`);
58
+ throw new Error(`sqlite.migrate: the version ${migration.version} is not a whole number of 1 or more`);
59
59
  }
60
- if (byVersion.has(migration.version)) throw new Error(`db.migrate: two migrations have the version ${migration.version}`);
60
+ if (byVersion.has(migration.version)) throw new Error(`sqlite.migrate: two migrations have the version ${migration.version}`);
61
61
  const name = migration.name ?? `migration ${migration.version}`;
62
62
  byVersion.set(migration.version, { version: migration.version, name, sql: migration.sql, checksum: checksum(migration.sql) });
63
63
  }
@@ -69,10 +69,10 @@ function ordered(migrations: DbMigration[]): Migration[] {
69
69
  */
70
70
  export async function dbMigrate(input: DbMigrateInput): Promise<DbMigrateOutput> {
71
71
  if ((input.folder === undefined) === (input.migrations === undefined)) {
72
- throw new Error("db.migrate: give exactly one of folder or migrations");
72
+ throw new Error("sqlite.migrate: give exactly one of folder or migrations");
73
73
  }
74
74
  const table = input.table ?? DEFAULT_MIGRATION_TABLE;
75
- if (!MIGRATION_TABLE_PATTERN.test(table)) throw new Error(`db.migrate: the table name ${JSON.stringify(table)} is not valid`);
75
+ if (!MIGRATION_TABLE_PATTERN.test(table)) throw new Error(`sqlite.migrate: the table name ${JSON.stringify(table)} is not valid`);
76
76
  const migrations = ordered(input.folder !== undefined ? await migrationsOfFolder(input.folder) : input.migrations!);
77
77
 
78
78
  return withConnection({ dbPath: databasePath(input.dbPath) }, (db) => {
@@ -87,7 +87,7 @@ export async function dbMigrate(input: DbMigrateInput): Promise<DbMigrateOutput>
87
87
  for (const migration of migrations) {
88
88
  const known = applied.get(migration.version);
89
89
  if (known !== undefined && known !== migration.checksum) {
90
- throw new Error(`db.migrate: the migration ${migration.version} (${migration.name}) changed after it was applied: its sha256 is different`);
90
+ throw new Error(`sqlite.migrate: the migration ${migration.version} (${migration.name}) changed after it was applied: its sha256 is different`);
91
91
  }
92
92
  }
93
93
  const pending = migrations.filter((migration) => !applied.has(migration.version));
@@ -97,7 +97,7 @@ export async function dbMigrate(input: DbMigrateInput): Promise<DbMigrateOutput>
97
97
  runStatements(db, migration.sql, [], label);
98
98
  } catch (error) {
99
99
  const message = error instanceof Error ? error.message : String(error);
100
- throw new Error(message.startsWith(label) ? `db.migrate: ${message}` : `db.migrate: ${label} failed: ${message}`);
100
+ throw new Error(message.startsWith(label) ? `sqlite.migrate: ${message}` : `sqlite.migrate: ${label} failed: ${message}`);
101
101
  }
102
102
  db.run(`INSERT INTO "${table}" (version, name, checksum) VALUES (?, ?, ?)`, [migration.version, migration.name, migration.checksum]);
103
103
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * db.schema procedure (roadmap R44)
2
+ * sqlite.schema procedure (roadmap R44)
3
3
  *
4
4
  * The columns, the indexes and the foreign keys of a table (or the columns of a view). The
5
5
  * procedure reads them with the table-valued PRAGMA functions, so the name of the table is a
@@ -38,7 +38,7 @@ interface ForeignKeyRow {
38
38
  */
39
39
  export async function dbSchema(input: DbSchemaInput): Promise<DbSchemaOutput> {
40
40
  const path = databasePath(input.dbPath);
41
- requireDatabaseFile("db.schema", path);
41
+ requireDatabaseFile("sqlite.schema", path);
42
42
  return withConnection({ dbPath: path }, (db) => {
43
43
  db.run("PRAGMA query_only = 1");
44
44
  const found = query<{ type: "table" | "view"; sql: string | null }>(
@@ -46,7 +46,7 @@ export async function dbSchema(input: DbSchemaInput): Promise<DbSchemaOutput> {
46
46
  "SELECT type, sql FROM sqlite_master WHERE name = ? AND type IN ('table', 'view')",
47
47
  [input.table],
48
48
  ).rows[0];
49
- if (!found) throw new Error(`db.schema: no table or view named ${JSON.stringify(input.table)}`);
49
+ if (!found) throw new Error(`sqlite.schema: no table or view named ${JSON.stringify(input.table)}`);
50
50
 
51
51
  const columns: DbColumnInfo[] = query<ColumnRow>(db, "SELECT name, type, \"notnull\", dflt_value, pk FROM pragma_table_info(?) ORDER BY cid", [input.table]).rows.map(
52
52
  (row) => ({ name: row.name, type: row.type, notNull: row.notnull === 1, defaultValue: row.dflt_value, primaryKey: row.pk }),
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The helpers of the db.* procedures of roadmap R44.
2
+ * The helpers of the sqlite.* procedures of roadmap R44.
3
3
  */
4
4
 
5
5
  import { existsSync } from "node:fs";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * db.tables procedure (roadmap R44)
2
+ * sqlite.tables procedure (roadmap R44)
3
3
  *
4
4
  * The tables and the views of a database, without the internal `sqlite_` tables. The connection
5
5
  * is read only (PRAGMA query_only), and a missing file is an error: it does not make one.
@@ -14,7 +14,7 @@ import { databasePath, requireDatabaseFile } from "./shared.js";
14
14
  */
15
15
  export async function dbTables(input: DbTablesInput): Promise<DbTablesOutput> {
16
16
  const path = databasePath(input.dbPath);
17
- requireDatabaseFile("db.tables", path);
17
+ requireDatabaseFile("sqlite.tables", path);
18
18
  const types = input.views === false ? "('table')" : "('table', 'view')";
19
19
  return withConnection({ dbPath: path }, (db) => {
20
20
  db.run("PRAGMA query_only = 1");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * db.transaction procedure (roadmap R44)
2
+ * sqlite.transaction procedure (roadmap R44)
3
3
  *
4
4
  * The statements of the input run in order in one transaction. When one fails, the procedure
5
5
  * rolls back the transaction and does not save the file: the database stays as it was. A
@@ -20,7 +20,7 @@ export async function dbTransaction(input: DbTransactionInput): Promise<DbTransa
20
20
  const results: DbStatementResult[] = [];
21
21
  try {
22
22
  input.statements.forEach((statement, index) => {
23
- const label = `db.transaction: statements[${index}]`;
23
+ const label = `sqlite.transaction: statements[${index}]`;
24
24
  try {
25
25
  results.push(runStatements(db, statement.sql, statement.params ?? [], label));
26
26
  } catch (error) {