@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
@@ -0,0 +1,67 @@
1
+ import type { LiveQueryInput, LiveQueryObserveOptions } from "./live.js";
2
+ /** The structural live-query surface shared by MinnowDatabase and its worker client. */
3
+ export interface LiveQueryBackend {
4
+ observe(query: LiveQueryInput, options: LiveQueryObserveOptions): Promise<LiveQuerySubscriptionLike>;
5
+ refresh(): Promise<void>;
6
+ close(): void | Promise<void>;
7
+ }
8
+ export interface LiveQuerySubscriptionLike {
9
+ readonly dependencyTableIds: readonly string[];
10
+ close(): void | Promise<void>;
11
+ }
12
+ export interface LiveQueryDriver {
13
+ liveQueries(options?: {
14
+ channelName?: string;
15
+ pollIntervalMs?: number;
16
+ }): LiveQueryBackend;
17
+ }
18
+ /**
19
+ * Adapter-neutral query boundary. `query` drives dependency tracking in Minnow; `execute`
20
+ * deliberately stays with the adapter so its result transforms and inferred row type survive.
21
+ */
22
+ export interface LiveQuerySource<out TRow> {
23
+ readonly query: LiveQueryInput;
24
+ execute(signal?: AbortSignal): Promise<readonly TRow[]>;
25
+ }
26
+ export type LiveSnapshot<TRow> = {
27
+ readonly status: "loading";
28
+ readonly rows: readonly TRow[];
29
+ } | {
30
+ readonly status: "ready";
31
+ readonly rows: readonly TRow[];
32
+ readonly version: number | null;
33
+ } | {
34
+ readonly status: "error";
35
+ readonly rows: readonly TRow[];
36
+ readonly error: unknown;
37
+ readonly version: number | null;
38
+ };
39
+ /**
40
+ * One typed live SELECT. It is also a framework-neutral external store: getSnapshot is stable
41
+ * until state changes and subscribe returns synchronous cleanup. Slow consumers coalesce to the
42
+ * newest invalidation; errors retain the last good rows.
43
+ */
44
+ export declare class LiveQuery<out TRow> implements AsyncIterable<LiveSnapshot<TRow>> {
45
+ #private;
46
+ /** Type-only row helper (`typeof query.$inferRow`). */
47
+ readonly $inferRow: TRow;
48
+ constructor(backend: LiveQueryBackend, source: LiveQuerySource<TRow>, onClose?: () => void);
49
+ getSnapshot: () => LiveSnapshot<TRow>;
50
+ subscribe: (listener: () => void) => (() => void);
51
+ refresh(): Promise<void>;
52
+ close(): void;
53
+ [Symbol.asyncIterator](): AsyncIterator<LiveSnapshot<TRow>, undefined>;
54
+ }
55
+ export interface LiveQueryManagerOptions {
56
+ readonly channelName?: string;
57
+ readonly pollIntervalMs?: number;
58
+ }
59
+ /** Owns one shared low-level set and every typed query created from it. */
60
+ export declare class LiveQueryManager {
61
+ #private;
62
+ constructor(driver: LiveQueryDriver, options?: LiveQueryManagerOptions);
63
+ watch<TRow>(source: LiveQuerySource<TRow>): LiveQuery<TRow>;
64
+ refresh(): Promise<void>;
65
+ close(): Promise<void>;
66
+ }
67
+ export declare function createLiveQueryManager(driver: LiveQueryDriver, options?: LiveQueryManagerOptions): LiveQueryManager;
@@ -0,0 +1,349 @@
1
+ import { dateMilliseconds } from "../date-value.js";
2
+ function sameLiveValue(left, right) {
3
+ if (Object.is(left, right))
4
+ return true;
5
+ if (left instanceof Date || right instanceof Date) {
6
+ return (left instanceof Date &&
7
+ right instanceof Date &&
8
+ Object.is(dateMilliseconds(left), dateMilliseconds(right)));
9
+ }
10
+ if (Array.isArray(left) || Array.isArray(right)) {
11
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length)
12
+ return false;
13
+ for (let index = 0; index < left.length; index += 1) {
14
+ if (!sameLiveValue(left[index], right[index]))
15
+ return false;
16
+ }
17
+ return true;
18
+ }
19
+ if (typeof left !== "object" || left === null || typeof right !== "object" || right === null) {
20
+ return false;
21
+ }
22
+ const leftRecord = left;
23
+ const rightRecord = right;
24
+ const leftKeys = Object.keys(leftRecord);
25
+ const rightKeys = Object.keys(rightRecord);
26
+ if (leftKeys.length !== rightKeys.length)
27
+ return false;
28
+ for (let index = 0; index < leftKeys.length; index += 1) {
29
+ const key = leftKeys[index];
30
+ if (key === undefined || key !== rightKeys[index])
31
+ return false;
32
+ if (!sameLiveValue(leftRecord[key], rightRecord[key]))
33
+ return false;
34
+ }
35
+ return true;
36
+ }
37
+ function sameRows(left, right) {
38
+ return sameLiveValue(left, right);
39
+ }
40
+ function immutableRows(rows) {
41
+ // Adapters own their values. Copy the array so mutating the returned builder result cannot
42
+ // change the snapshot identity retained for exact suppression.
43
+ return Object.freeze([...rows]);
44
+ }
45
+ /**
46
+ * One typed live SELECT. It is also a framework-neutral external store: getSnapshot is stable
47
+ * until state changes and subscribe returns synchronous cleanup. Slow consumers coalesce to the
48
+ * newest invalidation; errors retain the last good rows.
49
+ */
50
+ export class LiveQuery {
51
+ #listeners = new Set();
52
+ #source;
53
+ #backend;
54
+ #onClose;
55
+ #snapshot = { status: "loading", rows: [] };
56
+ #subscription;
57
+ #observationGeneration = 0;
58
+ #queued;
59
+ #execution;
60
+ #executionAbort;
61
+ #invalidationSequence = 0;
62
+ #closed = false;
63
+ constructor(backend, source, onClose) {
64
+ this.#backend = backend;
65
+ this.#source = source;
66
+ this.#onClose = onClose;
67
+ }
68
+ getSnapshot = () => this.#snapshot;
69
+ subscribe = (listener) => {
70
+ if (this.#closed)
71
+ throw new Error("Live query is closed");
72
+ // A subscription is a lease, even when a framework reuses the same callback function.
73
+ const registered = () => listener();
74
+ this.#listeners.add(registered);
75
+ if (this.#listeners.size === 1)
76
+ this.#startObservation();
77
+ let subscribed = true;
78
+ return () => {
79
+ if (!subscribed)
80
+ return;
81
+ subscribed = false;
82
+ this.#listeners.delete(registered);
83
+ if (this.#listeners.size === 0)
84
+ this.#stopObservation();
85
+ };
86
+ };
87
+ async refresh() {
88
+ if (this.#closed)
89
+ throw new Error("Live query is closed");
90
+ const sequence = this.#invalidationSequence;
91
+ if (this.#listeners.size > 0) {
92
+ // A transient dependency/observer setup failure must not strand existing subscribers.
93
+ // Re-open first so this refresh and future commits reach the query again.
94
+ if (this.#subscription === undefined)
95
+ this.#startObservation();
96
+ const opening = this.#subscription;
97
+ if (opening !== undefined)
98
+ await opening.catch(() => undefined);
99
+ await this.#backend.refresh();
100
+ }
101
+ // A backend refresh normally invalidates observers itself. Only synthesize an execution
102
+ // when it did not, which preserves manual retry without running every refreshed query twice.
103
+ if (this.#invalidationSequence === sequence) {
104
+ const version = this.#snapshot.status === "loading" ? null : this.#snapshot.version;
105
+ this.#schedule({ manifestVersion: version, catalogEpoch: 0, initial: false });
106
+ }
107
+ await this.#waitForIdle();
108
+ }
109
+ close() {
110
+ if (this.#closed)
111
+ return;
112
+ this.#closed = true;
113
+ this.#observationGeneration += 1;
114
+ this.#executionAbort?.abort(new Error("Live query is closed"));
115
+ const subscription = this.#subscription;
116
+ this.#subscription = undefined;
117
+ if (subscription !== undefined) {
118
+ void subscription.then((handle) => handle.close()).catch(() => undefined);
119
+ }
120
+ // Wake iterators before removing their listeners so a parked next() terminates.
121
+ this.#emit();
122
+ this.#listeners.clear();
123
+ this.#onClose?.();
124
+ }
125
+ #startObservation() {
126
+ if (this.#subscription !== undefined || this.#closed)
127
+ return;
128
+ const generation = (this.#observationGeneration += 1);
129
+ const subscription = this.#backend.observe(this.#source.query, {
130
+ onInvalidate: (invalidation) => {
131
+ if (generation !== this.#observationGeneration || this.#closed)
132
+ return;
133
+ this.#schedule(invalidation);
134
+ },
135
+ onError: (error) => {
136
+ if (generation !== this.#observationGeneration || this.#closed)
137
+ return;
138
+ this.#setError(error, this.#currentVersion());
139
+ },
140
+ onComplete: () => {
141
+ if (generation !== this.#observationGeneration)
142
+ return;
143
+ this.#subscription = undefined;
144
+ },
145
+ });
146
+ this.#subscription = subscription;
147
+ subscription.catch((error) => {
148
+ if (generation !== this.#observationGeneration || this.#closed)
149
+ return;
150
+ this.#subscription = undefined;
151
+ this.#setError(error, this.#currentVersion());
152
+ });
153
+ }
154
+ #stopObservation() {
155
+ this.#observationGeneration += 1;
156
+ this.#executionAbort?.abort(new Error("Live query has no subscribers"));
157
+ const subscription = this.#subscription;
158
+ this.#subscription = undefined;
159
+ if (subscription !== undefined) {
160
+ void subscription.then((handle) => handle.close()).catch(() => undefined);
161
+ }
162
+ }
163
+ #schedule(invalidation) {
164
+ this.#invalidationSequence += 1;
165
+ this.#queued = invalidation;
166
+ if (this.#execution !== undefined)
167
+ return;
168
+ const execution = this.#drain();
169
+ this.#execution = execution;
170
+ void execution.finally(() => {
171
+ if (this.#execution === execution)
172
+ this.#execution = undefined;
173
+ if (this.#queued !== undefined && !this.#closed)
174
+ this.#schedule(this.#queued);
175
+ });
176
+ }
177
+ async #drain() {
178
+ while (this.#queued !== undefined && !this.#closed) {
179
+ const invalidation = this.#queued;
180
+ this.#queued = undefined;
181
+ const abort = new AbortController();
182
+ this.#executionAbort = abort;
183
+ try {
184
+ const rows = immutableRows(await this.#source.execute(abort.signal));
185
+ if (this.#executionWasCancelled(abort))
186
+ continue;
187
+ // A newer invalidation arrived while this ran. Skip the intermediate snapshot and let
188
+ // the loop execute once more against the newest durable state.
189
+ if (this.#hasQueuedInvalidation())
190
+ continue;
191
+ const previous = this.#snapshot.rows;
192
+ if (this.#snapshot.status === "ready" &&
193
+ this.#snapshot.version === invalidation.manifestVersion &&
194
+ sameRows(previous, rows)) {
195
+ continue;
196
+ }
197
+ if (sameRows(previous, rows) && this.#snapshot.status !== "loading") {
198
+ // Advance the version without replacing the immutable row array.
199
+ this.#snapshot = {
200
+ status: "ready",
201
+ rows: previous,
202
+ version: invalidation.manifestVersion,
203
+ };
204
+ }
205
+ else {
206
+ this.#snapshot = {
207
+ status: "ready",
208
+ rows,
209
+ version: invalidation.manifestVersion,
210
+ };
211
+ }
212
+ this.#emit();
213
+ }
214
+ catch (error) {
215
+ if (this.#executionWasCancelled(abort) || this.#hasQueuedInvalidation())
216
+ continue;
217
+ this.#setError(error, invalidation.manifestVersion);
218
+ }
219
+ finally {
220
+ if (this.#executionAbort === abort)
221
+ this.#executionAbort = undefined;
222
+ }
223
+ }
224
+ }
225
+ #executionWasCancelled(abort) {
226
+ return abort.signal.aborted || this.#closed;
227
+ }
228
+ #hasQueuedInvalidation() {
229
+ return this.#queued !== undefined;
230
+ }
231
+ async #waitForIdle() {
232
+ for (;;) {
233
+ const execution = this.#execution;
234
+ if (execution === undefined)
235
+ return;
236
+ await execution;
237
+ if (this.#execution === undefined && this.#queued === undefined)
238
+ return;
239
+ }
240
+ }
241
+ #currentVersion() {
242
+ return this.#snapshot.status === "loading" ? null : this.#snapshot.version;
243
+ }
244
+ #setError(error, version) {
245
+ this.#snapshot = { status: "error", rows: this.#snapshot.rows, error, version };
246
+ this.#emit();
247
+ }
248
+ #emit() {
249
+ for (const listener of [...this.#listeners]) {
250
+ try {
251
+ listener();
252
+ }
253
+ catch {
254
+ // One framework listener must not prevent the others from observing the same snapshot.
255
+ }
256
+ }
257
+ }
258
+ [Symbol.asyncIterator]() {
259
+ const inbox = {
260
+ latest: this.#snapshot,
261
+ hasLatest: true,
262
+ stopped: false,
263
+ notify: undefined,
264
+ };
265
+ const wake = () => {
266
+ const notify = inbox.notify;
267
+ inbox.notify = undefined;
268
+ notify?.();
269
+ };
270
+ const unsubscribe = this.subscribe(() => {
271
+ inbox.latest = this.#snapshot;
272
+ inbox.hasLatest = true;
273
+ wake();
274
+ });
275
+ const done = { done: true, value: undefined };
276
+ const read = async () => {
277
+ for (;;) {
278
+ if (inbox.stopped || this.#closed)
279
+ return done;
280
+ if (inbox.hasLatest) {
281
+ inbox.hasLatest = false;
282
+ return { done: false, value: inbox.latest };
283
+ }
284
+ await new Promise((resolve) => {
285
+ inbox.notify = resolve;
286
+ });
287
+ }
288
+ };
289
+ let queue = Promise.resolve();
290
+ return {
291
+ next: () => {
292
+ const result = queue.then(read, read);
293
+ queue = result.then(() => undefined, () => undefined);
294
+ return result;
295
+ },
296
+ return: async () => {
297
+ inbox.stopped = true;
298
+ wake();
299
+ unsubscribe();
300
+ return done;
301
+ },
302
+ throw: async (error) => {
303
+ inbox.stopped = true;
304
+ wake();
305
+ unsubscribe();
306
+ throw error;
307
+ },
308
+ };
309
+ }
310
+ }
311
+ /** Owns one shared low-level set and every typed query created from it. */
312
+ export class LiveQueryManager {
313
+ #backend;
314
+ #queries = new Set();
315
+ #closed = false;
316
+ constructor(driver, options = {}) {
317
+ this.#backend = driver.liveQueries(options);
318
+ }
319
+ watch(source) {
320
+ if (this.#closed)
321
+ throw new Error("Live query manager is closed");
322
+ const query = new LiveQuery(this.#backend, source, () => {
323
+ this.#queries.delete(query);
324
+ });
325
+ this.#queries.add(query);
326
+ return query;
327
+ }
328
+ async refresh() {
329
+ if (this.#closed)
330
+ throw new Error("Live query manager is closed");
331
+ // The shared backend sweep remains selective. Queries that were already in an adapter-level
332
+ // error also run their local retry path; concurrent backend refresh calls collapse into the
333
+ // same low-level sweep, so this does not multiply durable probes.
334
+ const failed = [...this.#queries].filter((query) => query.getSnapshot().status === "error");
335
+ await Promise.all([this.#backend.refresh(), ...failed.map((query) => query.refresh())]);
336
+ }
337
+ async close() {
338
+ if (this.#closed)
339
+ return;
340
+ this.#closed = true;
341
+ for (const query of [...this.#queries])
342
+ query.close();
343
+ this.#queries.clear();
344
+ await this.#backend.close();
345
+ }
346
+ }
347
+ export function createLiveQueryManager(driver, options = {}) {
348
+ return new LiveQueryManager(driver, options);
349
+ }
@@ -43,11 +43,16 @@ export interface ColumnarTable {
43
43
  readonly rowCount: number;
44
44
  readonly columns: ReadonlyMap<string, ColumnVector>;
45
45
  readonly uniqueKey?: string;
46
+ /** Optional physical-row permutation supplied by an exact index-ordered materialization. */
47
+ readonly scanOrder?: Uint32Array;
48
+ /** True only when that permutation satisfies this prepared plan's complete ORDER BY. */
49
+ readonly orderedForPlan?: true;
46
50
  }
47
51
  export interface PreparedVectorQuery {
48
52
  readonly memoryUsage: QueryMemoryUsage;
49
53
  execute(): QueryResult;
50
54
  executeAsync(options?: AsyncQueryExecutionOptions): Promise<QueryResult>;
55
+ executeBatches(options: QueryBatchExecutionOptions, consume: (batch: QueryResult) => void | Promise<void>): Promise<readonly string[]>;
51
56
  close(): void;
52
57
  }
53
58
  export interface QuerySpillStore {
@@ -80,6 +85,12 @@ export interface AsyncQueryExecutionOptions {
80
85
  */
81
86
  readonly loadScanWindow?: (start: number, length: number) => number | Promise<number>;
82
87
  }
88
+ export interface QueryBatchExecutionOptions extends AsyncQueryExecutionOptions {
89
+ /** Maximum result rows handed to the consumer at once. */
90
+ readonly batchRows: number;
91
+ /** Stops before the next scan batch and is checked after every awaited storage read. */
92
+ readonly signal?: AbortSignal;
93
+ }
83
94
  export interface PrepareVectorQueryOptions {
84
95
  readonly memoryContext?: QueryMemoryContext;
85
96
  /**
@@ -91,8 +102,7 @@ export interface PrepareVectorQueryOptions {
91
102
  readonly ftsStats?: ReadonlyMap<string, FtsStats>;
92
103
  }
93
104
  export declare function createColumnarTable(name: string, columns: ReadonlyMap<string, ColumnarColumnInput>, uniqueKey?: string): ColumnarTable;
94
- export declare function columnarTableFromRows(name: string, rows: readonly DatabaseRow[], projectedColumnNames?: readonly string[]): ColumnarTable;
105
+ export declare function columnarTableFromRows(name: string, rows: readonly DatabaseRow[], projectedColumnNames?: readonly string[], protectText?: boolean): ColumnarTable;
95
106
  export declare function prepareVectorQuery(plan: CompiledQuery, inputTables: ReadonlyMap<string, ColumnarTable>, options?: PrepareVectorQueryOptions): PreparedVectorQuery;
96
107
  export declare function vectorValue(vector: ColumnVector, rowIndex: number): QueryValue;
97
108
  export {};
98
- //# sourceMappingURL=vector.d.ts.map