@minnowdb/core 0.2.1 → 0.4.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 (270) hide show
  1. package/README.md +23 -32
  2. package/dist/block-format/block.d.ts +32 -13
  3. package/dist/block-format/block.js +179 -55
  4. package/dist/block-format/checksum.d.ts +2 -1
  5. package/dist/block-format/checksum.js +5 -2
  6. package/dist/block-format/codecs.d.ts +7 -2
  7. package/dist/block-format/codecs.js +53 -12
  8. package/dist/block-format/column.d.ts +3 -2
  9. package/dist/block-format/column.js +96 -30
  10. package/dist/block-format/index.d.ts +2 -2
  11. package/dist/block-format/index.js +2 -2
  12. package/dist/block-format/physical.d.ts +8 -2
  13. package/dist/block-format/physical.js +14 -6
  14. package/dist/block-format/types.d.ts +7 -3
  15. package/dist/block-format/types.js +0 -1
  16. package/dist/block-format/unicode.d.ts +11 -0
  17. package/dist/block-format/unicode.js +46 -0
  18. package/dist/date-value.d.ts +15 -0
  19. package/dist/date-value.js +64 -0
  20. package/dist/engine/artifact-cache.d.ts +2 -1
  21. package/dist/engine/artifact-cache.js +5 -1
  22. package/dist/engine/batch.d.ts +11 -3
  23. package/dist/engine/batch.js +17 -7
  24. package/dist/engine/buffered-writer.d.ts +2 -1
  25. package/dist/engine/buffered-writer.js +34 -9
  26. package/dist/engine/cache-limits.d.ts +25 -0
  27. package/dist/engine/cache-limits.js +25 -0
  28. package/dist/engine/catalog.d.ts +24 -5
  29. package/dist/engine/catalog.js +34 -7
  30. package/dist/engine/client.d.ts +29 -7
  31. package/dist/engine/client.js +216 -40
  32. package/dist/engine/database.d.ts +224 -39
  33. package/dist/engine/database.js +7475 -1539
  34. package/dist/engine/defaults.d.ts +4 -8
  35. package/dist/engine/defaults.js +46 -22
  36. package/dist/engine/errors.d.ts +19 -1
  37. package/dist/engine/errors.js +32 -2
  38. package/dist/engine/fts.d.ts +0 -6
  39. package/dist/engine/fts.js +41 -14
  40. package/dist/engine/group-index.d.ts +0 -1
  41. package/dist/engine/group-index.js +6 -11
  42. package/dist/engine/index.d.ts +16 -10
  43. package/dist/engine/index.js +12 -9
  44. package/dist/engine/join-index.d.ts +0 -1
  45. package/dist/engine/join-index.js +2 -2
  46. package/dist/engine/keyed-live.d.ts +57 -0
  47. package/dist/engine/keyed-live.js +226 -0
  48. package/dist/engine/live-api.d.ts +4 -0
  49. package/dist/engine/live-api.js +4 -0
  50. package/dist/engine/live.d.ts +43 -26
  51. package/dist/engine/live.js +504 -126
  52. package/dist/engine/memory.d.ts +2 -1
  53. package/dist/engine/memory.js +3 -2
  54. package/dist/engine/optimizer.d.ts +0 -1
  55. package/dist/engine/optimizer.js +262 -27
  56. package/dist/engine/query-api.d.ts +3 -0
  57. package/dist/engine/query-api.js +3 -0
  58. package/dist/engine/query-cache.d.ts +1 -2
  59. package/dist/engine/query-cache.js +7 -5
  60. package/dist/engine/query.d.ts +134 -29
  61. package/dist/engine/query.js +1668 -372
  62. package/dist/engine/result-wire.d.ts +0 -1
  63. package/dist/engine/result-wire.js +2 -2
  64. package/dist/engine/schema-wire.d.ts +6 -3
  65. package/dist/engine/schema-wire.js +40 -34
  66. package/dist/engine/schema.d.ts +156 -80
  67. package/dist/engine/schema.js +570 -108
  68. package/dist/engine/sort-keys.d.ts +4 -4
  69. package/dist/engine/sort-keys.js +44 -29
  70. package/dist/engine/sql-domains.d.ts +31 -0
  71. package/dist/engine/sql-domains.js +585 -0
  72. package/dist/engine/sql-driver.d.ts +19 -0
  73. package/dist/engine/sql-driver.js +1 -0
  74. package/dist/engine/sql-json.d.ts +7 -9
  75. package/dist/engine/sql-json.js +75 -15
  76. package/dist/engine/sql-semantics.d.ts +8 -3
  77. package/dist/engine/sql-semantics.js +458 -30
  78. package/dist/engine/typed-live.d.ts +67 -0
  79. package/dist/engine/typed-live.js +349 -0
  80. package/dist/engine/vector.d.ts +12 -2
  81. package/dist/engine/vector.js +635 -119
  82. package/dist/engine/worker-host.d.ts +9 -2
  83. package/dist/engine/worker-host.js +620 -152
  84. package/dist/engine/worker.d.ts +0 -1
  85. package/dist/engine/worker.js +3 -3
  86. package/dist/engine/write-block-planner.d.ts +18 -0
  87. package/dist/engine/write-block-planner.js +112 -0
  88. package/dist/index.d.ts +0 -1
  89. package/dist/index.js +0 -1
  90. package/dist/plan/index.d.ts +3 -4
  91. package/dist/plan/index.js +3 -4
  92. package/dist/storage/index.d.ts +1 -1
  93. package/dist/storage/index.js +1 -1
  94. package/dist/storage/indexeddb.d.ts +99 -51
  95. package/dist/storage/indexeddb.js +13448 -2660
  96. package/dist/storage/memory.d.ts +64 -57
  97. package/dist/storage/memory.js +1042 -128
  98. package/dist/storage/opfs/files.d.ts +18 -7
  99. package/dist/storage/opfs/files.js +115 -28
  100. package/dist/storage/opfs/index.d.ts +1 -1
  101. package/dist/storage/opfs/index.js +1 -1
  102. package/dist/storage/opfs/leader.d.ts +409 -75
  103. package/dist/storage/opfs/leader.js +4621 -650
  104. package/dist/storage/opfs/rpc.d.ts +15 -3
  105. package/dist/storage/opfs/rpc.js +224 -2
  106. package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
  107. package/dist/storage/opfs/snapshot-ledger.js +281 -0
  108. package/dist/storage/opfs/store.d.ts +33 -99
  109. package/dist/storage/opfs/store.js +453 -364
  110. package/dist/storage/persistence.d.ts +37 -0
  111. package/dist/storage/persistence.js +78 -0
  112. package/dist/storage/snapshot-stream.d.ts +24 -0
  113. package/dist/storage/snapshot-stream.js +897 -0
  114. package/dist/storage/snapshot.d.ts +9 -85
  115. package/dist/storage/snapshot.js +33 -265
  116. package/dist/storage/toolkit/extents.d.ts +41 -7
  117. package/dist/storage/toolkit/extents.js +402 -39
  118. package/dist/storage/toolkit/index.d.ts +4 -5
  119. package/dist/storage/toolkit/index.js +28 -4
  120. package/dist/storage/toolkit/record-core.d.ts +182 -55
  121. package/dist/storage/toolkit/record-core.js +6158 -1331
  122. package/dist/storage/toolkit/sync-file.d.ts +10 -1
  123. package/dist/storage/toolkit/sync-file.js +50 -2
  124. package/dist/storage/toolkit/wal.d.ts +23 -5
  125. package/dist/storage/toolkit/wal.js +89 -27
  126. package/dist/storage/toolkit/wire.d.ts +15 -2
  127. package/dist/storage/toolkit/wire.js +199 -10
  128. package/dist/storage/types.d.ts +1439 -204
  129. package/dist/storage/types.js +1745 -141
  130. package/dist/testing/block-store-conformance.d.ts +0 -1
  131. package/dist/testing/block-store-conformance.js +1009 -119
  132. package/dist/testing/index.d.ts +46 -26
  133. package/dist/testing/index.js +112 -57
  134. package/dist/testing/opfs-shim.d.ts +13 -3
  135. package/dist/testing/opfs-shim.js +40 -6
  136. package/dist/testing/simulator.d.ts +97 -0
  137. package/dist/testing/simulator.js +591 -0
  138. package/dist/testing/sqllogictest.d.ts +89 -0
  139. package/dist/testing/sqllogictest.js +434 -0
  140. package/dist/transactions/index.d.ts +92 -16
  141. package/dist/transactions/index.js +1074 -236
  142. package/dist/worker-protocol/index.d.ts +3 -2
  143. package/dist/worker-protocol/index.js +2 -5
  144. package/package.json +53 -3
  145. package/postgres-feature-profile.json +223 -0
  146. package/sql-feature-matrix.json +172 -252
  147. package/dist/block-format/block.d.ts.map +0 -1
  148. package/dist/block-format/block.js.map +0 -1
  149. package/dist/block-format/checksum.d.ts.map +0 -1
  150. package/dist/block-format/checksum.js.map +0 -1
  151. package/dist/block-format/codecs.d.ts.map +0 -1
  152. package/dist/block-format/codecs.js.map +0 -1
  153. package/dist/block-format/column.d.ts.map +0 -1
  154. package/dist/block-format/column.js.map +0 -1
  155. package/dist/block-format/index.d.ts.map +0 -1
  156. package/dist/block-format/index.js.map +0 -1
  157. package/dist/block-format/physical.d.ts.map +0 -1
  158. package/dist/block-format/physical.js.map +0 -1
  159. package/dist/block-format/types.d.ts.map +0 -1
  160. package/dist/block-format/types.js.map +0 -1
  161. package/dist/engine/artifact-cache.d.ts.map +0 -1
  162. package/dist/engine/artifact-cache.js.map +0 -1
  163. package/dist/engine/batch.d.ts.map +0 -1
  164. package/dist/engine/batch.js.map +0 -1
  165. package/dist/engine/buffered-writer.d.ts.map +0 -1
  166. package/dist/engine/buffered-writer.js.map +0 -1
  167. package/dist/engine/catalog.d.ts.map +0 -1
  168. package/dist/engine/catalog.js.map +0 -1
  169. package/dist/engine/client.d.ts.map +0 -1
  170. package/dist/engine/client.js.map +0 -1
  171. package/dist/engine/coordinator.d.ts +0 -17
  172. package/dist/engine/coordinator.d.ts.map +0 -1
  173. package/dist/engine/coordinator.js +0 -60
  174. package/dist/engine/coordinator.js.map +0 -1
  175. package/dist/engine/database.d.ts.map +0 -1
  176. package/dist/engine/database.js.map +0 -1
  177. package/dist/engine/defaults.d.ts.map +0 -1
  178. package/dist/engine/defaults.js.map +0 -1
  179. package/dist/engine/errors.d.ts.map +0 -1
  180. package/dist/engine/errors.js.map +0 -1
  181. package/dist/engine/fts.d.ts.map +0 -1
  182. package/dist/engine/fts.js.map +0 -1
  183. package/dist/engine/group-index.d.ts.map +0 -1
  184. package/dist/engine/group-index.js.map +0 -1
  185. package/dist/engine/index.d.ts.map +0 -1
  186. package/dist/engine/index.js.map +0 -1
  187. package/dist/engine/join-index.d.ts.map +0 -1
  188. package/dist/engine/join-index.js.map +0 -1
  189. package/dist/engine/live.d.ts.map +0 -1
  190. package/dist/engine/live.js.map +0 -1
  191. package/dist/engine/memory.d.ts.map +0 -1
  192. package/dist/engine/memory.js.map +0 -1
  193. package/dist/engine/optimizer.d.ts.map +0 -1
  194. package/dist/engine/optimizer.js.map +0 -1
  195. package/dist/engine/query-cache.d.ts.map +0 -1
  196. package/dist/engine/query-cache.js.map +0 -1
  197. package/dist/engine/query.d.ts.map +0 -1
  198. package/dist/engine/query.js.map +0 -1
  199. package/dist/engine/result-wire.d.ts.map +0 -1
  200. package/dist/engine/result-wire.js.map +0 -1
  201. package/dist/engine/schema-wire.d.ts.map +0 -1
  202. package/dist/engine/schema-wire.js.map +0 -1
  203. package/dist/engine/schema.d.ts.map +0 -1
  204. package/dist/engine/schema.js.map +0 -1
  205. package/dist/engine/sort-keys.d.ts.map +0 -1
  206. package/dist/engine/sort-keys.js.map +0 -1
  207. package/dist/engine/sql-json.d.ts.map +0 -1
  208. package/dist/engine/sql-json.js.map +0 -1
  209. package/dist/engine/sql-semantics.d.ts.map +0 -1
  210. package/dist/engine/sql-semantics.js.map +0 -1
  211. package/dist/engine/vector.d.ts.map +0 -1
  212. package/dist/engine/vector.js.map +0 -1
  213. package/dist/engine/worker-host.d.ts.map +0 -1
  214. package/dist/engine/worker-host.js.map +0 -1
  215. package/dist/engine/worker.d.ts.map +0 -1
  216. package/dist/engine/worker.js.map +0 -1
  217. package/dist/index.d.ts.map +0 -1
  218. package/dist/index.js.map +0 -1
  219. package/dist/plan/index.d.ts.map +0 -1
  220. package/dist/plan/index.js.map +0 -1
  221. package/dist/storage/fixture-shape.d.ts +0 -42
  222. package/dist/storage/fixture-shape.d.ts.map +0 -1
  223. package/dist/storage/fixture-shape.js +0 -146
  224. package/dist/storage/fixture-shape.js.map +0 -1
  225. package/dist/storage/index.d.ts.map +0 -1
  226. package/dist/storage/index.js.map +0 -1
  227. package/dist/storage/indexeddb.d.ts.map +0 -1
  228. package/dist/storage/indexeddb.js.map +0 -1
  229. package/dist/storage/memory.d.ts.map +0 -1
  230. package/dist/storage/memory.js.map +0 -1
  231. package/dist/storage/opfs/files.d.ts.map +0 -1
  232. package/dist/storage/opfs/files.js.map +0 -1
  233. package/dist/storage/opfs/index.d.ts.map +0 -1
  234. package/dist/storage/opfs/index.js.map +0 -1
  235. package/dist/storage/opfs/leader.d.ts.map +0 -1
  236. package/dist/storage/opfs/leader.js.map +0 -1
  237. package/dist/storage/opfs/rpc.d.ts.map +0 -1
  238. package/dist/storage/opfs/rpc.js.map +0 -1
  239. package/dist/storage/opfs/store.d.ts.map +0 -1
  240. package/dist/storage/opfs/store.js.map +0 -1
  241. package/dist/storage/snapshot.d.ts.map +0 -1
  242. package/dist/storage/snapshot.js.map +0 -1
  243. package/dist/storage/toolkit/extents.d.ts.map +0 -1
  244. package/dist/storage/toolkit/extents.js.map +0 -1
  245. package/dist/storage/toolkit/index.d.ts.map +0 -1
  246. package/dist/storage/toolkit/index.js.map +0 -1
  247. package/dist/storage/toolkit/record-core.d.ts.map +0 -1
  248. package/dist/storage/toolkit/record-core.js.map +0 -1
  249. package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
  250. package/dist/storage/toolkit/sync-file.js.map +0 -1
  251. package/dist/storage/toolkit/wal.d.ts.map +0 -1
  252. package/dist/storage/toolkit/wal.js.map +0 -1
  253. package/dist/storage/toolkit/wire.d.ts.map +0 -1
  254. package/dist/storage/toolkit/wire.js.map +0 -1
  255. package/dist/storage/types.d.ts.map +0 -1
  256. package/dist/storage/types.js.map +0 -1
  257. package/dist/testing/block-store-conformance.d.ts.map +0 -1
  258. package/dist/testing/block-store-conformance.js.map +0 -1
  259. package/dist/testing/index.d.ts.map +0 -1
  260. package/dist/testing/index.js.map +0 -1
  261. package/dist/testing/opfs-shim.d.ts.map +0 -1
  262. package/dist/testing/opfs-shim.js.map +0 -1
  263. package/dist/testing/seeds.d.ts +0 -11
  264. package/dist/testing/seeds.d.ts.map +0 -1
  265. package/dist/testing/seeds.js +0 -50
  266. package/dist/testing/seeds.js.map +0 -1
  267. package/dist/transactions/index.d.ts.map +0 -1
  268. package/dist/transactions/index.js.map +0 -1
  269. package/dist/worker-protocol/index.d.ts.map +0 -1
  270. package/dist/worker-protocol/index.js.map +0 -1
@@ -1,60 +1,138 @@
1
- import type { DatabaseSnapshot, SnapshotLoadProgress } from "./snapshot.js";
2
1
  export declare const storeNames: readonly ["catalog", "manifests", "segments", "blocks", "transactions", "leases", "statistics", "temp", "gc"];
3
- /** The manifest fields every commit publishes; `Manifest` adds the resolved block list. */
2
+ export declare const MAX_MANIFEST_CHANGED_TABLE_IDS = 1024;
3
+ export declare function canonicalManifestChangedTableIds(ids: readonly string[]): string[];
4
+ export declare function validateCanonicalManifestChangedTableIds(ids: readonly string[]): string[];
5
+ /**
6
+ * The bounded manifest record every commit publishes. Block membership is paged separately;
7
+ * reading this record must never materialize a database-sized ID array.
8
+ */
4
9
  export interface ManifestSummary {
5
10
  version: number;
6
11
  previousVersion: number | null;
7
12
  createdAt: string;
8
- /**
9
- * Table IDs whose logical content this commit changed; empty means a logical no-change such as
10
- * compaction. Absent on manifests written before change tracking, which readers treat as
11
- * potentially changing every table.
12
- */
13
- changedTableIds?: string[];
13
+ /** Exact live payload membership cardinality at this version. */
14
+ liveBlockCount: number;
15
+ /** Exact sum of stored payload bytes in the live membership at this version. */
16
+ liveBlockBytes: number;
17
+ /** Table IDs whose logical content changed; empty means compaction or another logical no-op. */
18
+ changedTableIds: string[];
14
19
  /** A pruned descriptor remains readable for commit reconciliation but cannot be pinned. */
15
20
  prunedAt?: string;
21
+ /**
22
+ * Commit-local maintenance hints, not persisted in manifest history. A ready full-text
23
+ * column reports its durable delta-tail length so the committing engine can rebuild before
24
+ * metadata grows with every later commit, even when nobody searches the column again.
25
+ */
26
+ ftsDeltaCounts?: Array<{
27
+ tableId: string;
28
+ columnId: string;
29
+ count: number;
30
+ }>;
16
31
  }
17
- export interface Manifest extends ManifestSummary {
18
- blockIds: string[];
19
- }
32
+ /** Public/cold manifest inspection is bounded metadata only. */
33
+ export type Manifest = ManifestSummary;
20
34
  /**
21
- * The stored manifest shape: a checkpoint carries the complete sorted block list; a delta
22
- * carries only this commit's added and removed ids plus its distance from the checkpoint below
23
- * it. Reads resolve a version by walking back to the nearest checkpoint and applying deltas
24
- * forward, so publishing a commit writes O(changed blocks) instead of rewriting every live
25
- * block id. Pruning first tombstones records; maintenance later deletes only the obsolete prefix
26
- * below the checkpoint that the earliest readable version needs.
35
+ * One payload's bounded manifest-membership interval. The block is visible at version `v` iff
36
+ * `addedVersion <= v && (removedVersion === null || v < removedVersion)`. `byteLength` and the
37
+ * payload bytes are immutable while this record exists; `removedVersion`, when present, is
38
+ * strictly greater than `addedVersion`.
39
+ *
40
+ * A retired ID cannot be added again while this provenance record or a transaction/job that
41
+ * names it is retained. Once every readable/pinned version in the interval and every recovery
42
+ * root is gone, garbage collection removes payload and provenance together; no immortal ID
43
+ * tombstone is required.
27
44
  */
28
- export interface StoredManifestRecord extends ManifestSummary {
29
- blockIds?: string[];
30
- addedBlockIds?: string[];
31
- removedBlockIds?: string[];
32
- /** Deltas since the checkpoint below; 0 on checkpoints. */
33
- deltaDepth?: number;
34
- }
35
- /** Every this-many commits the store writes a full checkpoint instead of a delta. */
36
- export declare const MANIFEST_CHECKPOINT_INTERVAL = 32;
37
- /** Applies one stored record to a running block set (checkpoint replaces, delta mutates). */
38
- export declare function applyManifestRecord(blockIds: Set<string>, record: StoredManifestRecord): void;
39
- export interface PublishManifestInput {
40
- changedTableIds?: readonly string[];
45
+ export interface ManifestBlockRecord {
46
+ readonly blockId: string;
47
+ readonly byteLength: number;
48
+ /** CRC-32 of the exact stored payload, computed once before the first durable write. */
49
+ readonly checksum: number;
50
+ readonly addedVersion: number;
51
+ readonly removedVersion: number | null;
52
+ }
53
+ export interface ListManifestBlockPageInput {
54
+ readonly version: number;
55
+ readonly afterBlockId: string | null;
56
+ readonly limit: number;
57
+ }
58
+ export interface ListRetiredManifestBlockPageInput {
59
+ /** Includes records retired at or before this published version. */
60
+ readonly removedThroughVersion: number;
61
+ readonly afterBlockId: string | null;
62
+ readonly limit: number;
63
+ }
64
+ export interface ManifestBlockPage {
65
+ readonly records: ReadonlyArray<Pick<ManifestBlockRecord, "blockId" | "byteLength" | "checksum">>;
66
+ readonly nextCursor: string | null;
67
+ }
68
+ /** Input for adapter-internal manifest construction; not a public store mutation. */
69
+ export interface CreateManifestInput {
70
+ changedTableIds: readonly string[];
41
71
  expectedVersion: number | null;
42
- blockIds: readonly string[];
72
+ liveBlockCount: number;
73
+ liveBlockBytes: number;
43
74
  createdAt?: string;
44
75
  }
76
+ /**
77
+ * The stored manifest is the same bounded summary returned publicly. Exact block membership is
78
+ * represented once by `ManifestBlockRecord` intervals; checkpoint/delta arrays are deliberately
79
+ * absent from the v1 layout.
80
+ */
81
+ export type StoredManifestRecord = ManifestSummary;
82
+ /**
83
+ * One atomic user-table drop. The store compares both catalog and manifest revisions, derives
84
+ * every block that the table's segments still contribute to that exact manifest, publishes one
85
+ * successor without those blocks, and removes all table-owned metadata in the same durable
86
+ * step. Stored block payloads remain for lease-aware collection.
87
+ */
88
+ export interface DropTableInput {
89
+ tableId: string;
90
+ expectedTableRevision: number;
91
+ expectedManifestVersion: number | null;
92
+ /** Catalog epoch of the complete dependency proof used to authorize this drop. */
93
+ expectedCatalogEpoch: number;
94
+ committedAt: string;
95
+ }
96
+ /** One atomic column retirement; see `CatalogStore.dropTableColumn`. */
97
+ export interface DropTableColumnInput {
98
+ tableId: string;
99
+ columnId: string;
100
+ expectedTableRevision: number;
101
+ expectedManifestVersion: number | null;
102
+ /** Catalog epoch of the complete dependency proof used to authorize this drop. */
103
+ expectedCatalogEpoch: number;
104
+ committedAt: string;
105
+ }
45
106
  export declare const simpleDataTypes: readonly ["boolean", "number", "string", "datetime"];
46
107
  export type SimpleDataType = (typeof simpleDataTypes)[number];
108
+ /** PostgreSQL logical domains layered over the four stable physical block encodings. */
109
+ export type SqlDomain = {
110
+ kind: "numeric";
111
+ precision?: number;
112
+ scale?: number;
113
+ } | {
114
+ kind: "json" | "jsonb" | "uuid" | "time" | "interval";
115
+ } | {
116
+ kind: "array";
117
+ element: string;
118
+ } | {
119
+ kind: "enum";
120
+ name: string;
121
+ values: string[];
122
+ };
123
+ /** Validates the logical metadata shared by SQL DDL, the schema DSL, and restored catalogs. */
124
+ export declare function validateSqlDomain(domain: SqlDomain, context: string): SqlDomain;
47
125
  /**
48
- * Declarative write-time default. Plain structured-clone-safe data: the spec crosses the
49
- * worker postMessage boundary and persists in the catalog, so function defaults are
50
- * unrepresentable by design the schema DSL carries those separately (`ColumnBuilder.defaultFn`)
51
- * and the typed facade fills them before a batch reaches the engine.
126
+ * Declarative SQL write-time default. The spec is structured-clone-safe because it crosses the
127
+ * worker boundary and persists in the catalog. Expressions are parsed and type-checked by the
128
+ * engine before the catalog is changed, then evaluated once for every omitted insert slot.
52
129
  */
53
130
  export type ColumnDefault = {
54
- kind: "now";
55
- } | {
56
131
  kind: "literal";
57
- value: boolean | number | string;
132
+ value: boolean | number | string | Date;
133
+ } | {
134
+ kind: "expression";
135
+ sql: string;
58
136
  } | {
59
137
  kind: "autoincrement";
60
138
  };
@@ -62,8 +140,17 @@ export interface TableColumnRecord {
62
140
  id: string;
63
141
  name: string;
64
142
  type: SimpleDataType;
143
+ /**
144
+ * SQL INTEGER/SMALLINT/BIGINT columns use the number physical type, but only accept exact
145
+ * JavaScript safe integers. Absent for the public `number` type and SQL floating-point types.
146
+ * Keeping the domain in catalog metadata prevents a declared integer from silently rounding
147
+ * before it reaches storage while preserving the released Float64 block encoding.
148
+ */
149
+ integer?: true;
150
+ /** SQL-level semantics for a value physically encoded in this column's primitive type. */
151
+ sqlDomain?: SqlDomain;
65
152
  nullable: boolean;
66
- /** Fills null-or-absent slots at insert time; never applied at read time. */
153
+ /** Fills omitted or SQL `DEFAULT` slots at insert time; explicit NULL is never replaced. */
67
154
  defaultValue?: ColumnDefault;
68
155
  /**
69
156
  * What rows written before this column existed read as, instead of NULL.
@@ -85,22 +172,40 @@ export interface TableColumnRecord {
85
172
  * dropping it) is catalog-only while narrowing it is rejected by migration planning.
86
173
  */
87
174
  enumValues?: string[];
88
- }
175
+ /** Engine-generated row-addressing column; stored and indexed, never exposed as SQL schema. */
176
+ hidden?: true;
177
+ }
178
+ export declare const MAX_TABLE_COLUMNS = 1024;
179
+ export declare const MAX_ENUM_VALUES = 4096;
180
+ export declare const MAX_SECONDARY_INDEXES = 1024;
181
+ export declare const MAX_TABLE_TRIGGERS = 256;
182
+ export declare const MAX_TRIGGER_STATEMENTS = 256;
183
+ export declare const MAX_TABLE_CONSTRAINTS = 1024;
184
+ export declare const MAX_TABLE_RECORD_CHARACTERS = 1048576;
185
+ export declare const MAX_TABLE_RECORD_ENTRIES = 65536;
89
186
  /**
90
187
  * The single authority on which enum declarations are legal, shared by the schema DSL's
91
188
  * `column.enum()` and the engine's `createTable`: at least one value, every value a non-empty
92
189
  * string, no duplicates. Returns a defensive copy.
93
190
  */
94
191
  export declare function validateEnumValues(values: readonly string[], context: string): string[];
192
+ /**
193
+ * Validates the catalog invariants owned by a table's column list. Storage adapters call this
194
+ * for ordinary catalog writes and before restoring snapshots or checkpoints, so malformed
195
+ * metadata cannot enter through a less common persistence path.
196
+ */
197
+ export declare function validateTableColumns(columns: readonly TableColumnRecord[]): void;
95
198
  /**
96
199
  * The single authority on which default declarations are legal, shared by the schema DSL's
97
200
  * `table()` and the engine's `createTable` so the two entry points (and the wire path between
98
- * them) can never drift: defaults require non-nullable columns, "now" is datetime-only,
99
- * auto-increment is the number unique key, and the unique key never defaults to a constant.
201
+ * them) can never drift. Storage owns structural and literal validation; the engine additionally
202
+ * parses and type-checks SQL expressions before catalog mutation.
100
203
  */
101
204
  export declare function validateColumnDefault(column: {
102
205
  name: string;
103
206
  type: SimpleDataType;
207
+ integer?: true;
208
+ sqlDomain?: SqlDomain;
104
209
  nullable: boolean;
105
210
  isUniqueKey: boolean;
106
211
  enumValues?: readonly string[];
@@ -118,26 +223,67 @@ export interface FtsColumnIndexRecord {
118
223
  /** Manifest version the base build covers; commit deltas above it merge at read time. */
119
224
  buildFromVersion: number;
120
225
  }
226
+ export type SecondaryIndexState = "building" | "ready" | "invalid";
227
+ export type SecondaryIndexDirection = "asc" | "desc";
228
+ /**
229
+ * One durable secondary index. The physical postings live in the same bounded, immutable
230
+ * base-plus-delta substrate as full-text postings, under `storageColumnId`; keeping the storage
231
+ * identity separate from the catalog column IDs lets one postings generation represent a
232
+ * composite key.
233
+ *
234
+ * Postings are a pruning accelerator, never truth. A keyed table stores a deterministic hash of
235
+ * its immutable unique key (collisions only add false positives); an append-only keyless table
236
+ * stores its hidden row ID. Every scan re-evaluates the SQL predicate against the row.
237
+ */
238
+ export interface SecondaryIndexRecord {
239
+ name: string;
240
+ /** First indexed column, repeated for constant-time scalar catalog access. */
241
+ columnId: string;
242
+ /** Ordered key columns. */
243
+ columnIds: string[];
244
+ /** Declared key direction per column. */
245
+ directions: SecondaryIndexDirection[];
246
+ /** SQL UNIQUE: enforced through an atomic, separately namespaced membership set. */
247
+ unique?: true;
248
+ /** The membership set has been seeded and must be enforced, independent of postings state. */
249
+ uniqueEnforced?: true;
250
+ /** Prefix-free composite encoding used by every v1 secondary index. */
251
+ termEncoding: "tuple-v1";
252
+ storage: "postings-v1";
253
+ storageColumnId: string;
254
+ locator: "row-id" | "key-hash-v1";
255
+ state: SecondaryIndexState;
256
+ /** Identifies the builder allowed to publish a `building` record; omitted otherwise. */
257
+ buildId?: string;
258
+ /** Manifest version the base build covers; commit deltas above it merge at read time. */
259
+ buildFromVersion: number;
260
+ }
261
+ /**
262
+ * Whether a secondary-index replacement changes the contract a staged writer must honor.
263
+ * Physical posting-build state is deliberately excluded: postings are only a reverified pruning
264
+ * accelerator. Index identity, key shape, and UNIQUE enforcement are structural.
265
+ */
266
+ export declare function secondaryIndexWriteContractChanged(previous: Readonly<Record<string, SecondaryIndexRecord>> | null | undefined, next: Readonly<Record<string, SecondaryIndexRecord>> | null | undefined): boolean;
121
267
  export interface TableRecord {
122
268
  id: string;
123
269
  name: string;
124
270
  columns: TableColumnRecord[];
125
271
  uniqueKeyColumnId?: string;
272
+ /** Declared PRIMARY KEY columns. More than one uses the hidden scalar row-addressing key. */
273
+ primaryKeyColumnIds?: string[];
126
274
  uniqueKeyLookupReady?: boolean;
127
275
  /** Full-text index state per column ID. Writers that see this emit commit deltas. */
128
276
  ftsColumns?: Record<string, FtsColumnIndexRecord>;
129
- /** AFTER triggers on this table, fired by the committing writer inside its transaction. */
277
+ /** Durable secondary indexes keyed by stable index ID. */
278
+ secondaryIndexes?: Record<string, SecondaryIndexRecord>;
279
+ /** BEFORE/AFTER triggers on this table, fired by the committing writer in its transaction. */
130
280
  triggers?: TriggerRecord[];
131
- /**
132
- * Single-column FOREIGN KEY constraints (E141-04). The referenced column is the parent's
133
- * unique key, which is what the engine can probe for existence and what its keyed write paths
134
- * address rows by; a parent key never changes, so only ON DELETE has an action to take.
135
- */
281
+ /** FOREIGN KEY constraints. Column tuples are always explicit, including scalar keys. */
136
282
  foreignKeys?: Array<{
137
283
  name: string;
138
- column: string;
284
+ columns: string[];
139
285
  parentTable: string;
140
- parentColumn: string;
286
+ parentColumns: string[];
141
287
  onDelete: "restrict" | "cascade" | "set null";
142
288
  }>;
143
289
  /**
@@ -151,12 +297,10 @@ export interface TableRecord {
151
297
  }>;
152
298
  /**
153
299
  * True when `migrate()` created this table from a schema declaration, which makes the schema
154
- * authoritative over it: dropping the declaration may drop the table. A table created with
155
- * `CREATE TABLE`, or one written before this field existed, is absent-or-false and no
156
- * migration removes it — the same rule views follow, and it matters more here because a
157
- * table holds rows.
300
+ * authoritative over it: dropping the declaration may drop the table. `CREATE TABLE` records
301
+ * false explicitly.
158
302
  */
159
- managed?: boolean;
303
+ managed: boolean;
160
304
  /**
161
305
  * A view rather than a table: the query text it stands for, and no segments of its own. The
162
306
  * `columns` are the query's inferred output schema, so a view answers the same catalog
@@ -165,23 +309,53 @@ export interface TableRecord {
165
309
  view?: {
166
310
  sql: string;
167
311
  /**
168
- * True when `migrate()` created this view from a schema declaration, which makes the schema
169
- * authoritative over it: dropping the declaration drops the view. A view created with
170
- * `CREATE VIEW`, or one written before this field existed, is absent-or-false and no
171
- * migration will remove it.
312
+ * True when `migrate()` created this view from a schema declaration. `CREATE VIEW` records
313
+ * false explicitly.
172
314
  */
173
- managed?: boolean;
315
+ managed: boolean;
316
+ };
317
+ /** Internal catalog object backing CREATE TYPE ... AS ENUM; never exposed as a table. */
318
+ enumType?: {
319
+ name: string;
320
+ values: string[];
321
+ };
322
+ /** Internal catalog object backing a durable PostgreSQL sequence. */
323
+ sequence?: {
324
+ name: string;
325
+ start: number;
326
+ columnId: string;
174
327
  };
175
328
  createdAt: string;
176
- /** Compare-and-swap revision for catalog evolution; records written before it read as 0. */
177
- revision?: number;
329
+ /** Compare-and-swap revision for catalog evolution. */
330
+ revision: number;
178
331
  }
332
+ /** Exact UTF-8 bytes used when the canonical record-wire JSON codec persists a table record. */
333
+ export declare function catalogRecordRetainedBytes(record: TableRecord): number;
334
+ export declare function manifestRecordRetainedBytes(record: Manifest): number;
179
335
  /**
180
- * One AFTER trigger: catalog-persisted on its table record so the catalog epoch makes it
336
+ * Admission charge for one manifest summary, including the exact canonical tombstone bytes that
337
+ * later reclamation may add. Reserving them up front prevents quota deadlock at the byte ceiling.
338
+ */
339
+ export declare function manifestRecordRetainedReservationBytes(record: Manifest): number;
340
+ export declare function segmentRecordRetainedBytes(record: SegmentRecord): number;
341
+ /** Hard catalog cardinality/text bounds shared by engine, adapters, checkpoints, and snapshots. */
342
+ export declare function validateTableRecordBounds(record: TableRecord): void;
343
+ /** Validates the durable identities and ownership rules of one table's secondary indexes. */
344
+ export declare function validateSecondaryIndexes(record: TableRecord): void;
345
+ /** Ordered catalog column IDs for one canonical secondary-index record. */
346
+ export declare function secondaryIndexColumnIds(index: SecondaryIndexRecord): readonly string[];
347
+ /** Declared directions for one canonical secondary-index record. */
348
+ export declare function secondaryIndexDirections(index: SecondaryIndexRecord): readonly SecondaryIndexDirection[];
349
+ /** Unique-membership namespace owned by one physical secondary index. */
350
+ export declare function secondaryUniqueKeyNamespace(tableId: string, indexId: string): string;
351
+ /**
352
+ * One row trigger: catalog-persisted on its table record so the catalog epoch makes it
181
353
  * visible to every tab immediately, and executed by the committing writer inside the same
182
354
  * transaction as the triggering write — the write and its derivations publish atomically.
183
355
  */
184
356
  export interface TriggerRecord {
357
+ /** Immutable durable identity. Names may be reused only after this trigger is gone. */
358
+ id: string;
185
359
  name: string;
186
360
  event: "insert" | "update" | "delete";
187
361
  /**
@@ -210,7 +384,25 @@ export declare class TableRecordConflictError extends Error {
210
384
  readonly name = "TableRecordConflictError";
211
385
  constructor(tableId: string, expectedRevision: number, actualRevision: number | null);
212
386
  }
387
+ /** A catalog drop lost a race with durable work that still owns records for the table. */
388
+ export declare class TableInUseError extends Error {
389
+ readonly tableId: string;
390
+ readonly ownerKind: "transaction" | "compaction job" | "foreign key" | "pending foreign key" | "accelerator build";
391
+ readonly ownerId: string;
392
+ readonly name = "TableInUseError";
393
+ constructor(tableId: string, ownerKind: "transaction" | "compaction job" | "foreign key" | "pending foreign key" | "accelerator build", ownerId: string);
394
+ }
395
+ /** Refuses a commit that would cross the persisted level-zero fragmentation safety ceiling. */
396
+ export declare class CompactionBacklogError extends Error {
397
+ readonly tableName: string;
398
+ readonly levelZeroSegments: number;
399
+ readonly limit: number;
400
+ readonly name = "CompactionBacklogError";
401
+ constructor(tableName: string, levelZeroSegments: number, limit: number);
402
+ }
213
403
  export type SegmentKind = "insert" | "upsert" | "update" | "delete" | "base";
404
+ /** Absolute visible level-zero ceiling; every commit that adds L0 must enforce this or lower. */
405
+ export declare const MAX_LEVEL_ZERO_SEGMENTS = 4096;
214
406
  /** Maps a contiguous segment-row run to its immutable hidden row IDs. */
215
407
  export interface RowIdSpan {
216
408
  readonly rowStart: number;
@@ -225,20 +417,17 @@ export interface SegmentRecord {
225
417
  rowIdStart: bigint;
226
418
  rowIdEndExclusive: bigint;
227
419
  columnBlockIds: Record<string, string[]>;
228
- kind?: SegmentKind;
420
+ kind: SegmentKind;
229
421
  keyColumnId?: string;
230
- /** Missing on legacy records, which are interpreted as level zero. */
231
- level?: number;
232
- /** Missing on legacy records, where commit order supplies the logical order. */
233
- logicalOrder?: number;
422
+ level: number;
423
+ logicalOrder: number;
234
424
  /**
235
425
  * Staging position inside the owning transaction. Orders segments of one commit relative
236
- * to each other (an in-scope update must fold after the in-scope insert it patches);
237
- * missing on legacy records, which never shared a key within one commit.
426
+ * to each other (an in-scope update must fold after the in-scope insert it patches).
238
427
  */
239
- commitOrdinal?: number;
240
- /** Missing on legacy insert/upsert records, which imply one contiguous row-ID span. */
241
- rowIdSpans?: readonly RowIdSpan[];
428
+ commitOrdinal: number;
429
+ /** Empty for a contiguous row-ID envelope; merged bases record every retained range. */
430
+ rowIdSpans: readonly RowIdSpan[];
242
431
  /** Monotone policy ordinal for an immutable append-row-range level-two partition. */
243
432
  readonly partitionOrdinal?: number;
244
433
  createdAt: string;
@@ -247,15 +436,82 @@ export interface RowIdRange {
247
436
  start: bigint;
248
437
  endExclusive: bigint;
249
438
  }
439
+ /** Largest persisted row ID/posting value; the wire format is canonical unsigned 64-bit. */
440
+ export declare const MAX_ROW_ID: bigint;
441
+ /** One past `MAX_ROW_ID`, allowed only for exclusive range/counter ends. */
442
+ export declare const MAX_ROW_ID_EXCLUSIVE_END: bigint;
443
+ /** Auto-increment values are exposed as exact JavaScript numbers and therefore stop here. */
444
+ export declare const MAX_AUTO_INCREMENT_VALUE: bigint;
445
+ export declare const MAX_AUTO_INCREMENT_EXCLUSIVE_END: bigint;
250
446
  export type LeaseKind = "reader" | "backup";
447
+ /** A live owner renews; an abandoned durable pin expires within one hour. */
448
+ export declare const MAX_LEASE_TTL_MS: number;
449
+ /** Durable resource ceilings; adapters sweep expired owners before atomically refusing creation. */
450
+ export declare const MAX_ACTIVE_LEASES = 4096;
451
+ export declare const MAX_ACTIVE_TEMP_OWNERS = 1024;
452
+ export declare const MAX_TEMP_RUNS_PER_OWNER = 1024;
453
+ export declare const MAX_TEMP_PAGES_PER_OWNER = 16384;
454
+ export declare const MAX_TEMP_RUNS_TOTAL = 65536;
455
+ export declare const MAX_TEMP_PAGES_TOTAL = 262144;
456
+ export declare const MAX_TEMP_BYTES_PER_OWNER: number;
457
+ export declare const MAX_TEMP_BYTES_TOTAL: number;
458
+ export declare const MAX_ACTIVE_COMPACTION_JOBS = 1024;
459
+ export declare const MAX_ACTIVE_GARBAGE_COLLECTION_JOBS = 1;
460
+ export declare const MAX_ACTIVE_UNIQUE_KEY_BUILDS = 1024;
461
+ export declare const MAX_ACTIVE_FTS_BASE_BUILDS = 128;
462
+ export declare const MAX_ACTIVE_SECONDARY_INDEX_BUILDS = 128;
463
+ export declare const MAX_ACCELERATOR_BUILD_STAGED_BYTES_TOTAL: number;
464
+ export declare const MAX_ACCELERATOR_BUILD_STAGED_ENTRIES_TOTAL = 16777216;
465
+ export declare const MAX_ACTIVE_TRANSACTIONS = 4096;
466
+ export declare const MAX_GLOBAL_STAGED_ARTIFACT_BYTES: number;
467
+ export declare const MAX_GLOBAL_STAGED_BLOCKS = 65536;
468
+ export declare const MAX_GLOBAL_STAGED_SEGMENTS = 65536;
469
+ /** Catalog enumeration remains bounded even for cold public inspection APIs. */
470
+ export declare const MAX_CATALOG_RECORDS = 4096;
471
+ /** Total canonical UTF-8 record-wire bytes retained by the durable catalog. */
472
+ export declare const MAX_CATALOG_RETAINED_BYTES: number;
473
+ export declare const MAX_MANIFEST_RECORDS = 65536;
474
+ export declare const MAX_MANIFEST_RETAINED_BYTES: number;
475
+ export declare const MAX_SEGMENT_RECORDS = 1048576;
476
+ export declare const MAX_SEGMENT_RETAINED_BYTES: number;
477
+ /** Durable diagnostic/provenance tails are pruned before admitting more terminal records. */
478
+ export declare const MAX_TERMINAL_TRANSACTION_RECORDS = 65536;
479
+ export declare const MAX_TERMINAL_COMPACTION_JOB_RECORDS = 4096;
480
+ export declare const MAX_COMPLETED_GARBAGE_COLLECTION_JOB_RECORDS = 1024;
481
+ /** An old renewable pin may not force unbounded history retention while writes continue. */
482
+ export declare const MAX_PINNED_MANIFEST_VERSION_LAG = 4096;
483
+ export declare const MAX_PINNED_RETIRED_BLOCKS = 65536;
484
+ export declare const MAX_PINNED_RETIRED_BYTES: number;
485
+ /** Total obsolete physical history is refused before it can consume an origin indefinitely. */
486
+ export declare const MAX_RETIRED_HISTORY_BYTES: number;
487
+ /** A durable resource family reached its fixed corruption/growth safety ceiling. */
488
+ export declare class StorageResourceLimitError extends Error {
489
+ readonly resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte";
490
+ readonly count: number;
491
+ readonly limit: number;
492
+ readonly name = "StorageResourceLimitError";
493
+ constructor(resource: "lease" | "temp owner" | "temp run" | "temp page" | "temporary run total" | "temporary page total" | "temp owner byte" | "temporary byte" | "compaction job" | "garbage collection job" | "unique-key build" | "full-text build" | "secondary-index build" | "accelerator build byte" | "accelerator build entry" | "transaction" | "staged artifact byte" | "staged block" | "staged segment" | "catalog record" | "catalog byte" | "manifest record" | "manifest byte" | "segment record" | "segment byte" | "terminal transaction" | "terminal compaction job" | "completed garbage collection job" | "pinned manifest version lag" | "pinned retired block" | "pinned retired byte" | "snapshot accelerator byte" | "snapshot accelerator entry" | "retired history byte", count: number, limit: number);
494
+ }
251
495
  export interface LeaseRecord {
252
496
  id: string;
253
497
  kind: LeaseKind;
254
498
  manifestVersion: number | null;
255
499
  ownerId: string;
500
+ /** Issuance cutoff used to prove the initial expiry is bounded. */
501
+ createdAt: string;
256
502
  expiresAt: string;
257
503
  revision: number;
258
504
  }
505
+ export interface RenewLeaseInput {
506
+ id: string;
507
+ expectedRevision: number;
508
+ /** A persisted expiry at or before this boundary is irrevocably expired. */
509
+ expiresAtCutoff: string;
510
+ expiresAt: string;
511
+ }
512
+ export interface MoveLeaseInput extends RenewLeaseInput {
513
+ manifestVersion: number | null;
514
+ }
259
515
  export declare const compactionJobStates: readonly ["planned", "running", "ready", "published", "cancelled", "aborted"];
260
516
  export type CompactionJobState = (typeof compactionJobStates)[number];
261
517
  export interface CompactionJobCursor {
@@ -399,19 +655,18 @@ export interface CompactionJobRecord {
399
655
  sourceStoredBytes: number;
400
656
  outputStoredBytes: number;
401
657
  logicalBytes: number;
402
- /** Missing on Phase 6A records, which normalize to copy-v1. */
403
- readonly rewritePlan?: CompactionRewritePlan;
658
+ readonly rewritePlan: CompactionRewritePlan;
404
659
  /** Null for copy-v1; points at the next output for rechunk-v1. */
405
- outputCursor?: CompactionOutputCursor | null;
660
+ outputCursor: CompactionOutputCursor | null;
406
661
  /** Immutable execution budget. Zero for copy-v1 jobs. */
407
- readonly memoryBudgetBytes?: number;
662
+ readonly memoryBudgetBytes: number;
408
663
  /** Immutable planner estimate. Zero for copy-v1 jobs. */
409
- readonly minimumMemoryBytes?: number;
410
- /** Immutable stored bytes from newly promoted level-zero sources. Missing on legacy jobs. */
411
- readonly level0SourceStoredBytes?: number;
412
- /** Immutable stored bytes from the retained level-one anchor. Missing on legacy jobs. */
413
- readonly anchorSourceStoredBytes?: number;
414
- /** Output partition assigned by the append-row-range L2 policy. Missing on legacy jobs. */
664
+ readonly minimumMemoryBytes: number;
665
+ /** Immutable stored bytes from newly promoted level-zero sources. */
666
+ readonly level0SourceStoredBytes: number;
667
+ /** Immutable stored bytes from the retained level-one anchor. */
668
+ readonly anchorSourceStoredBytes: number;
669
+ /** Output partition assigned by the append-row-range L2 policy. */
415
670
  readonly outputPartitionOrdinal?: number;
416
671
  /** Immutable maximum compaction output bytes per newly promoted L0 byte. */
417
672
  readonly maxWriteAmplification?: number;
@@ -425,8 +680,8 @@ export interface CompactionJobRecord {
425
680
  * write-amplification budget.
426
681
  */
427
682
  readonly priorAttemptOutputStoredBytes?: number;
428
- peakWorkingBytes?: number;
429
- outputLogicalBytes?: number;
683
+ peakWorkingBytes: number;
684
+ outputLogicalBytes: number;
430
685
  targetLevel: number;
431
686
  state: CompactionJobState;
432
687
  transactionId: string | null;
@@ -454,6 +709,16 @@ export interface CompactionJobRecordUpdate {
454
709
  updatedAt: string;
455
710
  error?: string | null;
456
711
  }
712
+ /** Every segment ID owned by a compaction job, including merge partition outputs. */
713
+ export declare function compactionOutputSegmentIds(job: Pick<CompactionJobRecord, "outputSegmentId" | "rewritePlan">): string[];
714
+ /**
715
+ * Proves that a compaction transaction stages exactly the immutable job output. This is kept in
716
+ * the storage contract (rather than only in the engine) so adapters reject forged, reordered, or
717
+ * partially journaled output before publication and while loading durable state.
718
+ */
719
+ export declare function assertCompactionOutputProvenance(job: CompactionJobRecord, table: TableRecord, transaction: Pick<TransactionRecord, "id" | "pendingBlockIds" | "pendingSegmentIds">, sourceSegments: readonly SegmentRecord[], outputSegments: readonly SegmentRecord[], options?: {
720
+ readonly allowOutputPrefix?: boolean;
721
+ }): void;
457
722
  export declare class CompactionJobConflictError extends Error {
458
723
  readonly jobId: string;
459
724
  readonly expectedRevision: number;
@@ -489,6 +754,52 @@ export interface CreateGarbageCollectionJobInput {
489
754
  /** Fixed cutoff used to decide which persisted leases protect a manifest for this job. */
490
755
  leaseCutoff: string;
491
756
  createdAt: string;
757
+ /** Present while bounded discovery is still paging durable metadata. */
758
+ discovery?: GarbageCollectionDiscovery;
759
+ }
760
+ export type GarbageCollectionDiscoveryPhase = "manifests" | "manifest-blocks" | "segments" | "transactions" | "compactions" | "complete";
761
+ export interface GarbageCollectionDiscovery {
762
+ phase: GarbageCollectionDiscoveryPhase;
763
+ currentManifestVersion: number | null;
764
+ retainAboveVersion: number;
765
+ retainAfter: number;
766
+ maxPlanningItems: number;
767
+ manifestCursor: number | null;
768
+ segmentCursor: string | null;
769
+ transactionCursor: string | null;
770
+ compactionCursor: string | null;
771
+ visitedRecords: number;
772
+ /**
773
+ * When a bounded candidate array fills, reclamation completes this job and the next job
774
+ * resumes this discovery phase instead of restarting at the beginning.
775
+ */
776
+ resumePhase?: Exclude<GarbageCollectionDiscoveryPhase, "complete"> | null;
777
+ /** Older continuation resumed only after this job first discovers newly eligible manifests. */
778
+ postManifestPhase?: Exclude<GarbageCollectionDiscoveryPhase, "complete" | "manifests"> | null;
779
+ /** Exact within-record provenance offset; every named artifact is examined at most once. */
780
+ artifactCursor?: GarbageCollectionArtifactCursor | null;
781
+ }
782
+ export interface GarbageCollectionArtifactCursor {
783
+ family: "manifest" | "transaction" | "compaction";
784
+ /** Decimal manifest version for `manifest`; durable record ID for the other families. */
785
+ recordId: string;
786
+ /** Exact lexical manifest-membership cursor; null for transaction/compaction arrays. */
787
+ blockId: string | null;
788
+ blockIndex: number;
789
+ segmentIndex: number;
790
+ }
791
+ /** Absolute per-call bound for durable maintenance candidate arrays and storage transactions. */
792
+ export declare const MAX_MAINTENANCE_BATCH_ITEMS = 1024;
793
+ export declare function boundedMaintenanceBatchItems(value: number, label: string): number;
794
+ export interface UpdateGarbageCollectionPlanningInput {
795
+ jobId: string;
796
+ expectedRevision: number;
797
+ candidateManifestVersions?: readonly number[];
798
+ candidateSegmentIds?: readonly string[];
799
+ candidateBlockIds?: readonly string[];
800
+ candidateTransactionIds?: readonly string[];
801
+ discovery: GarbageCollectionDiscovery;
802
+ updatedAt: string;
492
803
  }
493
804
  export interface GarbageCollectionJobRecord {
494
805
  id: string;
@@ -516,6 +827,8 @@ export interface GarbageCollectionJobRecord {
516
827
  leaseCutoff: string;
517
828
  createdAt: string;
518
829
  updatedAt: string;
830
+ /** Omitted only for explicitly supplied bounded candidate jobs; no discovery scan is needed. */
831
+ discovery?: GarbageCollectionDiscovery;
519
832
  }
520
833
  export interface RunGarbageCollectionStepInput {
521
834
  jobId: string;
@@ -584,9 +897,29 @@ export declare class LeaseConflictError extends Error {
584
897
  readonly name = "LeaseConflictError";
585
898
  constructor(leaseId: string, expectedRevision: number, actualRevision: number | null);
586
899
  }
900
+ /** A renewal/move cutoff reached the persisted expiry; expiry is irrevocable. */
901
+ export declare class LeaseExpiredError extends Error {
902
+ readonly leaseId: string;
903
+ readonly expiresAt: string;
904
+ readonly expiresAtCutoff: string;
905
+ readonly name = "LeaseExpiredError";
906
+ constructor(leaseId: string, expiresAt: string, expiresAtCutoff: string);
907
+ }
908
+ /** Refuses a lease release from a different durable owner. */
909
+ export declare class LeaseOwnerConflictError extends Error {
910
+ readonly leaseId: string;
911
+ readonly expectedOwnerId: string;
912
+ readonly actualOwnerId: string;
913
+ readonly name = "LeaseOwnerConflictError";
914
+ constructor(leaseId: string, expectedOwnerId: string, actualOwnerId: string);
915
+ }
587
916
  export type TransactionStatus = "active" | "committed" | "aborted";
588
917
  export interface TransactionRecord {
589
918
  id: string;
919
+ /** Stable writer identity; liveness renewals never contend on the data revision. */
920
+ ownerId: string;
921
+ /** Durable writer liveness deadline. Expired active records are atomically aborted by GC. */
922
+ expiresAt: string;
590
923
  snapshotVersion: number | null;
591
924
  pendingBlockIds: string[];
592
925
  pendingSegmentIds: string[];
@@ -595,6 +928,33 @@ export interface TransactionRecord {
595
928
  startedAt: string;
596
929
  updatedAt: string;
597
930
  committedVersion: number | null;
931
+ /**
932
+ * Structural catalog epoch captured atomically with the transaction snapshot. Every active
933
+ * transaction carries this storage-owned guard; commit rejects it if DDL changed the schema
934
+ * used to prepare the staged artifacts. Terminal records drop the guard.
935
+ */
936
+ schemaEpochGuard?: number;
937
+ /**
938
+ * Catalog record owned exclusively by this active transaction. It is invisible to catalog
939
+ * reads until commit; abort/expiry discards the reservation with the transaction.
940
+ */
941
+ pendingTable?: TableRecord;
942
+ /** Next row id for the pending table, advanced atomically as insert artifacts stage. */
943
+ pendingTableNextRowId?: bigint;
944
+ /** Catalog epoch captured atomically when the pending table was admitted. */
945
+ catalogEpochGuard?: number;
946
+ }
947
+ export interface AbortTransactionIfExpiredInput {
948
+ transactionId: string;
949
+ expectedOwnerId: string;
950
+ expiresAtCutoff: string;
951
+ updatedAt: string;
952
+ }
953
+ export interface RenewTransactionInput {
954
+ transactionId: string;
955
+ ownerId: string;
956
+ expiresAtCutoff: string;
957
+ expiresAt: string;
598
958
  }
599
959
  export interface TransactionRecordUpdate {
600
960
  snapshotVersion?: number | null;
@@ -603,10 +963,15 @@ export interface TransactionRecordUpdate {
603
963
  status?: TransactionStatus;
604
964
  updatedAt: string;
605
965
  committedVersion?: number | null;
966
+ /** Storage-owned advancement while staging rows for a pending table. */
967
+ pendingTableNextRowId?: bigint;
606
968
  }
607
969
  export interface BeginTransactionInput {
608
- /** Record to create; the store stamps `snapshotVersion` with the current manifest version. */
609
- record: Omit<TransactionRecord, "snapshotVersion">;
970
+ /**
971
+ * Record to create; the store atomically stamps both `snapshotVersion` and
972
+ * `schemaEpochGuard` from its current state.
973
+ */
974
+ record: Omit<TransactionRecord, "snapshotVersion" | "schemaEpochGuard">;
610
975
  /** Reserve this many row ids for the table in the same atomic step. */
611
976
  reserveRowIds?: {
612
977
  tableId: string;
@@ -622,6 +987,15 @@ export interface BeginTransactionInput {
622
987
  count: number;
623
988
  atLeast?: bigint;
624
989
  };
990
+ /**
991
+ * Reserves one invisible catalog record and its initial row-id state in the same atomic step.
992
+ * The expected epoch closes schema/FK races between planning and begin; commit rechecks it.
993
+ */
994
+ pendingTable?: {
995
+ record: TableRecord;
996
+ nextRowId: bigint;
997
+ expectedCatalogEpoch: number;
998
+ };
625
999
  }
626
1000
  export interface BeginTransactionResult {
627
1001
  record: TransactionRecord;
@@ -635,6 +1009,332 @@ export interface StageTransactionArtifactsInput {
635
1009
  segments: readonly SegmentRecord[];
636
1010
  updatedAt: string;
637
1011
  }
1012
+ /** Maximum immutable payloads accepted by one atomic staging/WAL operation. */
1013
+ export declare const MAX_TRANSACTION_STAGE_BLOCKS = 64;
1014
+ /** Maximum segment records accepted by one atomic staging/WAL operation. */
1015
+ export declare const MAX_TRANSACTION_STAGE_SEGMENTS = 64;
1016
+ /**
1017
+ * Maximum block bytes accepted by one atomic staging/WAL operation. The limit still admits one
1018
+ * maximum-size physical block; callers split collections of smaller blocks into bounded calls.
1019
+ */
1020
+ export declare const MAX_TRANSACTION_STAGE_BYTES: number;
1021
+ /** Durable journal ceilings prevent a forgotten or hostile transaction growing without bound. */
1022
+ export declare const MAX_TRANSACTION_PENDING_BLOCKS = 4096;
1023
+ export declare const MAX_TRANSACTION_PENDING_SEGMENTS = 4096;
1024
+ /** Storage-boundary preflight shared by staging and single-shot write implementations. */
1025
+ export declare function assertTransactionArtifactBatchLimits(blocks: readonly BlockWrite[], segments: readonly SegmentRecord[]): void;
1026
+ /** Preflight for the complete persisted journal after a bounded staging operation. */
1027
+ export declare function assertTransactionArtifactJournalLimits(pendingBlockIds: readonly string[], pendingSegmentIds: readonly string[]): void;
1028
+ /**
1029
+ * Atomically rewinds one active transaction's artifact journal to an earlier set and removes
1030
+ * exactly the artifacts that are no longer reachable. Journals are canonically sorted, so an
1031
+ * adapter validates the retained and removed lists as a duplicate-free, disjoint, exact
1032
+ * partition of its current journal before changing records or bytes.
1033
+ */
1034
+ export interface RollbackTransactionArtifactsInput {
1035
+ transactionId: string;
1036
+ expectedRevision: number;
1037
+ pendingBlockIds: readonly string[];
1038
+ pendingSegmentIds: readonly string[];
1039
+ removeBlockIds: readonly string[];
1040
+ removeSegmentIds: readonly string[];
1041
+ updatedAt: string;
1042
+ }
1043
+ export type StorageIntegrityMode = "metadata" | "full";
1044
+ export interface StorageIntegrityIssue {
1045
+ readonly code: string;
1046
+ readonly location: string;
1047
+ readonly message: string;
1048
+ }
1049
+ export interface StorageIntegrityReport {
1050
+ readonly mode: StorageIntegrityMode;
1051
+ readonly ok: boolean;
1052
+ readonly checkedRecords: number;
1053
+ readonly checkedBlocks: number;
1054
+ readonly checkedBytes: number;
1055
+ /** Total findings; `issues` itself is bounded by `maxIssues`. */
1056
+ readonly issueCount: number;
1057
+ readonly issues: readonly StorageIntegrityIssue[];
1058
+ }
1059
+ export interface StorageStats {
1060
+ readonly backend: string;
1061
+ /** Bytes reachable as current database records or payloads. */
1062
+ readonly logicalBytes: number;
1063
+ /** Actual substrate allocation where available; IndexedDB cannot report this per database. */
1064
+ readonly physicalBytes: number | null;
1065
+ readonly liveBlockCount: number;
1066
+ /** Stored payloads outside the current manifest, including staged and retired bytes. */
1067
+ readonly obsoleteBlockCount: number;
1068
+ readonly liveBlockBytes: number;
1069
+ readonly obsoleteBlockBytes: number;
1070
+ readonly temporaryBytes: number;
1071
+ readonly walBytes: number | null;
1072
+ readonly checkpointBytes: number | null;
1073
+ readonly orphanBytes: number | null;
1074
+ readonly manifestCount: number;
1075
+ readonly transactionCount: number;
1076
+ readonly segmentCount: number;
1077
+ readonly maintenance?: {
1078
+ readonly degraded: boolean;
1079
+ readonly consecutiveFailures: number;
1080
+ readonly lastError: string | null;
1081
+ readonly walLimitBytes: number | null;
1082
+ /** Durable garbage awaiting safe physical deletion, when the substrate separates the two. */
1083
+ readonly cleanupDebtBytes: number | null;
1084
+ /** Refusal/backpressure ceiling for cleanup debt, or null when the substrate has no debt. */
1085
+ readonly cleanupLimitBytes: number | null;
1086
+ };
1087
+ }
1088
+ export interface InterruptedSnapshotImport {
1089
+ readonly identity: string;
1090
+ readonly version: number;
1091
+ readonly createdAt: string;
1092
+ readonly stagedBlockCount: number;
1093
+ readonly stagedBytes: number;
1094
+ }
1095
+ export interface InterruptedSnapshotImportAbortResult {
1096
+ readonly identity: string;
1097
+ readonly removedBlockCount: number;
1098
+ readonly removedBytes: number;
1099
+ }
1100
+ /** A snapshot session is renewable but can never pin storage for more than one hour unattended. */
1101
+ export declare const MAX_SNAPSHOT_SESSION_TTL_MS: number;
1102
+ /** Snapshot v1 is an ordered bounded record stream; no database-sized collection is in header. */
1103
+ export declare const SNAPSHOT_FRAME_KINDS: readonly ["catalog-page", "segment-page", "transaction-page", "unique-page", "posting-page", "block"];
1104
+ export type SnapshotFrameKind = (typeof SNAPSHOT_FRAME_KINDS)[number];
1105
+ export declare const MAX_SNAPSHOT_FRAME_ITEMS = 1024;
1106
+ export declare const MAX_SNAPSHOT_METADATA_FRAME_BYTES: number;
1107
+ export declare const MAX_SNAPSHOT_FRAME_BATCH_ITEMS = 64;
1108
+ export declare const MAX_SNAPSHOT_METADATA_BATCH_BYTES: number;
1109
+ /** One block frame is permitted; metadata frames still use MAX_SNAPSHOT_METADATA_BATCH_BYTES. */
1110
+ export declare const MAX_SNAPSHOT_FRAME_BATCH_BYTES: number;
1111
+ /**
1112
+ * Framed restore builds accelerator generations in a disposable core before one atomic publish.
1113
+ * These aggregate ceilings keep that validation heap bounded until generations become a native
1114
+ * chunk-backed structure rather than one materialized in-memory collection.
1115
+ */
1116
+ export declare const MAX_SNAPSHOT_IMPORT_ACCELERATOR_RETAINED_BYTES: number;
1117
+ export declare const MAX_SNAPSHOT_IMPORT_ACCELERATOR_RETAINED_ENTRIES = 16777216;
1118
+ export interface SnapshotKindSummary {
1119
+ readonly frameCount: number;
1120
+ readonly itemCount: number;
1121
+ readonly storedBytes: number;
1122
+ }
1123
+ /** One table plus its durable allocation counters; memberships/postings are separate pages. */
1124
+ export interface SnapshotCatalogItem {
1125
+ readonly kind: "table";
1126
+ readonly record: TableRecord;
1127
+ readonly nextRowId: bigint;
1128
+ readonly autoIncrement: ReadonlyArray<{
1129
+ readonly columnId: string;
1130
+ readonly next: bigint;
1131
+ }>;
1132
+ }
1133
+ export interface SnapshotSegmentItem {
1134
+ readonly kind: "segment";
1135
+ readonly record: SegmentRecord;
1136
+ }
1137
+ export interface SnapshotTransactionItem {
1138
+ readonly kind: "transaction";
1139
+ readonly record: TransactionRecord;
1140
+ }
1141
+ /** Descriptor precedes this generation's chunks; tokens are globally strict lexical order. */
1142
+ export interface SnapshotUniqueGenerationItem {
1143
+ readonly kind: "unique-generation";
1144
+ readonly tableId: string;
1145
+ readonly indexId: string | null;
1146
+ readonly namespaceId: string;
1147
+ readonly generationId: string;
1148
+ readonly chunkCount: number;
1149
+ readonly tokenCount: number;
1150
+ }
1151
+ export interface SnapshotUniqueChunkItem {
1152
+ readonly kind: "unique-chunk";
1153
+ readonly namespaceId: string;
1154
+ readonly generationId: string;
1155
+ readonly ordinal: number;
1156
+ readonly keyTokens: readonly string[];
1157
+ }
1158
+ export type SnapshotUniqueItem = SnapshotUniqueGenerationItem | SnapshotUniqueChunkItem;
1159
+ /**
1160
+ * Complete canonical postings generation at the captured version. Both full-text and scalar
1161
+ * secondary accelerators use this base format; deltas are merged during bounded export, so a
1162
+ * restore never needs pre-snapshot history.
1163
+ */
1164
+ export interface SnapshotPostingGenerationItem {
1165
+ readonly kind: "posting-generation";
1166
+ readonly tableId: string;
1167
+ readonly ownerKind: "fts-column" | "secondary-index";
1168
+ readonly ownerId: string;
1169
+ readonly storageColumnId: string;
1170
+ readonly generationId: string;
1171
+ readonly coversVersion: number;
1172
+ readonly chunkCount: number;
1173
+ /** Exact safe sum of every posting frequency; secondary-index frequencies are always one. */
1174
+ readonly totalTokens: number;
1175
+ }
1176
+ export interface SnapshotPostingChunkItem {
1177
+ readonly kind: "posting-chunk";
1178
+ readonly storageColumnId: string;
1179
+ readonly generationId: string;
1180
+ readonly ordinal: number;
1181
+ readonly postings: readonly FtsPosting[];
1182
+ }
1183
+ export type SnapshotPostingItem = SnapshotPostingGenerationItem | SnapshotPostingChunkItem;
1184
+ export type SnapshotMetadataItem = SnapshotCatalogItem | SnapshotSegmentItem | SnapshotTransactionItem | SnapshotUniqueItem | SnapshotPostingItem;
1185
+ /** Modeled heap retained by one framed accelerator chunk during atomic snapshot validation. */
1186
+ export declare function snapshotAcceleratorItemRetainedUsage(item: SnapshotUniqueChunkItem | SnapshotPostingChunkItem): {
1187
+ bytes: number;
1188
+ entries: number;
1189
+ };
1190
+ /** Shared arithmetic boundary used by streaming adapters and allocation-free cap tests. */
1191
+ export declare function assertSnapshotImportAcceleratorUsage(bytes: number, entries: number): void;
1192
+ /**
1193
+ * Canonical compressed container header. It remains O(1) with database size: the six entries
1194
+ * below contain counts and byte totals only, never record descriptors, keys, or postings.
1195
+ */
1196
+ export interface SnapshotFrameStreamHeader {
1197
+ readonly formatVersion: 1;
1198
+ readonly databaseVersion: number;
1199
+ readonly createdAt: string;
1200
+ readonly kinds: Readonly<Record<SnapshotFrameKind, SnapshotKindSummary>>;
1201
+ }
1202
+ /**
1203
+ * One exact body frame. Frames are globally contiguous by `sequence` and grouped in
1204
+ * `SNAPSHOT_FRAME_KINDS` order. Within kinds, tables/segments/transactions/blocks order by ID;
1205
+ * UNIQUE generations order by namespace (descriptor, then contiguous chunk ordinals), and
1206
+ * posting generations by storageColumnId using the same descriptor/chunk rule. Metadata
1207
+ * payloads use canonical adapter encoding and contain at
1208
+ * most MAX_SNAPSHOT_FRAME_ITEMS/MAX_SNAPSHOT_METADATA_FRAME_BYTES; a block frame contains one
1209
+ * ID plus one stored block and may use MAX_STORED_BLOCK_BYTE_LENGTH. `checksum` covers the exact
1210
+ * payload. Metadata payloads use the single core-owned snapshot wire codec; adapters must not
1211
+ * substitute substrate encodings. Lost-ack replay must additionally compare the complete
1212
+ * persisted bytes.
1213
+ */
1214
+ export interface SnapshotFrame {
1215
+ readonly sequence: number;
1216
+ readonly kind: SnapshotFrameKind;
1217
+ readonly itemCount: number;
1218
+ /** Block ID for a block frame; null for core-wire-encoded metadata pages. */
1219
+ readonly key: string | null;
1220
+ readonly payload: Uint8Array;
1221
+ readonly checksum: number;
1222
+ }
1223
+ export interface SnapshotFrameFooter {
1224
+ readonly frameCount: number;
1225
+ readonly itemCount: number;
1226
+ readonly storedBytes: number;
1227
+ /** Rolling checksum over every canonical frame header and payload in sequence order. */
1228
+ readonly checksum: number;
1229
+ }
1230
+ export interface BeginSnapshotFrameExportInput {
1231
+ readonly ownerId: string;
1232
+ readonly createdAt: string;
1233
+ readonly expiresAt: string;
1234
+ }
1235
+ /**
1236
+ * Export begin atomically captures a frozen catalog/segment/transaction/accelerator generation
1237
+ * into bounded durable pages and records only an exact manifest version/cursor for blocks (it
1238
+ * must not copy block descriptors). It creates the backup lease in the same operation.
1239
+ * Catalog-only writes after begin cannot change the session.
1240
+ */
1241
+ export interface SnapshotFrameExportSession {
1242
+ readonly sessionId: string;
1243
+ readonly ownerId: string;
1244
+ readonly expiresAt: string;
1245
+ readonly header: SnapshotFrameStreamHeader;
1246
+ }
1247
+ export interface ReadSnapshotExportFrameInput {
1248
+ readonly sessionId: string;
1249
+ readonly ownerId: string;
1250
+ readonly sequence: number;
1251
+ readonly expiresAtCutoff: string;
1252
+ readonly expiresAt: string;
1253
+ }
1254
+ export interface BeginSnapshotFrameImportInput {
1255
+ readonly identity: string;
1256
+ readonly ownerId: string;
1257
+ readonly createdAt: string;
1258
+ readonly expiresAt: string;
1259
+ readonly header: SnapshotFrameStreamHeader;
1260
+ }
1261
+ export interface SnapshotFrameImportSession {
1262
+ readonly identity: string;
1263
+ readonly ownerId: string;
1264
+ readonly version: number;
1265
+ readonly createdAt: string;
1266
+ readonly expiresAt: string;
1267
+ readonly nextSequence: number;
1268
+ readonly stagedBytes: number;
1269
+ }
1270
+ export interface RenewSnapshotFrameImportInput {
1271
+ readonly identity: string;
1272
+ readonly ownerId: string;
1273
+ readonly expiresAtCutoff: string;
1274
+ readonly expiresAt: string;
1275
+ }
1276
+ export interface AppendSnapshotImportFramesInput extends RenewSnapshotFrameImportInput {
1277
+ /** Bounded contiguous frames; replayed sequences compare complete persisted bytes. */
1278
+ readonly frames: readonly SnapshotFrame[];
1279
+ }
1280
+ export interface FinishSnapshotFrameImportInput {
1281
+ readonly identity: string;
1282
+ readonly ownerId: string;
1283
+ readonly expiresAtCutoff: string;
1284
+ readonly footer: SnapshotFrameFooter;
1285
+ }
1286
+ export interface CloseSnapshotExportInput {
1287
+ readonly sessionId: string;
1288
+ readonly ownerId: string;
1289
+ }
1290
+ export interface CancelSnapshotImportInput {
1291
+ readonly identity: string;
1292
+ readonly ownerId: string;
1293
+ }
1294
+ export declare class SnapshotImportConflictError extends Error {
1295
+ readonly identity: string;
1296
+ readonly ownerId: string;
1297
+ readonly name = "SnapshotImportConflictError";
1298
+ constructor(identity: string, ownerId: string, message: string);
1299
+ }
1300
+ /**
1301
+ * Persisted bytes belong to a recognized storage family, but not to the version this reader
1302
+ * supports. This is deliberately distinct from corruption: callers must not infer that repair,
1303
+ * recreation, or deletion is safe merely because versions differ.
1304
+ */
1305
+ export declare class StorageFormatVersionError extends Error {
1306
+ readonly backend: string;
1307
+ readonly location: string;
1308
+ readonly actualVersion: number | null;
1309
+ readonly supportedVersion: number;
1310
+ readonly relation: "older" | "newer";
1311
+ readonly name = "StorageFormatVersionError";
1312
+ constructor(backend: string, location: string, actualVersion: number | null, supportedVersion: number, relation: "older" | "newer");
1313
+ }
1314
+ /** A native IndexedDB schema upgrade is waiting for another connection to close. */
1315
+ export declare class IndexedDbSchemaUpgradeBlockedError extends Error {
1316
+ readonly databaseName: string;
1317
+ readonly oldVersion: number;
1318
+ readonly requestedVersion: number;
1319
+ readonly name = "IndexedDbSchemaUpgradeBlockedError";
1320
+ constructor(databaseName: string, oldVersion: number, requestedVersion: number);
1321
+ }
1322
+ /** Persisted metadata or payload failed an adapter's fail-closed integrity checks. */
1323
+ export declare class StorageCorruptionError extends Error {
1324
+ readonly backend: string;
1325
+ readonly location: string;
1326
+ readonly name = "StorageCorruptionError";
1327
+ constructor(backend: string, location: string, message: string);
1328
+ }
1329
+ /**
1330
+ * A remote OPFS leader may have committed a mutation whose acknowledgement was lost.
1331
+ * Reconcile stable identities or revisions before retrying the named operation.
1332
+ */
1333
+ export declare class OpfsUncertainOutcomeError extends Error {
1334
+ readonly method: string;
1335
+ readonly name = "OpfsUncertainOutcomeError";
1336
+ constructor(method: string);
1337
+ }
638
1338
  /**
639
1339
  * The commit input carries only the change: added blocks are the transaction's journaled pending
640
1340
  * blocks, removals are the superseded ids. The store derives the published manifest from its
@@ -645,7 +1345,22 @@ export interface CommitTransactionInput {
645
1345
  changedTableIds?: readonly string[];
646
1346
  expectedTransactionRevision: number;
647
1347
  expectedManifestVersion: number | null;
1348
+ /**
1349
+ * Required when blocks are retired. The store atomically proves this ready compaction job is
1350
+ * owned by the transaction and that `removedBlockIds` exactly names its still-current,
1351
+ * unaliased source set. Ordinary commits cannot retire visible blocks.
1352
+ */
1353
+ compactionJobId?: string;
648
1354
  removedBlockIds?: readonly string[];
1355
+ /**
1356
+ * Atomic post-commit fragmentation ceilings. Exactly one entry is mandatory for every table
1357
+ * receiving a pending level-zero segment, no unrelated entry is allowed, and no limit may
1358
+ * exceed `MAX_LEVEL_ZERO_SEGMENTS`.
1359
+ */
1360
+ levelZeroSegmentLimits?: ReadonlyArray<{
1361
+ tableId: string;
1362
+ limit: number;
1363
+ }>;
649
1364
  /**
650
1365
  * Per-table unique-key changes, in operation order. Multi-entry commits come from atomic
651
1366
  * write scopes; entries for the same table apply sequentially, so in-scope conflicts
@@ -668,7 +1383,9 @@ export interface WriteTransactionInput extends Omit<CommitTransactionInput, "tra
668
1383
  * auto-increment reservation); its journal is extended and its revision compare-and-swapped
669
1384
  * (`TransactionRecordConflictError`). `{ record }` begins the transaction in the same step —
670
1385
  * a fresh record (revision 0, empty journal) the store pins at `expectedManifestVersion` —
671
- * so a write that needed no reservation costs one round trip in total.
1386
+ * so a write that needed no reservation costs one round trip in total. A fresh record carries
1387
+ * the `schemaEpochGuard` captured with its preparation; the store rejects a stale guard before
1388
+ * persisting any part of the write.
672
1389
  */
673
1390
  transaction: {
674
1391
  id: string;
@@ -685,12 +1402,188 @@ export interface UniqueKeyChanges {
685
1402
  requireAbsent: boolean;
686
1403
  remove?: boolean;
687
1404
  }
1405
+ /** Bounded calls used to seed an arbitrarily large UNIQUE namespace without heap materialization. */
1406
+ export declare const MAX_UNIQUE_KEY_BUILD_TOKENS_PER_CHUNK = 4096;
1407
+ export declare const MAX_UNIQUE_KEY_BUILD_CHUNK_BYTES: number;
1408
+ export declare const MAX_UNIQUE_KEY_BUILD_TTL_MS: number;
1409
+ export declare const MAX_UNIQUE_KEY_BUILD_STAGED_BYTES: number;
1410
+ export declare const MAX_UNIQUE_KEY_BUILD_STAGED_BYTES_TOTAL: number;
1411
+ export interface UniqueKeyBuildRecord {
1412
+ buildId: string;
1413
+ tableId: string;
1414
+ indexId: string;
1415
+ namespaceId: string;
1416
+ ownerId: string;
1417
+ state: "active" | "completed";
1418
+ nextOrdinal: number;
1419
+ tokenCount: number;
1420
+ retainedBytes: number;
1421
+ expiresAt: string;
1422
+ createdAt: string;
1423
+ updatedAt: string;
1424
+ completedAt?: string;
1425
+ }
1426
+ export interface BeginUniqueKeyBuildInput {
1427
+ buildId: string;
1428
+ tableId: string;
1429
+ indexId: string;
1430
+ namespaceId: string;
1431
+ ownerId: string;
1432
+ expiresAt: string;
1433
+ createdAt: string;
1434
+ }
1435
+ export interface AppendUniqueKeyBuildChunkInput {
1436
+ buildId: string;
1437
+ ownerId: string;
1438
+ expiresAtCutoff: string;
1439
+ ordinal: number;
1440
+ keyTokens: readonly string[];
1441
+ updatedAt: string;
1442
+ }
1443
+ export interface FinishUniqueKeyBuildInput {
1444
+ buildId: string;
1445
+ ownerId: string;
1446
+ expiresAtCutoff: string;
1447
+ expectedTableRevision: number;
1448
+ expectedManifestVersion: number | null;
1449
+ chunkCount: number;
1450
+ coversVersion: number;
1451
+ completedAt: string;
1452
+ }
1453
+ export interface RenewUniqueKeyBuildInput {
1454
+ buildId: string;
1455
+ ownerId: string;
1456
+ expiresAtCutoff: string;
1457
+ expiresAt: string;
1458
+ updatedAt: string;
1459
+ }
1460
+ export interface AbortUniqueKeyBuildInput {
1461
+ buildId: string;
1462
+ ownerId: string;
1463
+ expiresAtCutoff: string;
1464
+ }
1465
+ /**
1466
+ * A bounded background fold of one already-enforced UNIQUE namespace. Begin freezes the exact
1467
+ * immutable base generation and delta prefix through `throughVersion`; later commits keep
1468
+ * appending canonical `(namespace, token, version)` add/remove records to a separate ordered
1469
+ * tail. Each step resolves at most one bounded lexical token window
1470
+ * into a new immutable generation. Finish swaps the generation and drops only the frozen tail
1471
+ * prefix atomically, so no commit copies database-sized membership and readers always see an
1472
+ * exact base+tail view.
1473
+ */
1474
+ export interface UniqueKeyFoldRecord {
1475
+ foldId: string;
1476
+ tableId: string;
1477
+ indexId: string | null;
1478
+ namespaceId: string;
1479
+ ownerId: string;
1480
+ state: "active" | "completed";
1481
+ sourceGenerationId: string | null;
1482
+ outputGenerationId: string;
1483
+ throughVersion: number;
1484
+ afterToken: string | null;
1485
+ nextOrdinal: number;
1486
+ tokenCount: number;
1487
+ retainedBytes: number;
1488
+ createdAt: string;
1489
+ updatedAt: string;
1490
+ expiresAt: string;
1491
+ completedAt?: string;
1492
+ }
1493
+ export interface BeginUniqueKeyFoldInput {
1494
+ foldId: string;
1495
+ tableId: string;
1496
+ indexId: string | null;
1497
+ namespaceId: string;
1498
+ ownerId: string;
1499
+ outputGenerationId: string;
1500
+ expectedManifestVersion: number;
1501
+ createdAt: string;
1502
+ expiresAt: string;
1503
+ }
1504
+ export interface RunUniqueKeyFoldStepInput {
1505
+ foldId: string;
1506
+ ownerId: string;
1507
+ expiresAtCutoff: string;
1508
+ expiresAt: string;
1509
+ updatedAt: string;
1510
+ maxTokens: number;
1511
+ }
1512
+ export interface FinishUniqueKeyFoldInput {
1513
+ foldId: string;
1514
+ ownerId: string;
1515
+ expiresAtCutoff: string;
1516
+ expectedSourceGenerationId: string | null;
1517
+ expectedThroughVersion: number;
1518
+ chunkCount: number;
1519
+ completedAt: string;
1520
+ }
1521
+ export interface AbortUniqueKeyFoldInput {
1522
+ foldId: string;
1523
+ ownerId: string;
1524
+ expiresAtCutoff: string;
1525
+ }
1526
+ /** Validates one staged seed call before an adapter opens a transaction or WAL frame. */
1527
+ export declare function uniqueKeyBuildChunkRetainedBytes(keyTokens: readonly string[]): number;
1528
+ export declare class UniqueKeyBuildConflictError extends Error {
1529
+ readonly buildId: string;
1530
+ readonly reason: string;
1531
+ readonly name = "UniqueKeyBuildConflictError";
1532
+ constructor(buildId: string, reason: string);
1533
+ }
688
1534
  /** One term's postings within a commit delta or base chunk: parallel rowId/tf arrays. */
689
1535
  export interface FtsPosting {
690
1536
  term: string;
691
1537
  rowIds: bigint[];
692
1538
  tf: number[];
693
1539
  }
1540
+ export declare const MAX_POSTING_BUILD_TTL_MS: number;
1541
+ export interface BeginPostingBuildInput {
1542
+ tableId: string;
1543
+ columnId: string;
1544
+ buildId: string;
1545
+ ownerId: string;
1546
+ createdAt: string;
1547
+ expiresAt: string;
1548
+ }
1549
+ export interface RenewPostingBuildInput {
1550
+ tableId: string;
1551
+ columnId: string;
1552
+ buildId: string;
1553
+ ownerId: string;
1554
+ expiresAtCutoff: string;
1555
+ expiresAt: string;
1556
+ updatedAt: string;
1557
+ }
1558
+ export interface AppendPostingBuildChunkInput extends RenewPostingBuildInput {
1559
+ ordinal: number;
1560
+ chunk: readonly FtsPosting[];
1561
+ }
1562
+ export interface FinishPostingBuildInput {
1563
+ tableId: string;
1564
+ columnId: string;
1565
+ buildId: string;
1566
+ ownerId: string;
1567
+ expiresAtCutoff: string;
1568
+ coversVersion: number;
1569
+ chunkCount: number;
1570
+ totalTokens: number;
1571
+ completedAt: string;
1572
+ }
1573
+ export interface AbortPostingBuildInput {
1574
+ tableId: string;
1575
+ columnId: string;
1576
+ buildId: string;
1577
+ ownerId: string;
1578
+ expiresAtCutoff: string;
1579
+ }
1580
+ export declare class PostingBuildConflictError extends Error {
1581
+ readonly buildId: string;
1582
+ readonly ownerId: string;
1583
+ readonly reason: string;
1584
+ readonly name = "PostingBuildConflictError";
1585
+ constructor(buildId: string, ownerId: string, reason: string);
1586
+ }
694
1587
  /** One indexed column's contribution from one commit: postings for the commit's new rows. */
695
1588
  export interface FtsColumnDelta {
696
1589
  columnId: string;
@@ -709,20 +1602,69 @@ export interface FtsChanges {
709
1602
  tableId: string;
710
1603
  columns: readonly FtsColumnDelta[];
711
1604
  }
1605
+ /** In-memory and atomic-publish bounds for one transaction's accelerator/key deltas. */
1606
+ export declare const MAX_TRANSACTION_COMMIT_DELTA_BYTES: number;
1607
+ export declare const MAX_TRANSACTION_COMMIT_DELTA_ENTRIES = 1048576;
1608
+ export declare function transactionCommitDeltaRetainedBytes(uniqueKeyChanges: readonly UniqueKeyChanges[], ftsChanges: readonly FtsChanges[]): {
1609
+ bytes: number;
1610
+ entries: number;
1611
+ };
712
1612
  /** The per-term candidate row IDs a full-text index lookup returns, aligned with the query. */
713
1613
  export interface FtsCandidates {
714
1614
  /** Per requested term: ascending unique row IDs whose indexed column contained the term. */
715
1615
  rowIdsByTerm: bigint[][];
716
- }
1616
+ /** The bounded read stopped before collecting every matching row; callers must scan. */
1617
+ overflow: boolean;
1618
+ }
1619
+ /** Aggregate row-id ceiling for one postings candidate read. Overflow falls back to a scan. */
1620
+ export declare const MAX_FTS_CANDIDATE_ROW_IDS = 65536;
1621
+ /** Query-term cardinality shared by the parser and every postings adapter boundary. */
1622
+ export declare const MAX_FTS_QUERY_TERMS = 32;
1623
+ /** Retained metadata/value ceiling for one ordered postings read. Overflow falls back to a scan. */
1624
+ export declare const MAX_FTS_ORDERED_READ_BYTES: number;
1625
+ /** Hard public mutation bounds for one streamed postings-build chunk. */
1626
+ export declare const MAX_FTS_POSTINGS_PER_CHUNK = 65536;
1627
+ export declare const MAX_FTS_POSTING_ROW_IDS_PER_CHUNK = 1048576;
1628
+ export declare const MAX_FTS_POSTING_TERM_CHARACTERS = 65536;
1629
+ /** Indexed string values are bounded; non-indexed block strings retain the physical block cap. */
1630
+ export declare const MAX_INDEXED_STRING_CHARACTERS = 16384;
1631
+ /** Maximum normalized tokens retained from one indexed document. */
1632
+ export declare const MAX_FTS_TOKENS_PER_DOCUMENT = 4096;
1633
+ /** Bounded catalog/TOC cardinality. Oversized accelerators invalidate and queries scan. */
1634
+ export declare const MAX_FTS_BASE_CHUNKS = 4096;
1635
+ export declare const MAX_FTS_DELTA_CHUNKS = 128;
1636
+ /** One exact/prefix term lookup or a lexicographic term range over a postings index. */
1637
+ export type FtsPostingQuery = {
1638
+ term: string;
1639
+ prefix: boolean;
1640
+ } | {
1641
+ lower?: string;
1642
+ lowerInclusive?: boolean;
1643
+ upper?: string;
1644
+ upperInclusive?: boolean;
1645
+ };
1646
+ /** Strict, bounded runtime validation shared by every adapter before any postings read. */
1647
+ export declare function validateFtsPostingQueries(value: unknown, label?: string): asserts value is readonly FtsPostingQuery[];
1648
+ /** Whether a stored term belongs to one exact, prefix, or range lookup. */
1649
+ export declare function ftsPostingQueryMatches(term: string, query: FtsPostingQuery): boolean;
717
1650
  /**
718
1651
  * Shared candidate-merge core for both stores: fetching chunks is store-specific, but the
719
1652
  * term-match rule (exact, or prefix as a term range) and the sorted-unique row-id shape must
720
1653
  * never drift between backends — pruning would silently differ per store.
721
1654
  */
722
- export declare function collectFtsCandidates(chunkLists: Iterable<readonly FtsPosting[]>, terms: ReadonlyArray<{
723
- term: string;
724
- prefix: boolean;
725
- }>): FtsCandidates;
1655
+ export declare function collectFtsCandidates(chunkLists: Iterable<readonly FtsPosting[]>, terms: readonly FtsPostingQuery[], maxRowIds?: number): FtsCandidates;
1656
+ /** Validates the fixed memory ceilings accepted by one ordered postings read. */
1657
+ export declare function validateFtsOrderedReadLimits(maxRowIds?: number, maxRetainedBytes?: number): void;
1658
+ /**
1659
+ * K-way merges sorted base/delta chunks into canonical term order. Only one term's row map and
1660
+ * one cursor per chunk stay live beyond the returned postings, avoiding a second index-sized map.
1661
+ */
1662
+ export declare function collectFtsPostings(chunkLists: Iterable<readonly FtsPosting[]>): FtsPosting[];
1663
+ /** Bounded ordered merge; overflow deliberately returns no partial authoritative answer. */
1664
+ export declare function collectFtsPostingsBounded(chunkLists: Iterable<readonly FtsPosting[]>, maxRowIds?: number, maxRetainedBytes?: number): {
1665
+ postings: FtsPosting[];
1666
+ overflow: boolean;
1667
+ };
726
1668
  /**
727
1669
  * Shared stale-writer policy for both stores' commit steps: a commit that adds segments to a
728
1670
  * table with active full-text columns but no covering delta flips the uncovered columns to
@@ -730,18 +1672,40 @@ export declare function collectFtsCandidates(chunkLists: Iterable<readonly FtsPo
730
1672
  * Returns the updated record, or undefined when nothing changes.
731
1673
  */
732
1674
  export declare function invalidateUncoveredFtsColumns(record: TableRecord, coveredColumnIds: ReadonlySet<string>): TableRecord | undefined;
1675
+ /**
1676
+ * Scalar-index half of the stale-writer rule. A writer that staged table data from catalog
1677
+ * metadata older than a newly building/ready index cannot provide its postings, so the commit
1678
+ * invalidates that index atomically. Readers then scan until a rebuild closes the gap.
1679
+ */
1680
+ export declare function invalidateUncoveredSecondaryIndexes(record: TableRecord, coveredStorageColumnIds: ReadonlySet<string>): TableRecord | undefined;
1681
+ /** Physical posting IDs a current catalog record authorizes a commit to write. */
1682
+ export declare function activePostingStorageColumnIds(record: TableRecord): Set<string>;
733
1683
  export declare class UniqueKeyConflictError extends Error {
734
1684
  readonly tableId: string;
735
1685
  readonly keyToken: string;
736
1686
  readonly name = "UniqueKeyConflictError";
737
1687
  constructor(tableId: string, keyToken: string);
738
1688
  }
1689
+ /** A writer prepared before an enforced UNIQUE index existed and therefore cannot enforce it. */
1690
+ export declare class UniqueIndexCoverageError extends Error {
1691
+ readonly tableId: string;
1692
+ readonly indexName: string;
1693
+ readonly name = "UniqueIndexCoverageError";
1694
+ constructor(tableId: string, indexName: string);
1695
+ }
739
1696
  export declare class WriteConflictError extends Error {
740
1697
  readonly expectedVersion: number | null;
741
1698
  readonly actualVersion: number | null;
742
1699
  readonly name = "WriteConflictError";
743
1700
  constructor(expectedVersion: number | null, actualVersion: number | null);
744
1701
  }
1702
+ /** Staged write artifacts were prepared against a structurally different catalog. */
1703
+ export declare class SchemaConflictError extends Error {
1704
+ readonly expectedEpoch: number;
1705
+ readonly actualEpoch: number;
1706
+ readonly name = "SchemaConflictError";
1707
+ constructor(expectedEpoch: number, actualEpoch: number);
1708
+ }
745
1709
  export declare class TransactionRecordConflictError extends Error {
746
1710
  readonly transactionId: string;
747
1711
  readonly expectedRevision: number;
@@ -759,11 +1723,45 @@ export interface TempRunPage {
759
1723
  pageIndex: number;
760
1724
  bytes: Uint8Array;
761
1725
  }
1726
+ /**
1727
+ * Atomically adopts an unpublished compaction output left by an aborted attempt. The adapter
1728
+ * verifies that `segment` exactly matches the stored immutable record apart from its owner,
1729
+ * that the old owner is the expected aborted revision, and that the replacement is the active
1730
+ * transaction linked to `compactionJobId`. It then retags the segment and moves its journal
1731
+ * membership from the old owner to the replacement while advancing both transaction revisions.
1732
+ */
1733
+ export interface AdoptAbortedSegmentInput {
1734
+ segment: SegmentRecord;
1735
+ expectedAbortedTransactionId: string;
1736
+ expectedAbortedTransactionRevision: number;
1737
+ replacementTransactionId: string;
1738
+ expectedReplacementTransactionRevision: number;
1739
+ compactionJobId: string;
1740
+ updatedAt: string;
1741
+ }
1742
+ /** Hard storage-boundary limits for one scratch-page write. */
1743
+ export declare const MAX_TEMP_RUN_PAGES_PER_BATCH = 64;
1744
+ export declare const MAX_TEMP_RUN_PAGE_BYTES: number;
1745
+ export declare const MAX_TEMP_RUN_BATCH_BYTES: number;
1746
+ /**
1747
+ * Refuses an oversized scratch write before an adapter starts a transaction or appends a WAL
1748
+ * record. Query execution splits pages and batches to these limits, so the limits bound atomic
1749
+ * storage work without limiting the total size of a spill.
1750
+ */
1751
+ export declare function assertTempRunPageBatchLimits(pages: readonly TempRunPage[]): void;
762
1752
  export interface TempOwnerRecord {
763
1753
  ownerId: string;
1754
+ createdAt: string;
764
1755
  expiresAt: string;
765
1756
  revision: number;
766
1757
  }
1758
+ export declare const MAX_TEMP_OWNER_TTL_MS: number;
1759
+ export interface RenewTempOwnerInput {
1760
+ ownerId: string;
1761
+ expectedRevision: number;
1762
+ expiresAtCutoff: string;
1763
+ expiresAt: string;
1764
+ }
767
1765
  export declare class TempOwnerConflictError extends Error {
768
1766
  readonly ownerId: string;
769
1767
  readonly expectedRevision: number;
@@ -773,16 +1771,15 @@ export declare class TempOwnerConflictError extends Error {
773
1771
  }
774
1772
  /**
775
1773
  * One coherent read of everything query preparation needs before touching blocks: the
776
- * current manifest version, the named table records, every segment of the found tables,
777
- * and the transaction records those segments reference. Stores that can produce this in
778
- * one atomic read collapse the sequential per-record round trips a prepare would
779
- * otherwise issue.
1774
+ * current manifest version, the named table records, current segments of the found tables,
1775
+ * and the transaction records those segments reference. Stores that can produce this in one
1776
+ * atomic read collapse the sequential per-record round trips a prepare would otherwise issue.
780
1777
  */
781
1778
  export interface QueryCatalogState {
782
1779
  manifestVersion: number | null;
783
1780
  /** Positional per requested name; undefined where the table does not exist. */
784
1781
  tables: Array<TableRecord | undefined>;
785
- /** Segments of the found tables, sorted by id like listSegments. */
1782
+ /** Current-manifest segments of the found tables, sorted by id. */
786
1783
  segments: SegmentRecord[];
787
1784
  /** Records for the segments' transaction ids; missing records are omitted. */
788
1785
  transactions: TransactionRecord[];
@@ -794,41 +1791,106 @@ export interface QueryCatalogState {
794
1791
  catalogEpoch?: number;
795
1792
  }
796
1793
  /**
797
- * The two change counters a reader needs to know whether anything it may have cached is
798
- * still current, read together in one atomic storage transaction. `manifestVersion` moves on
1794
+ * The change counters a reader or writer needs, read together in one atomic storage
1795
+ * transaction. `manifestVersion` moves on
799
1796
  * every data commit. `catalogEpoch` moves on every catalog mutation — table creation, table
800
1797
  * record updates (schema migration, full-text index stamps), and every manifest publish —
801
1798
  * so an unchanged epoch proves cached catalog state is byte-identical to a fresh read.
802
1799
  * Physical garbage collection does not move the epoch: it only deletes records that are
803
1800
  * already invisible at every leased version, so cached state stays result-equivalent.
1801
+ * `schemaEpoch` is deliberately narrower: it moves only for structural catalog changes, so
1802
+ * writers can reject old-schema artifacts without making concurrent ordinary writes conflict.
804
1803
  */
805
1804
  export interface CatalogProbe {
806
1805
  manifestVersion: number | null;
807
1806
  catalogEpoch: number;
1807
+ /** Advances only when structural catalog state changes, never for an ordinary data commit. */
1808
+ schemaEpoch: number;
1809
+ }
1810
+ /** Optional serializable guard for a catalog mutation whose proof read multiple records. */
1811
+ export interface CatalogMutationOptions {
1812
+ /** Reject unless the complete catalog is still at this epoch. */
1813
+ expectedCatalogEpoch?: number;
1814
+ }
1815
+ /** Atomic replacement fields for one catalog record. */
1816
+ export interface TableRecordUpdate extends CatalogMutationOptions {
1817
+ columns?: TableColumnRecord[];
1818
+ /** Replaces the full-text index state map; null clears it. */
1819
+ ftsColumns?: Record<string, FtsColumnIndexRecord> | null;
1820
+ /** Replaces the secondary-index state map; null clears it. */
1821
+ secondaryIndexes?: Record<string, SecondaryIndexRecord> | null;
1822
+ /** Additional manifest CAS used when publishing a UNIQUE build from a stable snapshot. */
1823
+ expectedManifestVersion?: {
1824
+ value: number | null;
1825
+ };
1826
+ /** Atomically seeds one secondary UNIQUE membership namespace while activating enforcement. */
1827
+ uniqueKeySeed?: {
1828
+ namespaceId: string;
1829
+ keyTokens: readonly string[];
1830
+ };
1831
+ /** Atomically advances a newly declared auto-increment column's durable counter. */
1832
+ autoIncrementSeed?: {
1833
+ columnId: string;
1834
+ atLeast: bigint;
1835
+ };
1836
+ /** Replaces the trigger list; null clears it. */
1837
+ triggers?: TriggerRecord[] | null;
1838
+ /** Replaces a view definition in place; null converts the record back to a table. */
1839
+ view?: TableRecord["view"] | null;
808
1840
  }
809
1841
  /**
810
1842
  * Bulk payload storage: immutable, opaque byte blobs keyed by structured ids.
811
1843
  *
812
- * Blocks are write-once. `addBlock`/`addBlocks` MUST reject an id that already exists, and a
813
- * batch containing any duplicate MUST write nothing at all. Ids contain `/` separators
1844
+ * Blocks are written only through transaction staging, which creates their durable provenance
1845
+ * in the same atomic step and rejects duplicate ids without a partial write. Ids contain `/` separators
814
1846
  * (`table/<uuid>/segment/<uuid>/part/000001`) and sort lexically; treat them as opaque keys.
815
1847
  * Reads MUST return bytes the caller may mutate freely (a fresh copy or freshly deserialized
816
1848
  * buffer), and writes MUST NOT alias the caller's buffer — the engine may reuse it.
817
1849
  *
818
- * Published blocks are retired by superseding them in a commit, never by `removeBlock`; the
819
- * lease-aware collector deletes the bytes once no reader can be pinned to them.
1850
+ * Published blocks are retired by superseding them in a commit; only the store's atomic
1851
+ * lease-aware collection step can physically delete payloads once no reader can reach them.
820
1852
  */
1853
+ /** Maximum manifest-membership ids accepted by one storage call. */
1854
+ export declare const MAX_MANIFEST_BLOCK_PRESENCE_IDS = 1024;
1855
+ /** Maximum UTF-16 code units in any opaque storage identity. */
1856
+ export declare const MAX_STORAGE_ID_CHARACTERS = 1024;
1857
+ /** Maximum UTF-16 code units in a persisted catalog name. */
1858
+ export declare const MAX_CATALOG_NAME_CHARACTERS = 1024;
1859
+ /** Public adapter database-name bound; names commonly become substrate keys or path segments. */
1860
+ export declare const MAX_STORAGE_DATABASE_NAME_CHARACTERS = 256;
1861
+ /** Maximum positional items accepted by one public bulk-read operation. */
1862
+ export declare const MAX_STORAGE_BULK_READ_ITEMS = 1024;
1863
+ /** Maximum aggregate payload bytes returned by one getBlocks call. */
1864
+ export declare const MAX_BLOCK_READ_BATCH_BYTES: number;
1865
+ export declare class BlockReadBatchTooLargeError extends RangeError {
1866
+ readonly requestedBytes: number;
1867
+ readonly limitBytes: number;
1868
+ readonly name = "BlockReadBatchTooLargeError";
1869
+ constructor(requestedBytes: number, limitBytes?: number);
1870
+ }
1871
+ export declare function assertStorageBulkReadItems(items: readonly unknown[], label: string): void;
1872
+ export declare function validateStorageId(value: unknown, label?: string): string;
1873
+ export declare function validateCatalogName(value: unknown, label?: string): string;
1874
+ export declare function validateStorageDatabaseName(value: unknown): string;
821
1875
  export interface BlockPayloadStore {
822
- addBlock(id: string, bytes: Uint8Array): Promise<void>;
823
- /** All or nothing: an internal or existing duplicate id fails the whole batch unwritten. */
824
- addBlocks(blocks: readonly BlockWrite[]): Promise<void>;
825
1876
  getBlock(id: string): Promise<Uint8Array | undefined>;
826
1877
  /** Positional per requested id; undefined where a block does not exist. */
827
1878
  getBlocks(ids: readonly string[]): Promise<Array<Uint8Array | undefined>>;
828
- /** Deleting a missing block is not an error. */
829
- removeBlock(id: string): Promise<void>;
830
- /** Every stored block id, sorted lexically. A cold path tools and tests, not queries. */
831
- listBlockIds(): Promise<string[]>;
1879
+ /**
1880
+ * Reads one block only when it belongs to the exact readable manifest version. The membership
1881
+ * check and payload read are one storage operation: callers must never fetch a block first and
1882
+ * then race a separate manifest check. A missing/pruned manifest or non-member id returns
1883
+ * undefined. A readable member whose payload is missing or unreadable is corruption and MUST
1884
+ * throw StorageCorruptionError. Reader leases keep a selected version from being pruned while
1885
+ * it is in use.
1886
+ */
1887
+ readManifestBlock(version: number | null, id: string): Promise<Uint8Array | undefined>;
1888
+ /**
1889
+ * Positional membership in the exact readable manifest version. Implementations must bound the
1890
+ * input at MAX_MANIFEST_BLOCK_PRESENCE_IDS; callers window larger sets. A missing/pruned
1891
+ * manifest returns false for every id.
1892
+ */
1893
+ hasManifestBlocks(version: number | null, ids: readonly string[]): Promise<boolean[]>;
832
1894
  }
833
1895
  /**
834
1896
  * The table catalog: schema records, the counters that keep writes collision-free, and
@@ -843,30 +1905,40 @@ export interface BlockPayloadStore {
843
1905
  */
844
1906
  export interface CatalogStore {
845
1907
  /** Fails on a duplicate id or name. Advances the catalog epoch. */
846
- addTable(record: TableRecord): Promise<void>;
1908
+ addTable(record: TableRecord, options?: CatalogMutationOptions): Promise<void>;
847
1909
  getTable(id: string): Promise<TableRecord | undefined>;
848
1910
  getTableByName(name: string): Promise<TableRecord | undefined>;
849
- /** Sorted by table name. */
1911
+ /** Sorted by table name; MAX_CATALOG_RECORDS makes this array globally bounded. */
850
1912
  listTables(): Promise<TableRecord[]>;
851
- updateTable(id: string, expectedRevision: number, update: {
852
- columns?: TableColumnRecord[];
853
- /** Replaces the full-text index state map; null clears it. */
854
- ftsColumns?: Record<string, FtsColumnIndexRecord> | null;
855
- /** Replaces the trigger list; null clears it. */
856
- triggers?: TriggerRecord[] | null;
857
- }): Promise<TableRecord>;
858
- /**
859
- * Removes a table's catalog record together with everything else keyed to it: its segments,
860
- * its full-text base chunks and commit deltas, its unique-key membership, and its row-id and
861
- * autoincrement counters. One step, so a crash cannot leave a segment pointing at a table
862
- * that no longer exists. Advances the catalog epoch, and fails with a
863
- * `TableRecordConflictError` on a revision mismatch, like `updateTable`.
864
- *
865
- * The table's blocks are the caller's business: they are retired by superseding them in a
866
- * commit, which leaves the bytes for the lease-aware collector rather than deleting data a
867
- * pinned reader may still be reading.
1913
+ /**
1914
+ * Replaces catalog metadata atomically. When `columns` removes a column, the same operation
1915
+ * must also discard that column's full-text catalog entry, base chunks, and commit deltas.
1916
+ */
1917
+ updateTable(id: string, expectedRevision: number, update: TableRecordUpdate): Promise<TableRecord>;
1918
+ /**
1919
+ * Removes a segment-free catalog object such as a view. Must refuse when any segment names the
1920
+ * record; populated tables use `dropTable`, whose manifest retirement and catalog removal are
1921
+ * inseparable. Advances the catalog epoch and fails with `TableRecordConflictError` on a stale
1922
+ * revision.
1923
+ */
1924
+ removeTable(id: string, expectedRevision: number, options?: CatalogMutationOptions): Promise<void>;
1925
+ /**
1926
+ * Atomically retires all live table blocks, removes the catalog/segments/counters/indexes,
1927
+ * and publishes the successor manifest. A table revision mismatch throws
1928
+ * `TableRecordConflictError`; a manifest mismatch throws `WriteConflictError`; an active
1929
+ * transaction or nonterminal compaction owner throws `TableInUseError`. Any rejection leaves
1930
+ * the catalog, manifest, jobs, transaction journals, segments, counters, indexes, and payloads
1931
+ * unchanged. A success returns the one manifest summary it published.
868
1932
  */
869
- removeTable(id: string, expectedRevision: number): Promise<void>;
1933
+ dropTable(input: DropTableInput): Promise<ManifestSummary>;
1934
+ /**
1935
+ * Atomically removes one safe non-key column from catalog metadata and every table segment,
1936
+ * removes its posting accelerator, and publishes a successor retiring only removed column
1937
+ * blocks that no remaining segment reference owns. CAS/busy errors match `dropTable`; any
1938
+ * rejection changes nothing, payload bytes stay available to pinned historical readers, and
1939
+ * success returns the exactly one published manifest summary.
1940
+ */
1941
+ dropTableColumn(input: DropTableColumnInput): Promise<ManifestSummary>;
870
1942
  reserveRowIds(tableId: string, count: number): Promise<RowIdRange>;
871
1943
  /**
872
1944
  * Atomically reserves `count` auto-increment values for the column, first bumping the
@@ -875,6 +1947,36 @@ export interface CatalogStore {
875
1947
  reserveAutoIncrement(tableId: string, columnId: string, count: number, atLeast?: bigint): Promise<RowIdRange>;
876
1948
  /** Which of the given key tokens already exist for the table, deduplicated and sorted. */
877
1949
  getExistingUniqueKeys(tableId: string, keyTokens: readonly string[]): Promise<string[]>;
1950
+ /**
1951
+ * Starts or takes over the exact catalog-owned UNIQUE builder. One active session per namespace
1952
+ * and MAX_ACTIVE_UNIQUE_KEY_BUILDS globally are enforced atomically. Repeating the same active
1953
+ * owner/header is idempotent; an expired owner is irrevocably replaced with an empty stage.
1954
+ */
1955
+ beginUniqueKeyBuild(input: BeginUniqueKeyBuildInput): Promise<UniqueKeyBuildRecord>;
1956
+ getUniqueKeyBuild(buildId: string): Promise<UniqueKeyBuildRecord | undefined>;
1957
+ /** Refuses at/past the cutoff, so a dead builder can never be resurrected. */
1958
+ renewUniqueKeyBuild(input: RenewUniqueKeyBuildInput): Promise<UniqueKeyBuildRecord>;
1959
+ /**
1960
+ * Appends the exact next ordinal. Same-ordinal lost-ack replay compares the persisted tokens
1961
+ * byte-for-byte; a changed replay or duplicate within/across chunks refuses without mutation.
1962
+ */
1963
+ appendUniqueKeyBuildChunk(input: AppendUniqueKeyBuildChunkInput): Promise<UniqueKeyBuildRecord>;
1964
+ /**
1965
+ * Atomically verifies the exact building catalog owner, table+manifest CAS, completed chunk
1966
+ * count, and then publishes both the durable membership namespace and ready catalog state.
1967
+ * A same-build completed retry is a no-op; no catalog/membership intermediate state exists.
1968
+ */
1969
+ finishUniqueKeyBuild(input: FinishUniqueKeyBuildInput): Promise<TableRecord>;
1970
+ /** Exact owner may abort; after expiry any caller may reclaim. Returns false when absent. */
1971
+ abortUniqueKeyBuild(input: AbortUniqueKeyBuildInput): Promise<boolean>;
1972
+ /** Starts or takes over a bounded base+tail membership fold; see `UniqueKeyFoldRecord`. */
1973
+ beginUniqueKeyFold?(input: BeginUniqueKeyFoldInput): Promise<UniqueKeyFoldRecord>;
1974
+ getUniqueKeyFold?(foldId: string): Promise<UniqueKeyFoldRecord | undefined>;
1975
+ /** Advances at most `maxTokens` lexical keys and renews the exact live owner. */
1976
+ runUniqueKeyFoldStep?(input: RunUniqueKeyFoldStepInput): Promise<UniqueKeyFoldRecord>;
1977
+ /** Atomically promotes the output generation and removes only the frozen delta prefix. */
1978
+ finishUniqueKeyFold?(input: FinishUniqueKeyFoldInput): Promise<UniqueKeyFoldRecord>;
1979
+ abortUniqueKeyFold?(input: AbortUniqueKeyFoldInput): Promise<boolean>;
878
1980
  }
879
1981
  /**
880
1982
  * Versions and visibility: manifests (the set of live block ids at each version), segments
@@ -883,12 +1985,14 @@ export interface CatalogStore {
883
1985
  *
884
1986
  * This is where the whole consistency story lives. `commitTransaction` is THE atomic step of
885
1987
  * the database: in one durable, all-or-nothing action it validates the transaction record's
886
- * revision and active status, compare-and-swaps the current manifest version, publishes the
1988
+ * revision and active status, verifies its structural `schemaEpochGuard`, compare-and-swaps the
1989
+ * current manifest version, publishes the
887
1990
  * next manifest, finalizes the transaction's segments, applies unique-key changes (failing
888
1991
  * with `UniqueKeyConflictError` on a `requireAbsent` violation), applies full-text deltas,
889
1992
  * and flips the transaction record to committed. No intermediate state may ever be
890
- * observable, including after a crash at any moment. Version conflicts MUST be
891
- * `WriteConflictError` and revision conflicts `TransactionRecordConflictError` the exact
1993
+ * observable, including after a crash at any moment. Schema conflicts MUST be
1994
+ * `SchemaConflictError`, version conflicts `WriteConflictError`, and revision conflicts
1995
+ * `TransactionRecordConflictError` — the exact
892
1996
  * exported classes; the engine's retry and rebase loops match on them, and the worker client
893
1997
  * rehydrates them by name across the thread boundary.
894
1998
  */
@@ -897,21 +2001,30 @@ export interface TransactionStore {
897
2001
  /** The current version alone, without materializing the manifest's block list. */
898
2002
  getCurrentManifestVersion(): Promise<number | null>;
899
2003
  getManifest(version: number): Promise<Manifest | undefined>;
900
- listManifests(): Promise<Manifest[]>;
2004
+ /**
2005
+ * Exact ID-ordered membership at one historically published version. Implementations resolve
2006
+ * this from interval provenance without reconstructing or cloning a complete manifest. It must
2007
+ * remain pageable after the summary tombstone is removed so bounded maintenance can drain the
2008
+ * retired payload interval; callers separately use `getManifest`/leases to authorize reads.
2009
+ * A version newer than the current database returns an empty page. `limit` is storage-bounded.
2010
+ */
2011
+ listManifestBlockPage(input: ListManifestBlockPageInput): Promise<ManifestBlockPage>;
2012
+ /**
2013
+ * ID-ordered retired provenance, independent of bounded manifest-summary tombstones. This is
2014
+ * the durable garbage-discovery index: every record whose non-null `removedVersion` is at most
2015
+ * `removedThroughVersion` appears exactly once in a full cursor traversal.
2016
+ */
2017
+ listRetiredManifestBlockPage(input: ListRetiredManifestBlockPageInput): Promise<ManifestBlockPage>;
901
2018
  listManifestPage(afterVersion: number | null, limit: number): Promise<StoragePage<Manifest, number>>;
902
2019
  /**
903
- * Publishes the next version directly from a full block-id list, compare-and-swapping on
904
- * `expectedVersion` (`WriteConflictError` on a mismatch). Every id must exist. The engine
905
- * commits through transactions instead; this is the lower-level tool underneath.
2020
+ * Fails on a duplicate id; the record's snapshot version and pending ids must be valid. When
2021
+ * an active record omits `schemaEpochGuard`, the store stamps its current structural epoch in
2022
+ * the same atomic creation step. A supplied stale guard throws `SchemaConflictError`.
906
2023
  */
907
- publishManifest(input: PublishManifestInput): Promise<Manifest>;
908
- /** Fails on a duplicate id; the record's snapshot version and pending ids must be valid. */
909
2024
  createTransaction(record: TransactionRecord): Promise<void>;
910
2025
  getTransaction(id: string): Promise<TransactionRecord | undefined>;
911
2026
  /** Positional per requested id; undefined where a record does not exist. */
912
2027
  getTransactions(ids: readonly string[]): Promise<Array<TransactionRecord | undefined>>;
913
- /** Sorted by startedAt, then id. */
914
- listTransactions(): Promise<TransactionRecord[]>;
915
2028
  listTransactionPage(afterId: string | null, limit: number): Promise<StoragePage<TransactionRecord, string>>;
916
2029
  /**
917
2030
  * Compare-and-swap on `expectedRevision` (`TransactionRecordConflictError` on a mismatch).
@@ -919,13 +2032,35 @@ export interface TransactionStore {
919
2032
  * committed.
920
2033
  */
921
2034
  updateTransaction(id: string, expectedRevision: number, update: TransactionRecordUpdate): Promise<TransactionRecord>;
2035
+ /**
2036
+ * Extends a matching active writer's durable deadline without changing its data revision.
2037
+ * Returns false after ownership is lost or the transaction becomes terminal.
2038
+ */
2039
+ renewTransaction(input: RenewTransactionInput): Promise<boolean>;
2040
+ /**
2041
+ * Atomically aborts an active transaction only when both its owner still matches and its
2042
+ * durable deadline is at or before the cutoff. A concurrent renewal wins by returning
2043
+ * undefined; a successful abort preserves artifact provenance and advances the data revision.
2044
+ */
2045
+ abortTransactionIfExpired(input: AbortTransactionIfExpiredInput): Promise<TransactionRecord | undefined>;
922
2046
  /** Publishes the next version; the summary omits the block list, which commits never need. */
923
2047
  commitTransaction(input: CommitTransactionInput): Promise<ManifestSummary>;
924
- addSegment(record: SegmentRecord): Promise<void>;
925
2048
  getSegment(id: string): Promise<SegmentRecord | undefined>;
926
- /** Sorted by id; `tableId` filters. */
927
- listSegments(tableId?: string): Promise<SegmentRecord[]>;
928
- removeSegment(id: string): Promise<void>;
2049
+ /** Sorted by id, after the exclusive cursor; bounded for maintenance scans. */
2050
+ listSegmentPage(afterId: string | null, limit: number): Promise<StoragePage<SegmentRecord, string>>;
2051
+ /** Table-indexed ID page; query/compaction paths never scan unrelated segment history. */
2052
+ listTableSegmentPage(tableId: string, afterId: string | null, limit: number): Promise<StoragePage<SegmentRecord, string>>;
2053
+ /**
2054
+ * Removes an unpublished segment left by the exact aborted owner and unjournals it from that
2055
+ * transaction in the same atomic step. Returns false when the segment is already absent.
2056
+ * Any owner mismatch, non-aborted/missing owner, readable-manifest block reference, active
2057
+ * transaction pending-segment reference, or nonterminal compaction reference rejects without
2058
+ * mutation. A block alias in another journal is safe: this operation deletes segment metadata,
2059
+ * never payload bytes.
2060
+ */
2061
+ removeAbortedSegment(id: string, expectedTransactionId: string): Promise<boolean>;
2062
+ /** See `AdoptAbortedSegmentInput`; returns the updated replacement transaction journal. */
2063
+ adoptAbortedSegment(input: AdoptAbortedSegmentInput): Promise<TransactionRecord>;
929
2064
  }
930
2065
  /**
931
2066
  * Reader pins. A lease is a stored record with an expiry that protects one manifest version
@@ -935,15 +2070,28 @@ export interface TransactionStore {
935
2070
  * tabs safe.
936
2071
  */
937
2072
  export interface LeaseStore {
938
- /** Fails on a duplicate id, or when the pinned version's manifest is unavailable. */
2073
+ /**
2074
+ * Fails on a duplicate id or unavailable manifest. Before enforcing MAX_ACTIVE_LEASES, an
2075
+ * adapter sweeps a bounded expired page; creation and the final count check are atomic.
2076
+ */
939
2077
  createLease(record: LeaseRecord): Promise<void>;
940
2078
  getLease(id: string): Promise<LeaseRecord | undefined>;
941
- /** Sorted by id. */
2079
+ /** Sorted by id; MAX_ACTIVE_LEASES makes this array globally bounded. */
942
2080
  listLeases(): Promise<LeaseRecord[]>;
943
- renewLease(id: string, expectedRevision: number, expiresAt: string): Promise<LeaseRecord>;
2081
+ /** Bounded expiry/id-ordered page containing only records expired at the fixed cutoff. */
2082
+ listExpiredLeasePage(expiresAtCutoff: string, afterCursor: string | null, limit: number): Promise<StoragePage<LeaseRecord, string>>;
2083
+ renewLease(input: RenewLeaseInput): Promise<LeaseRecord>;
944
2084
  /** True when removed; false (without removing) when the lease has not yet expired. */
945
2085
  removeLeaseIfExpired(id: string, expectedRevision: number, expiresAtCutoff: string): Promise<boolean>;
946
- removeLease(id: string): Promise<void>;
2086
+ /**
2087
+ * Releases an existing lease only for its durable owner. A concurrent same-owner renewal may
2088
+ * still be removed safely; a different owner throws `LeaseOwnerConflictError` without change.
2089
+ * Returns false when the lease is already absent.
2090
+ */
2091
+ removeLease(input: {
2092
+ id: string;
2093
+ ownerId: string;
2094
+ }): Promise<boolean>;
947
2095
  }
948
2096
  /**
949
2097
  * Background-maintenance bookkeeping: the resumable job records that let compaction and
@@ -959,9 +2107,16 @@ export interface LeaseStore {
959
2107
  * needs.
960
2108
  */
961
2109
  export interface MaintenanceStore {
2110
+ /**
2111
+ * Atomically enforces one nonterminal job per table and MAX_ACTIVE_COMPACTION_JOBS globally;
2112
+ * terminal history does not count and is pruned separately.
2113
+ */
962
2114
  createCompactionJob(record: CompactionJobRecord): Promise<void>;
963
2115
  getCompactionJob(id: string): Promise<CompactionJobRecord | undefined>;
964
- /** Sorted by createdAt, then id; `tableId` filters. */
2116
+ /**
2117
+ * Sorted by createdAt, then id; `tableId` filters. The returned array is bounded by the
2118
+ * enforced active and terminal compaction-job record quotas above.
2119
+ */
965
2120
  listCompactionJobs(tableId?: string): Promise<CompactionJobRecord[]>;
966
2121
  listCompactionJobPage(afterId: string | null, limit: number): Promise<StoragePage<CompactionJobRecord, string>>;
967
2122
  updateCompactionJob(id: string, expectedRevision: number, update: CompactionJobRecordUpdate): Promise<CompactionJobRecord>;
@@ -971,19 +2126,33 @@ export interface MaintenanceStore {
971
2126
  * cancelled and its active transaction aborted — atomically.
972
2127
  */
973
2128
  cancelCompactionJob(id: string, expectedRevision: number, cancelledAt: string): Promise<CompactionJobRecord>;
974
- removeCompactionJob(id: string): Promise<void>;
2129
+ /**
2130
+ * Removes only published, cancelled, or aborted diagnostics. Returns false when missing or
2131
+ * when an existing source/output block or segment would lose its last durable provenance/root;
2132
+ * collection can remove that payload first. Nonterminal removal throws without mutation.
2133
+ */
2134
+ removeCompactionJob(id: string): Promise<boolean>;
975
2135
  /** Validates candidate provenance against persisted records before accepting the job. */
976
2136
  createGarbageCollectionJob(input: CreateGarbageCollectionJobInput): Promise<GarbageCollectionJobRecord>;
2137
+ /** CAS-appends one bounded discovery page; no payload or catalog data is changed. */
2138
+ updateGarbageCollectionPlanning(input: UpdateGarbageCollectionPlanningInput): Promise<GarbageCollectionJobRecord>;
977
2139
  getGarbageCollectionJob(id: string): Promise<GarbageCollectionJobRecord | undefined>;
978
- /** Sorted by createdAt, then id. */
2140
+ /**
2141
+ * Sorted by createdAt, then id. The returned array is bounded by the enforced active and
2142
+ * completed garbage-collection record quotas above.
2143
+ */
979
2144
  listGarbageCollectionJobs(): Promise<GarbageCollectionJobRecord[]>;
2145
+ /** Bounded page sorted by id; maintenance paths use this instead of materializing history. */
2146
+ listGarbageCollectionJobPage(afterId: string | null, limit: number): Promise<StoragePage<GarbageCollectionJobRecord, string>>;
980
2147
  runGarbageCollectionStep(input: RunGarbageCollectionStepInput): Promise<GarbageCollectionStepResult>;
981
2148
  /**
982
- * Deletes obsolete tombstones only after their garbage blocks are gone, while retaining the
983
- * checkpoint prefix readable deltas need. A tombstone is the collector's durable discovery
984
- * record between bounded passes.
2149
+ * Removes at most `maxItems` obsolete summary tombstones while retaining the checkpoint prefix
2150
+ * readable deltas need. Retired block provenance, not the summary, is durable garbage
2151
+ * discovery; `runGarbageCollectionStep` removes each payload and its provenance atomically, so
2152
+ * deleting a summary cannot strand an undiscoverable block.
985
2153
  */
986
- removePrunedManifestRecords(): Promise<number>;
2154
+ removePrunedManifestRecords(maxItems: number): Promise<number>;
2155
+ /** Removes only a completed diagnostic; planned/running collection is refused. */
987
2156
  removeGarbageCollectionJob(id: string): Promise<void>;
988
2157
  }
989
2158
  /**
@@ -993,6 +2162,12 @@ export interface MaintenanceStore {
993
2162
  * indexed columns as `invalid`.
994
2163
  */
995
2164
  export interface FtsIndexStore {
2165
+ /** Removes every base chunk and commit delta owned by one column. */
2166
+ /**
2167
+ * Removes posting storage only when the current catalog no longer owns it as a ready/building
2168
+ * accelerator (absent or invalid is safe). Refusal leaves catalog and postings unchanged.
2169
+ */
2170
+ removeFtsColumn(tableId: string, columnId: string): Promise<void>;
996
2171
  /**
997
2172
  * Replaces one column's full-text base chunks (term-range partitioned, term-sorted within
998
2173
  * each chunk) and deletes commit deltas the new base covers. The caller flips the catalog
@@ -1008,18 +2183,29 @@ export interface FtsIndexStore {
1008
2183
  * Per-term candidate row IDs from the base chunks plus every commit delta at or below
1009
2184
  * `upToVersion`, with the column's merged token total for exact BM25 statistics. Prefix
1010
2185
  * terms match the term range [term, term + "\uffff"). Reports the merged delta-chunk count
1011
- * so callers can schedule a rebuild when the tail grows, and the base's covered version —
2186
+ * so callers can schedule a rebuild when the tail grows, whether a published base exists,
2187
+ * and the base's covered version —
1012
2188
  * a concurrent rebuild can publish a base ahead of a reader's snapshot, and a caller
1013
2189
  * needing snapshot-exact statistics must detect `coversVersion > upToVersion` and fall
1014
2190
  * back (candidates stay a safe superset either way).
1015
2191
  */
1016
- readFtsCandidates(tableId: string, columnId: string, terms: ReadonlyArray<{
1017
- term: string;
1018
- prefix: boolean;
1019
- }>, upToVersion: number): Promise<FtsCandidates & {
2192
+ readFtsCandidates(tableId: string, columnId: string, terms: readonly FtsPostingQuery[], upToVersion: number, maxRowIds?: number): Promise<FtsCandidates & {
1020
2193
  deltaChunkCount: number;
1021
2194
  totalTokens: number;
1022
2195
  coversVersion: number;
2196
+ hasBase: boolean;
2197
+ }>;
2198
+ /**
2199
+ * Canonical term/posting order from the same snapshot-bounded base-plus-delta view. This is a
2200
+ * materialized cold path for ordered and covering scans; callers must not retain the result.
2201
+ */
2202
+ readFtsPostings(tableId: string, columnId: string, upToVersion: number, maxRowIds?: number, maxRetainedBytes?: number): Promise<{
2203
+ postings: FtsPosting[];
2204
+ /** The bounded merge stopped early; callers must scan and may rebuild the accelerator. */
2205
+ overflow: boolean;
2206
+ deltaChunkCount: number;
2207
+ coversVersion: number;
2208
+ hasBase: boolean;
1023
2209
  }>;
1024
2210
  }
1025
2211
  /**
@@ -1029,6 +2215,7 @@ export interface FtsIndexStore {
1029
2215
  * records are real records with the usual compare-and-swap (`TempOwnerConflictError`).
1030
2216
  */
1031
2217
  export interface TempSpillStore {
2218
+ /** Atomically enforces the per-owner run/page ceilings before storing payload bytes. */
1032
2219
  putTempRunPage(page: TempRunPage): Promise<void>;
1033
2220
  /**
1034
2221
  * Optional: writes a batch of pages in one storage round trip. Callers fall back to
@@ -1040,13 +2227,16 @@ export interface TempSpillStore {
1040
2227
  removeTempRun(ownerId: string, runId: string): Promise<void>;
1041
2228
  /** Removes the owner record and every page under the owner. */
1042
2229
  removeTempOwner(ownerId: string): Promise<void>;
2230
+ /** Sweeps a bounded expired page before atomically enforcing MAX_ACTIVE_TEMP_OWNERS. */
1043
2231
  createTempOwner(record: TempOwnerRecord): Promise<void>;
1044
2232
  getTempOwner(ownerId: string): Promise<TempOwnerRecord | undefined>;
1045
- renewTempOwner(ownerId: string, expectedRevision: number, expiresAt: string): Promise<TempOwnerRecord>;
2233
+ renewTempOwner(input: RenewTempOwnerInput): Promise<TempOwnerRecord>;
1046
2234
  /** Sweeps pages too when it removes; owners found only via orphaned pages count as expired. */
1047
2235
  removeTempOwnerIfExpired(ownerId: string, expiresAtCutoff: string): Promise<boolean>;
1048
2236
  /** Owner ids from records and from orphaned pages alike, deduplicated, sorted, paged. */
1049
2237
  listTempOwnerIdsPage(afterOwnerId: string | null, limit: number): Promise<StoragePage<string, string>>;
2238
+ /** Actual expired candidates only, ordered by expiry then owner id. */
2239
+ listExpiredTempOwnerPage(expiresAtCutoff: string, afterCursor: string | null, limit: number): Promise<StoragePage<string, string>>;
1050
2240
  }
1051
2241
  /**
1052
2242
  * The complete storage contract: a database is `MinnowDatabase` plus one implementation of
@@ -1061,8 +2251,10 @@ export interface TempSpillStore {
1061
2251
  *
1062
2252
  * - **Atomicity.** Every method is all-or-nothing, including after a crash at any moment.
1063
2253
  * `commitTransaction` and `runGarbageCollectionStep` mutate several record families in one
1064
- * durable step. A method that resolves has happened; a method that rejects has not
1065
- * (observably) happened.
2254
+ * durable step. A local adapter method that resolves has happened; one that rejects has not
2255
+ * happened. The narrow transport-loss exception is an OPFS follower call that throws
2256
+ * `OpfsUncertainOutcomeError`: its prior leader may have committed the named mutation before
2257
+ * losing the reply, so callers must inspect/reopen instead of blindly repeating it.
1066
2258
  * - **Conflicts are typed, by exact class.** Compare-and-swap failures throw the exported
1067
2259
  * error classes (`WriteConflictError`, `TransactionRecordConflictError`,
1068
2260
  * `TableRecordConflictError`, `LeaseConflictError`, `CompactionJobConflictError`,
@@ -1076,10 +2268,10 @@ export interface TempSpillStore {
1076
2268
  * received records and bytes must be copied or serialized before the call resolves.
1077
2269
  * - **Deterministic ordering.** List methods sort as documented on each capability interface;
1078
2270
  * pagination cursors are stable under concurrent writes.
1079
- * - **Optional means atomic.** The optional methods exist so an adapter that can do something
1080
- * in one atomic step may say so; callers trust a present method completely and fall back to
1081
- * the sequential calls when it is absent. Never implement one as the sequential calls in a
1082
- * trench coat.
2271
+ * - **Optional means atomic.** Optional accelerator methods exist so an adapter that can do
2272
+ * something in one atomic step may say so; callers trust a present method completely and fall
2273
+ * back to safe sequential calls when it is absent. Never implement one as sequential calls in
2274
+ * a trench coat.
1083
2275
  * - **Multiple connections are normal.** Several instances (tabs) may open one database.
1084
2276
  * Readers must never block writers; competing writers must resolve through the typed
1085
2277
  * conflicts. How is the adapter's business — storage transactions, a write-ahead log behind
@@ -1093,36 +2285,57 @@ export interface TempSpillStore {
1093
2285
  */
1094
2286
  export interface BlockStore extends BlockPayloadStore, CatalogStore, TransactionStore, LeaseStore, MaintenanceStore, FtsIndexStore, TempSpillStore {
1095
2287
  /**
1096
- * Optional: the current manifest version and catalog epoch in one atomic read. This is the
1097
- * freshness probe: an unchanged pair proves any cached catalog state is still exactly what
1098
- * a fresh read would return. Callers that find this absent must not cache catalog state.
2288
+ * Bounded builder for a postings base. Chunks stage under `buildId`; finish swaps the complete
2289
+ * generation into view atomically and prunes covered deltas. Beginning another build for the
2290
+ * same physical column reclaims an abandoned generation, so tab death cannot leak one
2291
+ * generation per retry. Required because an index build must never materialize one
2292
+ * database-sized storage value as a fallback.
2293
+ */
2294
+ beginFtsBaseBuild(input: BeginPostingBuildInput): Promise<void>;
2295
+ renewFtsBaseBuild(input: RenewPostingBuildInput): Promise<void>;
2296
+ writeFtsBaseBuildChunk(input: AppendPostingBuildChunkInput): Promise<void>;
2297
+ finishFtsBaseBuild(input: FinishPostingBuildInput): Promise<void>;
2298
+ abortFtsBaseBuild(input: AbortPostingBuildInput): Promise<void>;
2299
+ /**
2300
+ * The current manifest version, catalog epoch, and structural schema epoch in one atomic read.
2301
+ * An unchanged manifest/catalog pair proves cached query state is current; the schema epoch is
2302
+ * the narrower write-serialization guard and does not move for ordinary commits.
2303
+ */
2304
+ getCatalogProbe(): Promise<CatalogProbe>;
2305
+ /**
2306
+ * Optional atomic query-catalog read. The returned tables, segments, transaction owners, and
2307
+ * probe epoch must come from one storage snapshot; engines use the sequential stable-epoch
2308
+ * fallback when an adapter does not provide it.
1099
2309
  */
1100
- getCatalogProbe?(): Promise<CatalogProbe>;
2310
+ getQueryCatalogState?(names: readonly string[]): Promise<QueryCatalogState>;
1101
2311
  /**
1102
- * Optional: one atomic catalog read for query preparation. Implementations must return
1103
- * the same records the individual getTableByName/listSegments/getTransactions calls
1104
- * would; callers fall back to those calls when this is absent.
2312
+ * Reads the current manifest and schema epoch, creates the transaction record pinned to both,
2313
+ * and optionally reserves row ids or an invisible pending table in one atomic storage
2314
+ * transaction. Required because these reservations cannot be emulated safely by sequential
2315
+ * calls.
1105
2316
  */
1106
- getQueryCatalogState?(tableNames: readonly string[]): Promise<QueryCatalogState>;
2317
+ beginTransaction(input: BeginTransactionInput): Promise<BeginTransactionResult>;
1107
2318
  /**
1108
- * Optional: reads the current manifest version, creates the transaction record pinned to it,
1109
- * and optionally reserves row ids, all in one atomic storage transaction one round trip
1110
- * instead of three. Callers fall back to the individual calls when this is absent.
2319
+ * Required atomic staging boundary: saves blocks and segments and appends their ids to the
2320
+ * active transaction journal in one durable operation. It is equivalent to addBlocks +
2321
+ * addSegment(s) + one updateTransaction only when those changes commit atomically; there is no
2322
+ * sequential fallback because a crash could otherwise strand bytes or publish a dangling id.
2323
+ * A local refusal has no effect; the documented OPFS follower uncertain-outcome error means
2324
+ * the complete operation may have happened and must never be converted to an ordinary error.
1111
2325
  */
1112
- beginTransaction?(input: BeginTransactionInput): Promise<BeginTransactionResult>;
2326
+ stageTransactionArtifacts(input: StageTransactionArtifactsInput): Promise<TransactionRecord>;
1113
2327
  /**
1114
- * Optional: stages blocks and segments and journals them on the transaction record in one
1115
- * atomic storage transaction. Must be equivalent to addBlocks + addSegment(s) + one
1116
- * updateTransaction appending the new ids, with no intermediate state observable after a
1117
- * crash. Callers fall back to those calls when this is absent.
2328
+ * Required atomic savepoint rewind. Compare-and-swaps the journal, validates retained and
2329
+ * removed ids as its exact duplicate-free partition, and removes only the unreachable removed
2330
+ * artifacts in the same durable operation. A refusal leaves both journal and bytes unchanged.
1118
2331
  */
1119
- stageTransactionArtifacts?(input: StageTransactionArtifactsInput): Promise<TransactionRecord>;
2332
+ rollbackTransactionArtifacts(input: RollbackTransactionArtifactsInput): Promise<TransactionRecord>;
1120
2333
  /**
1121
2334
  * Optional: the single-shot write — begin (or continue) a transaction, stage its blocks and
1122
2335
  * segments, and commit, all in one atomic storage transaction. Must be exactly equivalent to
1123
2336
  * `stageTransactionArtifacts` followed by `commitTransaction` (preceded by `createTransaction`
1124
2337
  * at `expectedManifestVersion` when the input carries a fresh record): the same validation,
1125
- * the same typed conflicts (`WriteConflictError`, `TransactionRecordConflictError`,
2338
+ * the same typed conflicts (`SchemaConflictError`, `WriteConflictError`, `TransactionRecordConflictError`,
1126
2339
  * `UniqueKeyConflictError`), the same finalized records afterwards — and nothing at all
1127
2340
  * written when any part refuses, including the fresh record. This is what lets a simple
1128
2341
  * write cost one durable storage commit instead of three; callers fall back to the sequence
@@ -1133,29 +2346,52 @@ export interface BlockStore extends BlockPayloadStore, CatalogStore, Transaction
1133
2346
  * Optional: re-pins a lease to another manifest version and renews it, in one atomic step —
1134
2347
  * `createLease` at the new version plus `removeLease` of the old pin, as one round trip that
1135
2348
  * keeps the record and its id. Compare-and-swap on `expectedRevision` (`LeaseConflictError`);
1136
- * the target version's manifest must be available (`SnapshotManifestMissingError`), and a
1137
- * refused move leaves the lease exactly as it was. The engine uses it to carry its shared
2349
+ * the target version's manifest must be available (`SnapshotManifestMissingError`), and a refused
2350
+ * move leaves the lease exactly as it was. An already-expired pin cannot be moved or renewed,
2351
+ * and the new expiry is capped by `MAX_LEASE_TTL_MS`. The engine uses it to carry its shared
1138
2352
  * reader pin forward after each commit; callers fall back to create + remove when absent.
1139
2353
  */
1140
- moveLease?(id: string, expectedRevision: number, manifestVersion: number | null, expiresAt: string): Promise<LeaseRecord>;
2354
+ moveLease?(input: MoveLeaseInput): Promise<LeaseRecord>;
1141
2355
  /**
1142
- * Optional: one committed version copied out as a portable snapshot — see
1143
- * `/docs/storage/snapshots` for what it carries, drops, and guarantees. A store without it
1144
- * still works; `MinnowDatabase` reports the capability as missing rather than failing.
2356
+ * Native bounded snapshot v1. Built-in adapters implement this complete family together.
2357
+ * Export reads exactly one globally ordered frame at a time from a durable frozen generation;
2358
+ * close releases that generation and its lease. No method returns a database-sized array.
1145
2359
  */
1146
- exportSnapshot?(): Promise<DatabaseSnapshot>;
2360
+ beginSnapshotFrameExport?(input: BeginSnapshotFrameExportInput): Promise<SnapshotFrameExportSession>;
2361
+ readSnapshotExportFrame?(input: ReadSnapshotExportFrameInput): Promise<SnapshotFrame | undefined>;
2362
+ closeSnapshotFrameExport?(input: CloseSnapshotExportInput): Promise<boolean>;
1147
2363
  /**
1148
- * Optional: loads a snapshot into this store, which must be empty. Pairs with
1149
- * `exportSnapshot` implement both or neither.
2364
+ * Native bounded import. Append atomically accepts only contiguous bounded frames and exact
2365
+ * lost-ack replay bytes. Finish validates header/footer counts, order, checksums, complete
2366
+ * catalog/segment/transaction/index cross-references, and atomically promotes all staged
2367
+ * generations; rejection leaves the current database unchanged.
1150
2368
  */
1151
- importSnapshot?(snapshot: DatabaseSnapshot, options?: {
1152
- onProgress?: (progress: SnapshotLoadProgress) => void;
1153
- }): Promise<void>;
2369
+ beginSnapshotFrameImport?(input: BeginSnapshotFrameImportInput): Promise<SnapshotFrameImportSession>;
2370
+ renewSnapshotFrameImport?(input: RenewSnapshotFrameImportInput): Promise<SnapshotFrameImportSession>;
2371
+ appendSnapshotImportFrames?(input: AppendSnapshotImportFramesInput): Promise<SnapshotFrameImportSession>;
2372
+ finishSnapshotFrameImport?(input: FinishSnapshotFrameImportInput): Promise<void>;
2373
+ cancelSnapshotFrameImport?(input: CancelSnapshotImportInput): Promise<InterruptedSnapshotImportAbortResult>;
2374
+ /** Bounded, fail-closed verification of control records and optionally every live block. */
2375
+ checkIntegrity?(options?: {
2376
+ mode?: StorageIntegrityMode;
2377
+ maxIssues?: number;
2378
+ }): Promise<StorageIntegrityReport>;
2379
+ /** Explicit storage accounting; adapters do not run this on query or write hot paths. */
2380
+ getStorageStats?(): Promise<StorageStats>;
2381
+ /** Returns the durable marker for an unpublished snapshot import, if this adapter has one. */
2382
+ inspectInterruptedImport?(): Promise<InterruptedSnapshotImport | null>;
2383
+ /** Atomically abandons the named unpublished import and removes all staged state. */
2384
+ abortInterruptedImport?(identity: string): Promise<InterruptedSnapshotImportAbortResult>;
1154
2385
  /**
1155
2386
  * Optional: what this database's data occupies in its substrate, in bytes — the number an
1156
2387
  * application shows a user next to the quota, and what the benchmarks report.
1157
2388
  */
1158
2389
  getLogicalStorageBytes?(): Promise<number>;
2390
+ /**
2391
+ * Optional deterministic channel for commit hints between connections to the same durable
2392
+ * database. The engine creates the BroadcastChannel; stores only expose identity.
2393
+ */
2394
+ readonly liveQueryChannelName?: string;
1159
2395
  /**
1160
2396
  * Releases whatever the connection holds (open handles, channels, timers) without flushing
1161
2397
  * or deleting anything. Synchronous; safe to call twice. Data durability must never depend
@@ -1163,17 +2399,16 @@ export interface BlockStore extends BlockPayloadStore, CatalogStore, Transaction
1163
2399
  */
1164
2400
  close(): void;
1165
2401
  }
1166
- export declare function createManifest(input: PublishManifestInput): Manifest;
1167
- /**
1168
- * Normalizes the additive L2 partition metadata while preserving legacy segment records verbatim.
1169
- */
2402
+ export declare function createManifest(input: CreateManifestInput): Manifest;
2403
+ /** Strictly clones the one canonical v1 segment shape. */
1170
2404
  export declare function normalizeSegmentRecord(record: SegmentRecord): SegmentRecord;
1171
2405
  export declare function updateTransactionRecord(record: TransactionRecord, update: TransactionRecordUpdate): TransactionRecord;
1172
2406
  export declare function createGarbageCollectionJobRecord(input: CreateGarbageCollectionJobInput): GarbageCollectionJobRecord;
2407
+ export declare function normalizeGarbageCollectionDiscovery(discovery: GarbageCollectionDiscovery): GarbageCollectionDiscovery;
2408
+ export declare function updateGarbageCollectionPlanningRecord(record: GarbageCollectionJobRecord, input: UpdateGarbageCollectionPlanningInput): GarbageCollectionJobRecord;
1173
2409
  export declare function normalizeGarbageCollectionJobRecord(record: GarbageCollectionJobRecord): GarbageCollectionJobRecord;
1174
2410
  export declare function advanceGarbageCollectionJobRecord(record: GarbageCollectionJobRecord, accounting: GarbageCollectionStepAccounting): GarbageCollectionJobRecord;
1175
2411
  export declare function normalizeCompactionJobRecord(record: CompactionJobRecord): CompactionJobRecord;
1176
2412
  export declare function updateCompactionJobRecord(record: CompactionJobRecord, update: CompactionJobRecordUpdate): CompactionJobRecord;
1177
2413
  /** Floors an integer-times-double product without rounding the binary double upward. */
1178
2414
  export declare function floorWholeNumberProduct(left: number, right: number, label: string): number;
1179
- //# sourceMappingURL=types.d.ts.map