@tensorix/metahub 0.1.9 → 0.1.11

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 (93) hide show
  1. package/README.md +19 -6
  2. package/README.zh-CN.md +7 -2
  3. package/dist/cli.js +4130 -2200
  4. package/dist/cli.js.map +54 -46
  5. package/dist/core/compact.d.ts +42 -0
  6. package/dist/core/compact.d.ts.map +1 -0
  7. package/dist/core/config.d.ts +3 -3
  8. package/dist/core/config.d.ts.map +1 -1
  9. package/dist/core/context.d.ts +4 -4
  10. package/dist/core/context.d.ts.map +1 -1
  11. package/dist/core/crdt.d.ts +30 -7
  12. package/dist/core/crdt.d.ts.map +1 -1
  13. package/dist/core/databases.d.ts +22 -8
  14. package/dist/core/databases.d.ts.map +1 -1
  15. package/dist/core/db.d.ts +1 -27
  16. package/dist/core/db.d.ts.map +1 -1
  17. package/dist/core/documents.d.ts +30 -15
  18. package/dist/core/documents.d.ts.map +1 -1
  19. package/dist/core/driver.d.ts +25 -0
  20. package/dist/core/driver.d.ts.map +1 -0
  21. package/dist/core/errors.d.ts +32 -0
  22. package/dist/core/errors.d.ts.map +1 -0
  23. package/dist/core/history.d.ts +174 -0
  24. package/dist/core/history.d.ts.map +1 -0
  25. package/dist/core/hlc.d.ts +3 -3
  26. package/dist/core/hlc.d.ts.map +1 -1
  27. package/dist/core/index.d.ts +2 -0
  28. package/dist/core/index.d.ts.map +1 -1
  29. package/dist/core/indexing.d.ts +4 -4
  30. package/dist/core/indexing.d.ts.map +1 -1
  31. package/dist/core/integrity.d.ts +1 -1
  32. package/dist/core/integrity.d.ts.map +1 -1
  33. package/dist/core/node.d.ts +2 -2
  34. package/dist/core/node.d.ts.map +1 -1
  35. package/dist/core/properties.d.ts +10 -9
  36. package/dist/core/properties.d.ts.map +1 -1
  37. package/dist/core/records.d.ts +13 -8
  38. package/dist/core/records.d.ts.map +1 -1
  39. package/dist/core/resolve.d.ts +4 -4
  40. package/dist/core/resolve.d.ts.map +1 -1
  41. package/dist/core/schema-init.d.ts +37 -0
  42. package/dist/core/schema-init.d.ts.map +1 -0
  43. package/dist/core/schema.d.ts +1 -1
  44. package/dist/core/schema.d.ts.map +1 -1
  45. package/dist/core/search.d.ts +3 -3
  46. package/dist/core/search.d.ts.map +1 -1
  47. package/dist/core/sites-core.d.ts +60 -0
  48. package/dist/core/sites-core.d.ts.map +1 -0
  49. package/dist/core/sites.d.ts +11 -52
  50. package/dist/core/sites.d.ts.map +1 -1
  51. package/dist/core/snapshot.d.ts.map +1 -1
  52. package/dist/core/sqlcols.d.ts +4 -0
  53. package/dist/core/sqlcols.d.ts.map +1 -0
  54. package/dist/core/sync/auth.d.ts +5 -3
  55. package/dist/core/sync/auth.d.ts.map +1 -1
  56. package/dist/core/sync/client.d.ts +11 -3
  57. package/dist/core/sync/client.d.ts.map +1 -1
  58. package/dist/core/sync/files.d.ts.map +1 -1
  59. package/dist/core/sync/openapi.d.ts +5 -2
  60. package/dist/core/sync/openapi.d.ts.map +1 -1
  61. package/dist/core/sync/pairing.d.ts.map +1 -1
  62. package/dist/core/sync/peers-routes.d.ts +1 -1
  63. package/dist/core/sync/peers-routes.d.ts.map +1 -1
  64. package/dist/core/sync/peers.d.ts +10 -10
  65. package/dist/core/sync/peers.d.ts.map +1 -1
  66. package/dist/core/sync/protocol.d.ts +5 -0
  67. package/dist/core/sync/protocol.d.ts.map +1 -1
  68. package/dist/core/sync/routes.d.ts +4 -0
  69. package/dist/core/sync/routes.d.ts.map +1 -1
  70. package/dist/core/sync/server.d.ts +24 -1
  71. package/dist/core/sync/server.d.ts.map +1 -1
  72. package/dist/core/sync/sites-routes.d.ts +1 -1
  73. package/dist/core/sync/sites-routes.d.ts.map +1 -1
  74. package/dist/db-worker.js +268 -0
  75. package/dist/index.d.ts +2 -0
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +3254 -1865
  78. package/dist/index.js.map +36 -31
  79. package/dist/metahub-sdk.js +1 -0
  80. package/dist/mh-runtime.js +1 -0
  81. package/dist/sqlite3.wasm +0 -0
  82. package/dist/sw.js +1 -0
  83. package/dist/{core/sync/webui.d.ts → webui/server/assets.d.ts} +9 -6
  84. package/dist/webui/server/assets.d.ts.map +1 -0
  85. package/dist/webui/server/icons.d.ts +4 -0
  86. package/dist/webui/server/icons.d.ts.map +1 -0
  87. package/dist/webui/server/routes.d.ts +3 -0
  88. package/dist/webui/server/routes.d.ts.map +1 -0
  89. package/dist/webui.js +57 -57
  90. package/package.json +3 -1
  91. package/dist/core/sync/webui-routes.d.ts +0 -3
  92. package/dist/core/sync/webui-routes.d.ts.map +0 -1
  93. package/dist/core/sync/webui.d.ts.map +0 -1
@@ -0,0 +1,42 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export interface CompactResult {
3
+ /** HLC cutoff actually used — history at or before this collapses to a baseline. */
4
+ cutoff: string;
5
+ deleted_changes: number;
6
+ kept_changes: number;
7
+ blobs_deleted: number;
8
+ blob_bytes_freed: number;
9
+ db_bytes_before: number;
10
+ db_bytes_after: number;
11
+ dry_run: boolean;
12
+ }
13
+ export interface CompactOptions {
14
+ /** Keep full history for this many days; 0 keeps only the head state. */
15
+ keepDays: number;
16
+ /** Report what would be removed without changing anything. */
17
+ dryRun?: boolean;
18
+ /** Run VACUUM afterwards to return the freed pages to the filesystem. */
19
+ vacuum?: boolean;
20
+ /** Test hook: "now" in epoch millis. */
21
+ now?: number;
22
+ }
23
+ export declare function compactOplog(db: Database, opts: CompactOptions): CompactResult;
24
+ /**
25
+ * Delete cache blobs no longer referenced by any remaining oplog value or
26
+ * materialized site file. Conservative: only files named like a sha256 hash
27
+ * are candidates; anything else in the cache dir is left alone.
28
+ */
29
+ export declare function gcBlobs(db: Database, opts?: {
30
+ dryRun?: boolean;
31
+ }): {
32
+ deleted: number;
33
+ bytes: number;
34
+ };
35
+ /** Dry-run quick stats for doctor: total oplog rows and how many compaction
36
+ * with the given window would remove. */
37
+ export declare function compactEstimate(db: Database, keepDays: number, now?: number): {
38
+ total_changes: number;
39
+ compactable_changes: number;
40
+ db_bytes: number;
41
+ };
42
+ //# sourceMappingURL=compact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compact.d.ts","sourceRoot":"","sources":["../../src/core/compact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAsB3C,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAqBD,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,GAAG,aAAa,CAkD9E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA0CpC;AAED;0CAC0C;AAC1C,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,GAAG,SAAa,GACf;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAa1E"}
@@ -1,4 +1,4 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  export interface ServerConfig {
3
3
  host: string;
4
4
  port: number;
@@ -8,7 +8,7 @@ export interface ServerConfig {
8
8
  }
9
9
  export declare const DEFAULT_CONFIG: ServerConfig;
10
10
  /** Persisted server config, falling back to DEFAULT_CONFIG per field. */
11
- export declare function getServerConfig(db: Database): ServerConfig;
11
+ export declare function getServerConfig(db: DbDriver): ServerConfig;
12
12
  /** Persist a partial config update. Only provided fields are written. */
13
- export declare function setServerConfig(db: Database, partial: Partial<ServerConfig>): ServerConfig;
13
+ export declare function setServerConfig(db: DbDriver, partial: Partial<ServerConfig>): ServerConfig;
14
14
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQ3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC;AAeF,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAW1D;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAO1F"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,cAAc,EAAE,YAK5B,CAAC;AAeF,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAW1D;AAED,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAO1F"}
@@ -1,11 +1,11 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  import { type DatabaseRow } from "./databases.ts";
3
3
  /**
4
4
  * The current database, or null. Validated on read: if it points at a database
5
5
  * that no longer exists (deleted, or gone after a snapshot restore), the stale
6
6
  * pointer is cleared and null returned — no coupling to delete/restore paths.
7
7
  */
8
- export declare function getCurrentDatabase(db: Database): DatabaseRow | null;
9
- export declare function setCurrentDatabase(db: Database, id: string): void;
10
- export declare function clearCurrentDatabase(db: Database): void;
8
+ export declare function getCurrentDatabase(db: DbDriver): DatabaseRow | null;
9
+ export declare function setCurrentDatabase(db: DbDriver, id: string): void;
10
+ export declare function clearCurrentDatabase(db: DbDriver): void;
11
11
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO/D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,GAAG,IAAI,CAWnE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAIjE;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAEvD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO/D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,GAAG,IAAI,CAWnE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAIjE;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAEvD"}
@@ -1,4 +1,4 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  export interface Change {
3
3
  hlc: string;
4
4
  node_id: string;
@@ -6,30 +6,53 @@ export interface Change {
6
6
  row_id: string;
7
7
  col: string;
8
8
  value: string | null;
9
+ txn?: string | null;
9
10
  }
11
+ /** Oplog SELECT list, locked to the Change interface (also reused by history). */
12
+ export declare const CHANGE_COLS: readonly ["hlc", "node_id", "dataset", "row_id", "col", "value", "txn"];
13
+ export declare const CHANGE_SELECT: string;
14
+ /**
15
+ * Run `fn` with all emits stamped with one shared txn id. Nested calls keep the
16
+ * outermost group (a revert that calls updateDocument is ONE revision). `label`
17
+ * prefixes the id ("repair:", "revert:") so history can classify the source.
18
+ */
19
+ export declare function withChangeGroup<T>(label: string | null, fn: () => T): T;
20
+ /** Wrap a mutator so its body runs inside one change group. */
21
+ export declare function grouped<A extends unknown[], R>(fn: (...args: A) => R, label?: string | null): (...args: A) => R;
10
22
  /**
11
23
  * Record a change in the oplog (idempotent) and, if it is the latest write for
12
24
  * its register, materialize it. Order-independent: convergence holds regardless
13
25
  * of the order changes are applied, because the winner is recomputed as the max
14
26
  * HLC over the full oplog for that register.
15
27
  */
16
- export declare function applyChange(db: Database, c: Change): boolean;
28
+ export declare function applyChange(db: DbDriver, c: Change): boolean;
17
29
  /** Apply a local write: assign a fresh HLC, append to oplog, materialize. */
18
- export declare function emit(db: Database, dataset: string, rowId: string, col: string, value: unknown): Change;
30
+ export declare function emit(db: DbDriver, dataset: string, rowId: string, col: string, value: unknown): Change;
19
31
  /** Apply multiple local field writes to the same row. */
20
- export declare function emitFields(db: Database, dataset: string, rowId: string, fields: Record<string, unknown>): Change[];
32
+ export declare function emitFields(db: DbDriver, dataset: string, rowId: string, fields: Record<string, unknown>): Change[];
21
33
  /** Apply remote changes from a sync peer (advances clock, then merges). */
22
- export declare function ingest(db: Database, changes: Change[]): number;
34
+ export declare function ingest(db: DbDriver, changes: Change[]): number;
23
35
  /** All oplog changes with HLC strictly greater than `since` (test/debug helper). */
24
- export declare function changesSince(db: Database, since: string): Change[];
36
+ export declare function changesSince(db: DbDriver, since: string): Change[];
25
37
  export interface ChangeBatch {
26
38
  changes: Change[];
27
39
  cursor: number;
28
40
  }
41
+ export interface ChangesAfterOpts {
42
+ /** Max rows to return — pagination for large pulls (initial hydration). */
43
+ limit?: number;
44
+ /** Datasets to omit (partial replicas, e.g. a phone skipping site_files). */
45
+ excludeDatasets?: string[];
46
+ }
29
47
  /**
30
48
  * Changes inserted after `seq` (a local rowid), in insertion order, plus the
31
49
  * new high-water cursor. Used for replication: insertion order never skips a
32
50
  * change even when clocks are skewed.
51
+ *
52
+ * With `limit`, the cursor stops at the last returned row so the next pull
53
+ * resumes there; when the scan is exhausted (fewer rows than the limit, or no
54
+ * limit) the cursor jumps to the table's high-water rowid so excluded-dataset
55
+ * tails aren't rescanned every round.
33
56
  */
34
- export declare function changesAfterSeq(db: Database, seq: number): ChangeBatch;
57
+ export declare function changesAfterSeq(db: DbDriver, seq: number, opts?: ChangesAfterOpts): ChangeBatch;
35
58
  //# sourceMappingURL=crdt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../../src/core/crdt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA0ID;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAc5D;AAED,6EAA6E;AAC7E,wBAAgB,IAAI,CAClB,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,MAAM,CAYR;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,EAAE,CAMV;AAED,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAW9D;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAMlE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAStE"}
1
+ {"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../../src/core/crdt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAW5C,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,kFAAkF;AAClF,eAAO,MAAM,WAAW,yEAA0E,CAAC;AAEnG,eAAO,MAAM,aAAa,QAAyB,CAAC;AAMpD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQvE;AAED,+DAA+D;AAC/D,wBAAgB,OAAO,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAC5C,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,EACrB,KAAK,GAAE,MAAM,GAAG,IAAW,GAC1B,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAEnB;AA0ID;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAc5D;AAED,6EAA6E;AAC7E,wBAAgB,IAAI,CAClB,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,MAAM,CAaR;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,EAAE,CAMV;AAED,2EAA2E;AAC3E,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAW9D;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIlE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,gBAAqB,GAC1B,WAAW,CAuBb"}
@@ -1,19 +1,33 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  export interface DatabaseRow {
3
3
  id: string;
4
4
  name: string;
5
5
  icon: string | null;
6
6
  created_hlc: string;
7
7
  }
8
- export declare function createDatabase(db: Database, opts: {
8
+ export declare const DATABASE_COLS: readonly ["id", "name", "icon", "created_hlc"];
9
+ export declare const createDatabase: (db: DbDriver, opts: {
9
10
  name: string;
10
11
  icon?: string;
11
- }): DatabaseRow;
12
- export declare function updateDatabase(db: Database, id: string, fields: {
12
+ }) => DatabaseRow;
13
+ export declare const updateDatabase: (db: DbDriver, id: string, fields: {
13
14
  name?: string;
14
15
  icon?: string | null;
15
- }): DatabaseRow;
16
- export declare function getDatabase(db: Database, id: string): DatabaseRow | null;
17
- export declare function listDatabases(db: Database): DatabaseRow[];
18
- export declare function deleteDatabase(db: Database, id: string): boolean;
16
+ }) => DatabaseRow;
17
+ /**
18
+ * Copy a database whole — name, icon, every property (type/config/position
19
+ * preserved) and every record (in order, cells carried via an old→new property
20
+ * id map, so duplicate property names copy losslessly). A relation column
21
+ * pointing back at the *source* database is remapped to the copy so it stays
22
+ * self-referential; relations to other databases keep their target.
23
+ * `name`/`icon` override the defaults; the locale "copy" suffix is the
24
+ * caller's job. All emits share one txn, so the copy syncs as one revision.
25
+ */
26
+ export declare const duplicateDatabase: (db: DbDriver, id: string, opts?: {
27
+ name?: string;
28
+ icon?: string;
29
+ } | undefined) => DatabaseRow;
30
+ export declare function getDatabase(db: DbDriver, id: string): DatabaseRow | null;
31
+ export declare function listDatabases(db: DbDriver): DatabaseRow[];
32
+ export declare const deleteDatabase: (db: DbDriver, id: string) => boolean;
19
33
  //# sourceMappingURL=databases.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"databases.d.ts","sourceRoot":"","sources":["../../src/core/databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC,WAAW,CAMb;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,EACZ,EAAE,EAAE,MAAM,EACV,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC9C,WAAW,CAKb;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMxE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,EAAE,CAMzD;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAqBhE"}
1
+ {"version":3,"file":"databases.d.ts","sourceRoot":"","sources":["../../src/core/databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,gDAAiD,CAAC;AAI5E,eAAO,MAAM,cAAc;UAEX,MAAM;WAAS,MAAM;iBAOnC,CAAC;AAEH,eAAO,MAAM,cAAc;WAGR,MAAM;WAAS,MAAM,GAAG,IAAI;iBAM7C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB;WAGb,MAAM;WAAS,MAAM;6BAwBpC,CAAC;AAEH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAIxE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG,WAAW,EAAE,CAIzD;AAED,eAAO,MAAM,cAAc,uCAqBzB,CAAC"}
package/dist/core/db.d.ts CHANGED
@@ -1,32 +1,6 @@
1
1
  import { Database } from "bun:sqlite";
2
+ export { runSchema, ftsAvailable, migrateRecords, migratePeers, migrateDocBlocks, migrateDocuments, migrateOplog, initSchema, } from "./schema-init.ts";
2
3
  export declare function ensureDirs(): void;
3
- export declare function runSchema(db: Database): void;
4
- export declare function ftsAvailable(db: Database): boolean;
5
- /**
6
- * Migrate legacy EAV records (record_values: one row per cell) to the JSON
7
- * layout (records.data: one row per record). Idempotent — keyed off schema
8
- * shape, no version flag. A no-op for fresh databases.
9
- */
10
- export declare function migrateRecords(db: Database): void;
11
- /**
12
- * Add the peer-pairing columns to a legacy `peers` table (token/label/node_id/
13
- * enabled/last_sync_at/last_status/last_error). Idempotent — guarded per column,
14
- * never drops the table so existing replication cursors survive.
15
- */
16
- export declare function migratePeers(db: Database): void;
17
- /**
18
- * Add the `blank_after` column to a legacy `doc_blocks` table. Idempotent —
19
- * guarded by hasColumn; existing blocks default to 0 (canonical single-blank-line
20
- * separators), so the migration never changes an existing document's body.
21
- */
22
- export declare function migrateDocBlocks(db: Database): void;
23
- /**
24
- * Add the `order_key` column to a legacy `documents` table and backfill it.
25
- * Idempotent — guarded by hasColumn; backfill only touches rows with a NULL key,
26
- * assigning per-parent fractional indices in current created_hlc order, so the
27
- * displayed document order is unchanged until the user first drags something.
28
- */
29
- export declare function migrateDocuments(db: Database): void;
30
4
  /** Open (and migrate) the on-disk metahub database for the resolved home. */
31
5
  export declare function openMetahub(): Database;
32
6
  //# sourceMappingURL=db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/core/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAO5C;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAKlD;AAeD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAuBjD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAa/C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAGnD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAInD;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,IAAI,QAAQ,CAUtC"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/core/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAOtC,OAAO,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,IAAI,QAAQ,CAMtC"}
@@ -1,4 +1,4 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  export interface DocumentRow {
3
3
  id: string;
4
4
  title: string;
@@ -9,33 +9,48 @@ export interface DocumentRow {
9
9
  order_key: string | null;
10
10
  }
11
11
  export type DocumentSummary = Omit<DocumentRow, "body">;
12
+ export declare const DOCUMENT_COLS: readonly ["id", "title", "body", "database_id", "parent_id", "created_hlc", "order_key"];
12
13
  /** Max HLC over a document's own register and all its blocks — a read/edit token. */
13
- export declare function documentVersion(db: Database, id: string): string;
14
- export declare function createDocument(db: Database, opts: {
14
+ export declare function documentVersion(db: DbDriver, id: string): string;
15
+ export declare const createDocument: (db: DbDriver, opts: {
15
16
  title: string;
16
17
  body?: string;
17
18
  database_id?: string;
18
19
  parent_id?: string;
19
- }): DocumentRow;
20
- export declare function getDocument(db: Database, id: string): DocumentRow | null;
21
- export declare function listDocuments(db: Database, opts?: {
20
+ }) => DocumentRow;
21
+ export declare function getDocument(db: DbDriver, id: string): DocumentRow | null;
22
+ export declare function listDocuments(db: DbDriver, opts?: {
22
23
  database_id?: string;
23
24
  parent_id?: string;
24
25
  }): DocumentSummary[];
25
26
  /** Backfill order_key for every parent group that has un-keyed live documents. */
26
- export declare function backfillDocumentOrderKeys(db: Database): void;
27
+ export declare const backfillDocumentOrderKeys: (db: DbDriver) => void;
27
28
  /**
28
29
  * Move a document next to / into another, atomically updating both its parent
29
30
  * and its order_key. `into` nests it as the last child of `targetId`;
30
31
  * `before`/`after` re-orders (and reparents if needed) among the target's siblings.
31
32
  */
32
- export declare function moveDocument(db: Database, id: string, targetId: string, where: "before" | "after" | "into"): DocumentRow;
33
- export declare function updateDocument(db: Database, id: string, fields: {
33
+ export declare const moveDocument: (db: DbDriver, id: string, targetId: string, where: "before" | "after" | "into") => DocumentRow;
34
+ /**
35
+ * Copy a document — title, every block (verbatim, with fresh block ids so the
36
+ * copy edits independently) and its `database_id`/`parent_id`. The copy lands
37
+ * right after its source in the same sibling scope. Blocks are cloned at the
38
+ * block level (not via a markdown round-trip) so empty list items / blank-line
39
+ * runs survive losslessly. `title`/`parentId` override the defaults; the locale
40
+ * "copy" suffix is the caller's job. `created_hlc` is the copy's own birth time.
41
+ */
42
+ export declare const duplicateDocument: (db: DbDriver, id: string, opts?: {
43
+ title?: string;
44
+ parentId?: string | null;
45
+ } | undefined) => DocumentRow;
46
+ export declare const updateDocument: (db: DbDriver, id: string, fields: {
34
47
  title?: string;
35
48
  body?: string;
36
49
  database_id?: string;
37
50
  parent_id?: string | null;
38
- }): DocumentRow;
51
+ }, opts?: {
52
+ ifMatch?: string;
53
+ } | undefined) => DocumentRow;
39
54
  export interface EditDocResult {
40
55
  id: string;
41
56
  changed: boolean;
@@ -48,13 +63,13 @@ export interface EditDocResult {
48
63
  * cannot author it without current content, and drift surfaces as "not found".
49
64
  * `ifMatch` is an optional staleness backstop (a version from documentVersion).
50
65
  */
51
- export declare function editDocument(db: Database, id: string, opts: {
66
+ export declare const editDocument: (db: DbDriver, id: string, opts: {
52
67
  old: string;
53
68
  new: string;
54
69
  replaceAll?: boolean;
55
70
  ifMatch?: string;
56
- }): EditDocResult;
57
- export declare function appendDocument(db: Database, id: string, body: string): EditDocResult;
58
- export declare function prependDocument(db: Database, id: string, body: string): EditDocResult;
59
- export declare function deleteDocument(db: Database, id: string): boolean;
71
+ }) => EditDocResult;
72
+ export declare const appendDocument: (db: DbDriver, id: string, body: string) => EditDocResult;
73
+ export declare const prependDocument: (db: DbDriver, id: string, body: string) => EditDocResult;
74
+ export declare const deleteDocument: (db: DbDriver, id: string) => boolean;
60
75
  //# sourceMappingURL=documents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/core/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AA6FxD,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAShE;AAID,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,WAAW,CAYb;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMxE;AAMD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,eAAe,EAAE,CAYnB;AAgFD,kFAAkF;AAClF,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAK5D;AAgDD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GACjC,WAAW,CAUb;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,EACZ,EAAE,EAAE,MAAM,EACV,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzF,WAAW,CAiBb;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,EACZ,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,aAAa,CAmCf;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAUpF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAUrF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAUhE"}
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/core/documents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAExD,eAAO,MAAM,aAAa,0FAEhB,CAAC;AAqGX,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAShE;AAID,eAAO,MAAM,cAAc;WAEV,MAAM;WAAS,MAAM;kBAAgB,MAAM;gBAAc,MAAM;iBAa9E,CAAC;AAEH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAIxE;AAMD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,QAAQ,EACZ,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,eAAe,EAAE,CAWnB;AAgFD,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,wBAOpC,CAAC;AAgDH;;;;GAIG;AACH,eAAO,MAAM,YAAY,iGAevB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;YAGZ,MAAM;eAAa,MAAM,GAAG,IAAI;6BAuBhD,CAAC;AAEH,eAAO,MAAM,cAAc;YAGP,MAAM;WAAS,MAAM;kBAAgB,MAAM;gBAAc,MAAM,GAAG,IAAI;;cACtE,MAAM;6BAsBxB,CAAC;AAEH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY;SAGV,MAAM;SAAO,MAAM;iBAAe,OAAO;cAAY,MAAM;mBAqCxE,CAAC;AAEH,eAAO,MAAM,cAAc,2DAczB,CAAC;AAEH,eAAO,MAAM,eAAe,2DAc1B,CAAC;AAEH,eAAO,MAAM,cAAc,uCAUzB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Minimal synchronous SQLite driver surface that every runtime hosting a
3
+ * metahub database must provide. The portable core modules type against this
4
+ * instead of bun:sqlite so the same code can run on Bun (bun:sqlite's
5
+ * `Database` satisfies it structurally — no adapter needed) and in a browser
6
+ * worker (sqlite-wasm behind a small adapter).
7
+ *
8
+ * Keep this surface to what bun:sqlite and sqlite-wasm can both serve
9
+ * synchronously: positional `?` binds, `get`/`all`/`run` on prepared
10
+ * statements, `exec` for scripts, and re-entrant `transaction` wrappers.
11
+ */
12
+ export type SqlBindable = string | number | bigint | boolean | null | Uint8Array;
13
+ export interface Stmt {
14
+ get(...params: SqlBindable[]): unknown;
15
+ all(...params: SqlBindable[]): unknown[];
16
+ run(...params: SqlBindable[]): {
17
+ changes: number;
18
+ };
19
+ }
20
+ export interface DbDriver {
21
+ query(sql: string): Stmt;
22
+ exec(sql: string): void;
23
+ transaction<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
24
+ }
25
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/core/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,CAAC;AAEjF,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IACvC,GAAG,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,CAAC;IACzC,GAAG,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACpD;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,CAAC,CAAC,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;CAC/E"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Machine-readable error taxonomy — the contract between core and its callers.
3
+ *
4
+ * Agents and scripts dispatch on `code`, never on message text (messages may
5
+ * be reworded freely). The CLI maps codes to exit codes (src/cli/output.ts)
6
+ * and the HTTP layer maps them to status codes (src/webui/server/routes.ts),
7
+ * so adding a code here is enough for both surfaces to pick it up.
8
+ */
9
+ export type MhErrorCode =
10
+ /** Bad arguments / refusing a request as stated (HTTP 400, exit 2). */
11
+ "invalid_input"
12
+ /** Referenced entity does not exist (HTTP 404, exit 3). */
13
+ | "not_found"
14
+ /** A ref matched more than one entity — disambiguate and retry (exit 4). */
15
+ | "ambiguous"
16
+ /** Optimistic-concurrency failure: re-read, then retry (HTTP 409, exit 5). */
17
+ | "stale"
18
+ /** State conflict, e.g. a name that already exists (HTTP 409, exit 5). */
19
+ | "conflict"
20
+ /** Missing/invalid credentials (HTTP 401, exit 6). */
21
+ | "auth"
22
+ /** A remote peer was unreachable or replied non-OK — retryable (exit 7). */
23
+ | "network"
24
+ /** The requested listen port is taken (exit 98, historical). */
25
+ | "port_in_use";
26
+ export declare class MhError extends Error {
27
+ readonly code: MhErrorCode;
28
+ constructor(code: MhErrorCode, message: string);
29
+ }
30
+ /** The error's code, for errors that carry one (anything else → undefined). */
31
+ export declare function errorCode(e: unknown): MhErrorCode | undefined;
32
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW;AACrB,uEAAuE;AACrE,eAAe;AACjB,2DAA2D;GACzD,WAAW;AACb,4EAA4E;GAC1E,WAAW;AACb,8EAA8E;GAC5E,OAAO;AACT,0EAA0E;GACxE,UAAU;AACZ,sDAAsD;GACpD,MAAM;AACR,4EAA4E;GAC1E,SAAS;AACX,gEAAgE;GAC9D,aAAa,CAAC;AAElB,qBAAa,OAAQ,SAAQ,KAAK;IAE9B,QAAQ,CAAC,IAAI,EAAE,WAAW;gBAAjB,IAAI,EAAE,WAAW,EAC1B,OAAO,EAAE,MAAM;CAKlB;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAE7D"}
@@ -0,0 +1,174 @@
1
+ import type { DbDriver } from "./driver.ts";
2
+ /** Where a revision came from, derived from its change-group label. */
3
+ export type RevisionKind = "user" | "repair" | "revert";
4
+ export interface DocRevision {
5
+ /** Version token (max HLC of the revision) — pass to documentAtVersion/revertDocument. */
6
+ version: string;
7
+ /** Wall-clock time derived from the version HLC (ISO 8601). */
8
+ at: string;
9
+ node_id: string;
10
+ /** Source of the revision: a user edit, a repairHub fix, or a revert. */
11
+ kind: RevisionKind;
12
+ changes: number;
13
+ created: boolean;
14
+ deleted: boolean;
15
+ title_changed: boolean;
16
+ blocks_changed: number;
17
+ blocks_deleted: number;
18
+ }
19
+ /** A document's edit history, newest first, clustered into save-sized revisions. */
20
+ export declare function listDocumentRevisions(db: DbDriver, id: string): DocRevision[];
21
+ export interface DocumentVersionState {
22
+ id: string;
23
+ title: string;
24
+ body: string;
25
+ deleted: boolean;
26
+ /** Exact version (max HLC <= the requested cutoff) this state corresponds to. */
27
+ version: string;
28
+ }
29
+ /** Reconstruct a document as of version cutoff `at` (state at hlc <= at). */
30
+ export declare function documentAtVersion(db: DbDriver, id: string, at: string): DocumentVersionState;
31
+ export interface RevertDocResult {
32
+ id: string;
33
+ changed: boolean;
34
+ /** The version token the document was restored to. */
35
+ restored: string;
36
+ /** The document's version after the revert (a new head — revert is a forward write). */
37
+ version: string;
38
+ /** True when the revert resurrected a tombstoned document. */
39
+ undeleted: boolean;
40
+ }
41
+ /**
42
+ * Restore a document's title/body to a past version, as a new forward edit
43
+ * (reuses updateDocument's block reconcile, so unchanged blocks keep identity).
44
+ * Reverting a tombstoned document resurrects it; children unparented by the
45
+ * delete stay where they are. One change group, so history shows ONE revision.
46
+ */
47
+ export declare const revertDocument: (db: DbDriver, id: string, to: string, opts?: {
48
+ ifMatch?: string;
49
+ } | undefined) => RevertDocResult;
50
+ export interface RecordRevision {
51
+ version: string;
52
+ at: string;
53
+ node_id: string;
54
+ kind: RevisionKind;
55
+ changes: number;
56
+ created: boolean;
57
+ deleted: boolean;
58
+ /** Property ids of the cells written in this revision. */
59
+ fields: string[];
60
+ moved: boolean;
61
+ }
62
+ /** A record's edit history, newest first, clustered into save-sized revisions. */
63
+ export declare function listRecordRevisions(db: DbDriver, id: string): RecordRevision[];
64
+ /** One cell's value change inside a revision. A missing `before`/`after` key
65
+ * means the cell did not exist on that side (distinct from an explicit null). */
66
+ export interface FieldChange {
67
+ prop: string;
68
+ before?: unknown;
69
+ after?: unknown;
70
+ }
71
+ export interface DatabaseActivityEntry extends RecordRevision {
72
+ record_id: string;
73
+ /** Title-property value as of this revision — deleted records keep their last title. */
74
+ record_title: string | null;
75
+ /** Value-level changes of the touched cells. */
76
+ diffs: FieldChange[];
77
+ }
78
+ /**
79
+ * "What happened in this table lately": every record's revisions, merged and
80
+ * sorted newest first. Includes tombstoned records (so deletions show up) — a
81
+ * read-only aggregation, same clustering as listRecordRevisions per record.
82
+ * Walking each record's change stream oldest-first lets us carry a running
83
+ * cell-state map, so every entry ships its old→new values and a title snapshot
84
+ * for free (no extra queries). After compaction, `before` at the window edge
85
+ * may be missing (the superseded write was pruned) — it reads as "was empty".
86
+ */
87
+ export declare function listDatabaseActivity(db: DbDriver, databaseId: string, opts?: {
88
+ limit?: number;
89
+ }): DatabaseActivityEntry[];
90
+ export interface RecordVersionState {
91
+ id: string;
92
+ database_id: string | null;
93
+ deleted: boolean;
94
+ /** Cells present at that version, keyed by property id (raw, no name mapping). */
95
+ data: Record<string, unknown>;
96
+ version: string;
97
+ }
98
+ /** Reconstruct a record as of version cutoff `at` (state at hlc <= at). */
99
+ export declare function recordAtVersion(db: DbDriver, id: string, at: string): RecordVersionState;
100
+ export interface RevertRecordResult {
101
+ id: string;
102
+ changed: boolean;
103
+ /** Property ids whose cells were written by the revert. */
104
+ fields: string[];
105
+ /** True when the revert resurrected a tombstoned record. */
106
+ undeleted: boolean;
107
+ restored: string;
108
+ }
109
+ /** Restore a record's cells to a past version as a new forward edit. Only cells
110
+ * of currently-live properties are touched, so no orphan cells are recreated
111
+ * (the repairHub invariant). Reverting a tombstoned record resurrects it. */
112
+ export declare const revertRecord: (db: DbDriver, id: string, to: string) => RevertRecordResult;
113
+ export interface PropertyRevision {
114
+ version: string;
115
+ at: string;
116
+ node_id: string;
117
+ kind: RevisionKind;
118
+ changes: number;
119
+ created: boolean;
120
+ deleted: boolean;
121
+ /** Definition columns touched in this revision (name/type/config/position). */
122
+ fields: string[];
123
+ /** Record cells the same change group cleared (a type change / removal cascade). */
124
+ cells_cleared: number;
125
+ }
126
+ /** A property's definition history, newest first. Cascaded cell clears are
127
+ * counted via the shared txn so a type change reads as one revision. */
128
+ export declare function listPropertyRevisions(db: DbDriver, id: string): PropertyRevision[];
129
+ export interface PropertyVersionState {
130
+ id: string;
131
+ database_id: string | null;
132
+ name: string;
133
+ type: string;
134
+ config: unknown;
135
+ position: number | null;
136
+ deleted: boolean;
137
+ version: string;
138
+ }
139
+ /** Reconstruct a property definition as of version cutoff `at`. */
140
+ export declare function propertyAtVersion(db: DbDriver, id: string, at: string): PropertyVersionState;
141
+ export interface RevertPropertyResult {
142
+ id: string;
143
+ changed: boolean;
144
+ /** Definition columns the revert wrote. */
145
+ fields: string[];
146
+ /** Record cells restored to their value at the target version. */
147
+ restored_cells: number;
148
+ /** Cells left alone because a user wrote them after the target version. */
149
+ skipped_cells: number;
150
+ undeleted: boolean;
151
+ restored: string;
152
+ }
153
+ /**
154
+ * Schema-level rollback: restore a property's definition AND the record cells
155
+ * its later type-change/removal cascades cleared. Definition registers are
156
+ * emitted directly (never via updateProperty — its type-change cascade would
157
+ * re-clear the cells being restored). A cell is only touched when its current
158
+ * winner IS the cascade (or a repair); it is restored to the last value before
159
+ * that cascade, so user data — written before or after the target version —
160
+ * always survives. Cells whose winner is a user write are kept (skipped_cells).
161
+ * Pre-txn legacy writes can't be attributed, so they count as user writes.
162
+ */
163
+ export declare const revertProperty: (db: DbDriver, id: string, to: string) => RevertPropertyResult;
164
+ export interface FieldHistoryEntry {
165
+ version: string;
166
+ at: string;
167
+ node_id: string;
168
+ /** The value written; undefined when the write cleared the cell. */
169
+ value: unknown;
170
+ cleared: boolean;
171
+ }
172
+ /** The full write trail of one record cell (property id), newest first. */
173
+ export declare function recordFieldHistory(db: DbDriver, id: string, propId: string): FieldHistoryEntry[];
174
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/core/history.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Database } from "bun:sqlite";
1
+ import type { DbDriver } from "./driver.ts";
2
2
  export interface Hlc {
3
3
  millis: number;
4
4
  counter: number;
@@ -7,7 +7,7 @@ export interface Hlc {
7
7
  export declare function formatHlc(h: Hlc): string;
8
8
  export declare function parseHlc(s: string): Hlc;
9
9
  /** Issue a new timestamp for a local event, advancing the persisted clock. */
10
- export declare function nextHlc(db: Database, node: string, now?: number): string;
10
+ export declare function nextHlc(db: DbDriver, node: string, now?: number): string;
11
11
  /** Advance the local clock to account for an observed remote timestamp. */
12
- export declare function observeHlc(db: Database, node: string, remote: string, now?: number): void;
12
+ export declare function observeHlc(db: DbDriver, node: string, remote: string, now?: number): void;
13
13
  //# sourceMappingURL=hlc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hlc.d.ts","sourceRoot":"","sources":["../../src/core/hlc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAIxC;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAQvC;AAeD,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,MAAM,CAO5E;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,SAAa,GACf,IAAI,CAWN"}
1
+ {"version":3,"file":"hlc.d.ts","sourceRoot":"","sources":["../../src/core/hlc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAIxC;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG,CAQvC;AAeD,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,MAAM,CAO5E;AAED,2EAA2E;AAC3E,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,SAAa,GACf,IAAI,CAWN"}