@danypops/papyrus 0.17.1 → 0.17.2
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/package.json +2 -2
- package/src/db.ts +71 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/papyrus",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -42,6 +42,6 @@
|
|
|
42
42
|
"files": ["src", "extension", "README.md"],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"beautiful-mermaid": "1.1.3",
|
|
45
|
-
"@danypops/daemon-kit": "^0.2.
|
|
45
|
+
"@danypops/daemon-kit": "^0.2.1"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/src/db.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
8
|
import { mkdirSync } from "node:fs";
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
|
+
import { runMigrations, type SqliteMigrationRunner } from "@danypops/daemon-kit/storage";
|
|
10
11
|
import { SQLITE_BUSY_TIMEOUT_MS, SQLITE_SCHEMA_VERSION } from "./constants.ts";
|
|
11
12
|
|
|
12
13
|
const require_ = createRequire(import.meta.url);
|
|
@@ -363,6 +364,47 @@ function bootstrapEmptyDatabase(db: Db): void {
|
|
|
363
364
|
ensureCoreLedger(db, false);
|
|
364
365
|
}
|
|
365
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Version the hardcoded, sequential if-chain below produces once fully applied. Frozen
|
|
369
|
+
* forever, per this same function's own "deliberately not a hand-enumerated allow-list"
|
|
370
|
+
* history below: that chain is never edited once shipped, only ever extended with a new
|
|
371
|
+
* `if` branch -- except a NEW branch is no longer how migrations beyond this version are
|
|
372
|
+
* added (see FUTURE_MIGRATIONS). The legacy chain itself stays byte-for-byte as it always
|
|
373
|
+
* was: same SQL, same single all-or-nothing transaction, same dynamic post-hoc gap check.
|
|
374
|
+
*/
|
|
375
|
+
const LEGACY_MIGRATION_CHAIN_TARGET_VERSION = 13;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* A migration beyond LEGACY_MIGRATION_CHAIN_TARGET_VERSION. Runs through @danypops/
|
|
379
|
+
* daemon-kit's generic runMigrations engine (one transaction per migration, daemon-kit's
|
|
380
|
+
* default) via dbMigrationRunner below, instead of a new branch appended to the legacy
|
|
381
|
+
* if-chain -- the exact reuse daemon-kit's storage module was refactored (v0.2.1) to allow,
|
|
382
|
+
* since Papyrus's dual bun:sqlite/node:sqlite Db abstraction could never satisfy that
|
|
383
|
+
* engine's original bun:sqlite-only signature.
|
|
384
|
+
*/
|
|
385
|
+
export interface PapyrusMigration {
|
|
386
|
+
version: number;
|
|
387
|
+
name: string;
|
|
388
|
+
up: (db: Db) => void;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Currently empty -- no schema version beyond LEGACY_MIGRATION_CHAIN_TARGET_VERSION exists yet. The next migration is appended here, never as a new branch in the legacy chain above. */
|
|
392
|
+
const FUTURE_MIGRATIONS: ReadonlyArray<PapyrusMigration> = [];
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Adapts Papyrus's own Db/inTransaction to daemon-kit's storage-agnostic
|
|
396
|
+
* SqliteMigrationRunner port, so its runMigrations engine (written against bun:sqlite's
|
|
397
|
+
* concrete Database) runs unmodified against Papyrus's dual-runtime Db abstraction instead.
|
|
398
|
+
*/
|
|
399
|
+
export function dbMigrationRunner(db: Db): SqliteMigrationRunner<Db> {
|
|
400
|
+
return {
|
|
401
|
+
raw: db,
|
|
402
|
+
userVersion: () => schemaVersion(db),
|
|
403
|
+
setUserVersion: (version) => db.exec(`PRAGMA user_version = ${version}`),
|
|
404
|
+
transaction: (fn) => inTransaction(db, fn),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
366
408
|
export function migrateDb(db: Db): MigrationResult {
|
|
367
409
|
const from = schemaVersion(db);
|
|
368
410
|
if (from > SQLITE_SCHEMA_VERSION) {
|
|
@@ -378,11 +420,17 @@ export function migrateDb(db: Db): MigrationResult {
|
|
|
378
420
|
// migrating any already-deployed database sitting at schema 8, 9, or 10 (including the real
|
|
379
421
|
// production database at the time this was found) would have thrown "no explicit migration
|
|
380
422
|
// path" before ever reaching the migration chain below. Checked dynamically after the chain
|
|
381
|
-
// runs instead: if schemaVersion(db) hasn't reached
|
|
382
|
-
// `schemaVersion(db) === N` step below has had its chance to fire, `from` was never a
|
|
383
|
-
// starting point (a genuine gap in the chain) -- structurally cannot drift out of sync
|
|
384
|
-
// way a separate, parallel enumeration did.
|
|
385
|
-
|
|
423
|
+
// runs instead: if schemaVersion(db) hasn't reached LEGACY_MIGRATION_CHAIN_TARGET_VERSION once
|
|
424
|
+
// every `schemaVersion(db) === N` step below has had its chance to fire, `from` was never a
|
|
425
|
+
// valid starting point (a genuine gap in the chain) -- structurally cannot drift out of sync
|
|
426
|
+
// the way a separate, parallel enumeration did.
|
|
427
|
+
//
|
|
428
|
+
// Guarded by `from < LEGACY_MIGRATION_CHAIN_TARGET_VERSION`: a database already at or past
|
|
429
|
+
// that version (only reachable once FUTURE_MIGRATIONS below has entries) must skip this
|
|
430
|
+
// entire frozen chain, not merely fail to match any of its branches -- entering it and
|
|
431
|
+
// falling through to the final gap check would otherwise misreport a database correctly
|
|
432
|
+
// mid-way through FUTURE_MIGRATIONS as "no explicit migration path".
|
|
433
|
+
if (from < LEGACY_MIGRATION_CHAIN_TARGET_VERSION) inTransaction(db, () => {
|
|
386
434
|
if (schemaVersion(db) === 1) {
|
|
387
435
|
db.exec(`
|
|
388
436
|
INSERT OR IGNORE INTO statuses VALUES ('todo','task');
|
|
@@ -661,9 +709,25 @@ export function migrateDb(db: Db): MigrationResult {
|
|
|
661
709
|
`);
|
|
662
710
|
applied.push("session-identity");
|
|
663
711
|
}
|
|
664
|
-
if (schemaVersion(db) !==
|
|
712
|
+
if (schemaVersion(db) !== LEGACY_MIGRATION_CHAIN_TARGET_VERSION) throw new Error(`no explicit migration path from database schema ${from}`);
|
|
665
713
|
});
|
|
666
|
-
|
|
714
|
+
|
|
715
|
+
// Guarded by length: runMigrations treats an empty migrations array's "target version" as
|
|
716
|
+
// 0 (see its own sorted.at(-1) ?? 0), which would misreport a database the legacy chain
|
|
717
|
+
// already advanced past 0 as a downgrade. FUTURE_MIGRATIONS is empty only when there is
|
|
718
|
+
// nothing beyond LEGACY_MIGRATION_CHAIN_TARGET_VERSION to apply -- exactly the case where
|
|
719
|
+
// skipping the call is correct, not merely convenient.
|
|
720
|
+
if (FUTURE_MIGRATIONS.length > 0) {
|
|
721
|
+
const beforeFuture = schemaVersion(db);
|
|
722
|
+
runMigrations(dbMigrationRunner(db), [...FUTURE_MIGRATIONS]);
|
|
723
|
+
const afterFuture = schemaVersion(db);
|
|
724
|
+
for (const migration of FUTURE_MIGRATIONS) {
|
|
725
|
+
if (migration.version > beforeFuture && migration.version <= afterFuture) applied.push(migration.name);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (schemaVersion(db) !== SQLITE_SCHEMA_VERSION) throw new Error(`no explicit migration path from database schema ${from}`);
|
|
730
|
+
ensureCoreLedger(db, true);
|
|
667
731
|
return { from, to: schemaVersion(db), applied };
|
|
668
732
|
}
|
|
669
733
|
|