@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.
- package/README.md +23 -32
- package/dist/block-format/block.d.ts +32 -13
- package/dist/block-format/block.js +179 -55
- package/dist/block-format/checksum.d.ts +2 -1
- package/dist/block-format/checksum.js +5 -2
- package/dist/block-format/codecs.d.ts +7 -2
- package/dist/block-format/codecs.js +53 -12
- package/dist/block-format/column.d.ts +3 -2
- package/dist/block-format/column.js +96 -30
- package/dist/block-format/index.d.ts +2 -2
- package/dist/block-format/index.js +2 -2
- package/dist/block-format/physical.d.ts +8 -2
- package/dist/block-format/physical.js +14 -6
- package/dist/block-format/types.d.ts +7 -3
- package/dist/block-format/types.js +0 -1
- package/dist/block-format/unicode.d.ts +11 -0
- package/dist/block-format/unicode.js +46 -0
- package/dist/date-value.d.ts +15 -0
- package/dist/date-value.js +64 -0
- package/dist/engine/artifact-cache.d.ts +2 -1
- package/dist/engine/artifact-cache.js +5 -1
- package/dist/engine/batch.d.ts +11 -3
- package/dist/engine/batch.js +17 -7
- package/dist/engine/buffered-writer.d.ts +2 -1
- package/dist/engine/buffered-writer.js +34 -9
- package/dist/engine/cache-limits.d.ts +25 -0
- package/dist/engine/cache-limits.js +25 -0
- package/dist/engine/catalog.d.ts +24 -5
- package/dist/engine/catalog.js +34 -7
- package/dist/engine/client.d.ts +29 -7
- package/dist/engine/client.js +216 -40
- package/dist/engine/database.d.ts +224 -39
- package/dist/engine/database.js +7475 -1539
- package/dist/engine/defaults.d.ts +4 -8
- package/dist/engine/defaults.js +46 -22
- package/dist/engine/errors.d.ts +19 -1
- package/dist/engine/errors.js +32 -2
- package/dist/engine/fts.d.ts +0 -6
- package/dist/engine/fts.js +41 -14
- package/dist/engine/group-index.d.ts +0 -1
- package/dist/engine/group-index.js +6 -11
- package/dist/engine/index.d.ts +16 -10
- package/dist/engine/index.js +12 -9
- package/dist/engine/join-index.d.ts +0 -1
- package/dist/engine/join-index.js +2 -2
- package/dist/engine/keyed-live.d.ts +57 -0
- package/dist/engine/keyed-live.js +226 -0
- package/dist/engine/live-api.d.ts +4 -0
- package/dist/engine/live-api.js +4 -0
- package/dist/engine/live.d.ts +43 -26
- package/dist/engine/live.js +504 -126
- package/dist/engine/memory.d.ts +2 -1
- package/dist/engine/memory.js +3 -2
- package/dist/engine/optimizer.d.ts +0 -1
- package/dist/engine/optimizer.js +262 -27
- package/dist/engine/query-api.d.ts +3 -0
- package/dist/engine/query-api.js +3 -0
- package/dist/engine/query-cache.d.ts +1 -2
- package/dist/engine/query-cache.js +7 -5
- package/dist/engine/query.d.ts +134 -29
- package/dist/engine/query.js +1668 -372
- package/dist/engine/result-wire.d.ts +0 -1
- package/dist/engine/result-wire.js +2 -2
- package/dist/engine/schema-wire.d.ts +6 -3
- package/dist/engine/schema-wire.js +40 -34
- package/dist/engine/schema.d.ts +156 -80
- package/dist/engine/schema.js +570 -108
- package/dist/engine/sort-keys.d.ts +4 -4
- package/dist/engine/sort-keys.js +44 -29
- package/dist/engine/sql-domains.d.ts +31 -0
- package/dist/engine/sql-domains.js +585 -0
- package/dist/engine/sql-driver.d.ts +19 -0
- package/dist/engine/sql-driver.js +1 -0
- package/dist/engine/sql-json.d.ts +7 -9
- package/dist/engine/sql-json.js +75 -15
- package/dist/engine/sql-semantics.d.ts +8 -3
- package/dist/engine/sql-semantics.js +458 -30
- package/dist/engine/typed-live.d.ts +67 -0
- package/dist/engine/typed-live.js +349 -0
- package/dist/engine/vector.d.ts +12 -2
- package/dist/engine/vector.js +635 -119
- package/dist/engine/worker-host.d.ts +9 -2
- package/dist/engine/worker-host.js +620 -152
- package/dist/engine/worker.d.ts +0 -1
- package/dist/engine/worker.js +3 -3
- package/dist/engine/write-block-planner.d.ts +18 -0
- package/dist/engine/write-block-planner.js +112 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/plan/index.d.ts +3 -4
- package/dist/plan/index.js +3 -4
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/indexeddb.d.ts +99 -51
- package/dist/storage/indexeddb.js +13448 -2660
- package/dist/storage/memory.d.ts +64 -57
- package/dist/storage/memory.js +1042 -128
- package/dist/storage/opfs/files.d.ts +18 -7
- package/dist/storage/opfs/files.js +115 -28
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +1 -1
- package/dist/storage/opfs/leader.d.ts +409 -75
- package/dist/storage/opfs/leader.js +4621 -650
- package/dist/storage/opfs/rpc.d.ts +15 -3
- package/dist/storage/opfs/rpc.js +224 -2
- package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
- package/dist/storage/opfs/snapshot-ledger.js +281 -0
- package/dist/storage/opfs/store.d.ts +33 -99
- package/dist/storage/opfs/store.js +453 -364
- package/dist/storage/persistence.d.ts +37 -0
- package/dist/storage/persistence.js +78 -0
- package/dist/storage/snapshot-stream.d.ts +24 -0
- package/dist/storage/snapshot-stream.js +897 -0
- package/dist/storage/snapshot.d.ts +9 -85
- package/dist/storage/snapshot.js +33 -265
- package/dist/storage/toolkit/extents.d.ts +41 -7
- package/dist/storage/toolkit/extents.js +402 -39
- package/dist/storage/toolkit/index.d.ts +4 -5
- package/dist/storage/toolkit/index.js +28 -4
- package/dist/storage/toolkit/record-core.d.ts +182 -55
- package/dist/storage/toolkit/record-core.js +6158 -1331
- package/dist/storage/toolkit/sync-file.d.ts +10 -1
- package/dist/storage/toolkit/sync-file.js +50 -2
- package/dist/storage/toolkit/wal.d.ts +23 -5
- package/dist/storage/toolkit/wal.js +89 -27
- package/dist/storage/toolkit/wire.d.ts +15 -2
- package/dist/storage/toolkit/wire.js +199 -10
- package/dist/storage/types.d.ts +1439 -204
- package/dist/storage/types.js +1745 -141
- package/dist/testing/block-store-conformance.d.ts +0 -1
- package/dist/testing/block-store-conformance.js +1009 -119
- package/dist/testing/index.d.ts +46 -26
- package/dist/testing/index.js +112 -57
- package/dist/testing/opfs-shim.d.ts +13 -3
- package/dist/testing/opfs-shim.js +40 -6
- package/dist/testing/simulator.d.ts +97 -0
- package/dist/testing/simulator.js +591 -0
- package/dist/testing/sqllogictest.d.ts +89 -0
- package/dist/testing/sqllogictest.js +434 -0
- package/dist/transactions/index.d.ts +92 -16
- package/dist/transactions/index.js +1074 -236
- package/dist/worker-protocol/index.d.ts +3 -2
- package/dist/worker-protocol/index.js +2 -5
- package/package.json +53 -3
- package/postgres-feature-profile.json +223 -0
- package/sql-feature-matrix.json +172 -252
- package/dist/block-format/block.d.ts.map +0 -1
- package/dist/block-format/block.js.map +0 -1
- package/dist/block-format/checksum.d.ts.map +0 -1
- package/dist/block-format/checksum.js.map +0 -1
- package/dist/block-format/codecs.d.ts.map +0 -1
- package/dist/block-format/codecs.js.map +0 -1
- package/dist/block-format/column.d.ts.map +0 -1
- package/dist/block-format/column.js.map +0 -1
- package/dist/block-format/index.d.ts.map +0 -1
- package/dist/block-format/index.js.map +0 -1
- package/dist/block-format/physical.d.ts.map +0 -1
- package/dist/block-format/physical.js.map +0 -1
- package/dist/block-format/types.d.ts.map +0 -1
- package/dist/block-format/types.js.map +0 -1
- package/dist/engine/artifact-cache.d.ts.map +0 -1
- package/dist/engine/artifact-cache.js.map +0 -1
- package/dist/engine/batch.d.ts.map +0 -1
- package/dist/engine/batch.js.map +0 -1
- package/dist/engine/buffered-writer.d.ts.map +0 -1
- package/dist/engine/buffered-writer.js.map +0 -1
- package/dist/engine/catalog.d.ts.map +0 -1
- package/dist/engine/catalog.js.map +0 -1
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/coordinator.d.ts +0 -17
- package/dist/engine/coordinator.d.ts.map +0 -1
- package/dist/engine/coordinator.js +0 -60
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/engine/database.d.ts.map +0 -1
- package/dist/engine/database.js.map +0 -1
- package/dist/engine/defaults.d.ts.map +0 -1
- package/dist/engine/defaults.js.map +0 -1
- package/dist/engine/errors.d.ts.map +0 -1
- package/dist/engine/errors.js.map +0 -1
- package/dist/engine/fts.d.ts.map +0 -1
- package/dist/engine/fts.js.map +0 -1
- package/dist/engine/group-index.d.ts.map +0 -1
- package/dist/engine/group-index.js.map +0 -1
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/join-index.d.ts.map +0 -1
- package/dist/engine/join-index.js.map +0 -1
- package/dist/engine/live.d.ts.map +0 -1
- package/dist/engine/live.js.map +0 -1
- package/dist/engine/memory.d.ts.map +0 -1
- package/dist/engine/memory.js.map +0 -1
- package/dist/engine/optimizer.d.ts.map +0 -1
- package/dist/engine/optimizer.js.map +0 -1
- package/dist/engine/query-cache.d.ts.map +0 -1
- package/dist/engine/query-cache.js.map +0 -1
- package/dist/engine/query.d.ts.map +0 -1
- package/dist/engine/query.js.map +0 -1
- package/dist/engine/result-wire.d.ts.map +0 -1
- package/dist/engine/result-wire.js.map +0 -1
- package/dist/engine/schema-wire.d.ts.map +0 -1
- package/dist/engine/schema-wire.js.map +0 -1
- package/dist/engine/schema.d.ts.map +0 -1
- package/dist/engine/schema.js.map +0 -1
- package/dist/engine/sort-keys.d.ts.map +0 -1
- package/dist/engine/sort-keys.js.map +0 -1
- package/dist/engine/sql-json.d.ts.map +0 -1
- package/dist/engine/sql-json.js.map +0 -1
- package/dist/engine/sql-semantics.d.ts.map +0 -1
- package/dist/engine/sql-semantics.js.map +0 -1
- package/dist/engine/vector.d.ts.map +0 -1
- package/dist/engine/vector.js.map +0 -1
- package/dist/engine/worker-host.d.ts.map +0 -1
- package/dist/engine/worker-host.js.map +0 -1
- package/dist/engine/worker.d.ts.map +0 -1
- package/dist/engine/worker.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js.map +0 -1
- package/dist/storage/fixture-shape.d.ts +0 -42
- package/dist/storage/fixture-shape.d.ts.map +0 -1
- package/dist/storage/fixture-shape.js +0 -146
- package/dist/storage/fixture-shape.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/indexeddb.d.ts.map +0 -1
- package/dist/storage/indexeddb.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/opfs/files.d.ts.map +0 -1
- package/dist/storage/opfs/files.js.map +0 -1
- package/dist/storage/opfs/index.d.ts.map +0 -1
- package/dist/storage/opfs/index.js.map +0 -1
- package/dist/storage/opfs/leader.d.ts.map +0 -1
- package/dist/storage/opfs/leader.js.map +0 -1
- package/dist/storage/opfs/rpc.d.ts.map +0 -1
- package/dist/storage/opfs/rpc.js.map +0 -1
- package/dist/storage/opfs/store.d.ts.map +0 -1
- package/dist/storage/opfs/store.js.map +0 -1
- package/dist/storage/snapshot.d.ts.map +0 -1
- package/dist/storage/snapshot.js.map +0 -1
- package/dist/storage/toolkit/extents.d.ts.map +0 -1
- package/dist/storage/toolkit/extents.js.map +0 -1
- package/dist/storage/toolkit/index.d.ts.map +0 -1
- package/dist/storage/toolkit/index.js.map +0 -1
- package/dist/storage/toolkit/record-core.d.ts.map +0 -1
- package/dist/storage/toolkit/record-core.js.map +0 -1
- package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
- package/dist/storage/toolkit/sync-file.js.map +0 -1
- package/dist/storage/toolkit/wal.d.ts.map +0 -1
- package/dist/storage/toolkit/wal.js.map +0 -1
- package/dist/storage/toolkit/wire.d.ts.map +0 -1
- package/dist/storage/toolkit/wire.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/testing/block-store-conformance.d.ts.map +0 -1
- package/dist/testing/block-store-conformance.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/opfs-shim.d.ts.map +0 -1
- package/dist/testing/opfs-shim.js.map +0 -1
- package/dist/testing/seeds.d.ts +0 -11
- package/dist/testing/seeds.d.ts.map +0 -1
- package/dist/testing/seeds.js +0 -50
- package/dist/testing/seeds.js.map +0 -1
- package/dist/transactions/index.d.ts.map +0 -1
- package/dist/transactions/index.js.map +0 -1
- package/dist/worker-protocol/index.d.ts.map +0 -1
- package/dist/worker-protocol/index.js.map +0 -1
package/dist/engine/client.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { CompactionJobConflictError, GarbageCollectionJobConflictError, LeaseConflictError, SnapshotManifestMissingError, TableRecordConflictError, TempOwnerConflictError, TransactionRecordConflictError, UniqueKeyConflictError, WriteConflictError, } from "../storage/
|
|
2
|
-
import {
|
|
1
|
+
import { BlockReadBatchTooLargeError, CompactionBacklogError, CompactionJobConflictError, GarbageCollectionJobConflictError, IndexedDbSchemaUpgradeBlockedError, LeaseConflictError, LeaseExpiredError, LeaseOwnerConflictError, PostingBuildConflictError, SnapshotManifestMissingError, SnapshotImportConflictError, SchemaConflictError, StorageResourceLimitError, TableInUseError, TableRecordConflictError, TempOwnerConflictError, TransactionRecordConflictError, UniqueKeyBuildConflictError, UniqueKeyConflictError, UniqueIndexCoverageError, WriteConflictError, StorageCorruptionError, StorageFormatVersionError, OpfsUncertainOutcomeError, } from "../storage/types.js";
|
|
2
|
+
import { MAX_SNAPSHOT_STREAM_CHUNK_BYTES } from "../storage/snapshot.js";
|
|
3
|
+
import { parseRpcResponse, MAX_DATABASE_RPC_IN_FLIGHT, protocolVersion, } from "../worker-protocol/index.js";
|
|
3
4
|
import { toColumnarBatch } from "./batch.js";
|
|
4
|
-
import { CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MissingKeyError, SqlCompileError, UniqueConstraintError, } from "./errors.js";
|
|
5
|
+
import { CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, MissingKeyError, SqlCompileError, UniqueConstraintError, VisibleSegmentCursorStaleError, } from "./errors.js";
|
|
5
6
|
import { QueryMemoryBudgetError } from "./memory.js";
|
|
6
7
|
import { decodeQueryResult } from "./result-wire.js";
|
|
7
8
|
import { serializeSchema } from "./schema-wire.js";
|
|
@@ -10,24 +11,56 @@ import { serializeSchema } from "./schema-wire.js";
|
|
|
10
11
|
* database is tens of round trips, small enough that no single structured clone is long enough
|
|
11
12
|
* to be felt as a dropped frame.
|
|
12
13
|
*/
|
|
13
|
-
const SNAPSHOT_CHUNK_BYTES =
|
|
14
|
+
const SNAPSHOT_CHUNK_BYTES = MAX_SNAPSHOT_STREAM_CHUNK_BYTES;
|
|
15
|
+
const MAX_SNAPSHOT_BYTE_ARRAY_LENGTH = 0xffffffff;
|
|
16
|
+
async function* clientSnapshotChunks(bytes) {
|
|
17
|
+
for (let offset = 0; offset < bytes.byteLength; offset += SNAPSHOT_CHUNK_BYTES) {
|
|
18
|
+
yield bytes.subarray(offset, offset + SNAPSHOT_CHUNK_BYTES);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function throwIfClientSnapshotAborted(signal) {
|
|
22
|
+
if (signal?.aborted !== true)
|
|
23
|
+
return;
|
|
24
|
+
if (signal.reason instanceof Error)
|
|
25
|
+
throw signal.reason;
|
|
26
|
+
const error = new Error("Snapshot operation was aborted", { cause: signal.reason });
|
|
27
|
+
error.name = "AbortError";
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
14
30
|
const errorRegistry = new Map([
|
|
15
31
|
UniqueConstraintError,
|
|
16
32
|
MissingKeyError,
|
|
33
|
+
CompactionBacklogError,
|
|
17
34
|
CompactionMemoryBudgetError,
|
|
18
35
|
CompactionWriteAmplificationError,
|
|
19
36
|
CompactionJobCancelledError,
|
|
37
|
+
MaintenanceBacklogError,
|
|
20
38
|
SqlCompileError,
|
|
21
39
|
QueryMemoryBudgetError,
|
|
40
|
+
VisibleSegmentCursorStaleError,
|
|
41
|
+
BlockReadBatchTooLargeError,
|
|
22
42
|
WriteConflictError,
|
|
43
|
+
SchemaConflictError,
|
|
23
44
|
UniqueKeyConflictError,
|
|
45
|
+
UniqueIndexCoverageError,
|
|
24
46
|
TableRecordConflictError,
|
|
25
47
|
CompactionJobConflictError,
|
|
26
48
|
GarbageCollectionJobConflictError,
|
|
49
|
+
IndexedDbSchemaUpgradeBlockedError,
|
|
27
50
|
SnapshotManifestMissingError,
|
|
51
|
+
SnapshotImportConflictError,
|
|
52
|
+
TableInUseError,
|
|
28
53
|
TransactionRecordConflictError,
|
|
29
54
|
LeaseConflictError,
|
|
55
|
+
LeaseExpiredError,
|
|
56
|
+
LeaseOwnerConflictError,
|
|
57
|
+
StorageResourceLimitError,
|
|
30
58
|
TempOwnerConflictError,
|
|
59
|
+
UniqueKeyBuildConflictError,
|
|
60
|
+
PostingBuildConflictError,
|
|
61
|
+
StorageCorruptionError,
|
|
62
|
+
StorageFormatVersionError,
|
|
63
|
+
OpfsUncertainOutcomeError,
|
|
31
64
|
].map((constructor) => [constructor.name, constructor]));
|
|
32
65
|
function rehydrateError(serialized) {
|
|
33
66
|
const constructor = errorRegistry.get(serialized.name);
|
|
@@ -63,17 +96,20 @@ export class MinnowDatabaseClient {
|
|
|
63
96
|
#fatal;
|
|
64
97
|
#closed = false;
|
|
65
98
|
#onVisibilityChange;
|
|
99
|
+
#onMessage = (event) => {
|
|
100
|
+
this.#receive(event.data);
|
|
101
|
+
};
|
|
102
|
+
#onError = () => {
|
|
103
|
+
this.#fail(new Error("The database worker failed; see the worker's own error output"));
|
|
104
|
+
};
|
|
105
|
+
#onMessageError = () => {
|
|
106
|
+
this.#fail(new Error("A database worker message could not be deserialized"));
|
|
107
|
+
};
|
|
66
108
|
constructor(transport, options = {}) {
|
|
67
109
|
this.#transport = transport;
|
|
68
|
-
transport.addEventListener("message",
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
transport.addEventListener("error", () => {
|
|
72
|
-
this.#fail(new Error("The database worker failed; see the worker's own error output"));
|
|
73
|
-
});
|
|
74
|
-
transport.addEventListener("messageerror", () => {
|
|
75
|
-
this.#fail(new Error("A database worker message could not be deserialized"));
|
|
76
|
-
});
|
|
110
|
+
transport.addEventListener("message", this.#onMessage);
|
|
111
|
+
transport.addEventListener("error", this.#onError);
|
|
112
|
+
transport.addEventListener("messageerror", this.#onMessageError);
|
|
77
113
|
const payload = {
|
|
78
114
|
store: options.store ?? { kind: "indexeddb", name: "minnow" },
|
|
79
115
|
...(options.databaseOptions === undefined ? {} : { options: options.databaseOptions }),
|
|
@@ -143,6 +179,9 @@ export class MinnowDatabaseClient {
|
|
|
143
179
|
async deleteBatch(tableName, input) {
|
|
144
180
|
return (await this.#call("deleteBatch", [tableName, input]));
|
|
145
181
|
}
|
|
182
|
+
async delete(tableName, key) {
|
|
183
|
+
return (await this.#call("delete", [tableName, key]));
|
|
184
|
+
}
|
|
146
185
|
bufferedWriter(tableName, options = {}) {
|
|
147
186
|
const handleId = crypto.randomUUID();
|
|
148
187
|
const { onError, ...wireOptions } = options;
|
|
@@ -163,6 +202,50 @@ export class MinnowDatabaseClient {
|
|
|
163
202
|
async query(sql, options) {
|
|
164
203
|
return decodeQueryResult(await this.#call("query", options === undefined ? [sql] : [sql, options]));
|
|
165
204
|
}
|
|
205
|
+
/** Pulls one transferred columnar page at a time, preserving worker backpressure. */
|
|
206
|
+
queryCursor(sql, options = {}) {
|
|
207
|
+
const call = this.#call.bind(this);
|
|
208
|
+
const invoke = this._invoke.bind(this);
|
|
209
|
+
const handleId = crypto.randomUUID();
|
|
210
|
+
const { signal, onStats, ...wireOptions } = options;
|
|
211
|
+
if (onStats !== undefined) {
|
|
212
|
+
throw new TypeError("Query cursor onStats callbacks are not available across a worker");
|
|
213
|
+
}
|
|
214
|
+
async function* batches() {
|
|
215
|
+
let opened = false;
|
|
216
|
+
let closing;
|
|
217
|
+
const close = () => {
|
|
218
|
+
if (!opened)
|
|
219
|
+
return Promise.resolve();
|
|
220
|
+
closing ??= invoke(handleId, "close", []).catch(() => undefined);
|
|
221
|
+
return closing;
|
|
222
|
+
};
|
|
223
|
+
const onAbort = () => {
|
|
224
|
+
void close();
|
|
225
|
+
};
|
|
226
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
227
|
+
try {
|
|
228
|
+
signal?.throwIfAborted();
|
|
229
|
+
await call("queryCursorOpen", [handleId, sql, wireOptions]);
|
|
230
|
+
opened = true;
|
|
231
|
+
if (signal?.aborted === true) {
|
|
232
|
+
await close();
|
|
233
|
+
signal.throwIfAborted();
|
|
234
|
+
}
|
|
235
|
+
for (;;) {
|
|
236
|
+
const next = (await invoke(handleId, "next", []));
|
|
237
|
+
if (next.done)
|
|
238
|
+
return undefined;
|
|
239
|
+
yield decodeQueryResult(next.batch);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
signal?.removeEventListener("abort", onAbort);
|
|
244
|
+
await close();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return batches();
|
|
248
|
+
}
|
|
166
249
|
async run(query) {
|
|
167
250
|
return decodeQueryResult(await this.#call("run", [query])).rows;
|
|
168
251
|
}
|
|
@@ -231,30 +314,56 @@ export class MinnowDatabaseClient {
|
|
|
231
314
|
* database; `onProgress` reports each slice as it lands.
|
|
232
315
|
*/
|
|
233
316
|
async exportSnapshot(options = {}) {
|
|
317
|
+
const chunks = [];
|
|
318
|
+
let byteLength = 0;
|
|
319
|
+
for await (const chunk of this.exportSnapshotStream(options)) {
|
|
320
|
+
byteLength += chunk.byteLength;
|
|
321
|
+
if (!Number.isSafeInteger(byteLength) || byteLength > MAX_SNAPSHOT_BYTE_ARRAY_LENGTH) {
|
|
322
|
+
throw new RangeError("Snapshot exceeds the byte-array API's 4 GiB limit");
|
|
323
|
+
}
|
|
324
|
+
chunks.push(chunk);
|
|
325
|
+
}
|
|
326
|
+
const bytes = new Uint8Array(byteLength);
|
|
327
|
+
let offset = 0;
|
|
328
|
+
for (const chunk of chunks) {
|
|
329
|
+
bytes.set(chunk, offset);
|
|
330
|
+
offset += chunk.byteLength;
|
|
331
|
+
}
|
|
332
|
+
return bytes;
|
|
333
|
+
}
|
|
334
|
+
/** Pulls at most one bounded worker chunk at a time; closing releases the worker-side pin. */
|
|
335
|
+
async *exportSnapshotStream(options = {}) {
|
|
336
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
234
337
|
options.onProgress?.({ phase: "reading", transferredBytes: 0, totalBytes: 0 });
|
|
235
338
|
const opened = (await this.#call("exportSnapshotOpen", []));
|
|
236
|
-
|
|
237
|
-
|
|
339
|
+
let transferredBytes = 0;
|
|
340
|
+
let succeeded = false;
|
|
238
341
|
try {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
options.onProgress?.({ phase: "transfer", transferredBytes
|
|
342
|
+
for (;;) {
|
|
343
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
344
|
+
const next = (await this._invoke(opened.handleId, "read", []));
|
|
345
|
+
if (next.done)
|
|
346
|
+
break;
|
|
347
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
348
|
+
if (!(next.chunk instanceof Uint8Array) || next.chunk.byteLength === 0) {
|
|
349
|
+
throw new Error("Snapshot transfer returned an invalid chunk");
|
|
350
|
+
}
|
|
351
|
+
yield next.chunk;
|
|
352
|
+
transferredBytes += next.chunk.byteLength;
|
|
353
|
+
options.onProgress?.({ phase: "transfer", transferredBytes, totalBytes: 0 });
|
|
251
354
|
}
|
|
355
|
+
succeeded = true;
|
|
252
356
|
}
|
|
253
357
|
finally {
|
|
254
358
|
await this._invoke(opened.handleId, "close", []).catch(() => undefined);
|
|
359
|
+
if (succeeded) {
|
|
360
|
+
options.onProgress?.({
|
|
361
|
+
phase: "done",
|
|
362
|
+
transferredBytes,
|
|
363
|
+
totalBytes: transferredBytes,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
255
366
|
}
|
|
256
|
-
options.onProgress?.({ phase: "done", transferredBytes: totalBytes, totalBytes });
|
|
257
|
-
return bytes;
|
|
258
367
|
}
|
|
259
368
|
/**
|
|
260
369
|
* Loads a snapshot file into the worker's store, which must be empty. The bytes go over in the
|
|
@@ -262,6 +371,10 @@ export class MinnowDatabaseClient {
|
|
|
262
371
|
* multi-megabyte restore can be shown moving rather than as a frozen tab.
|
|
263
372
|
*/
|
|
264
373
|
async importSnapshot(bytes, options = {}) {
|
|
374
|
+
await this.importSnapshotStream(clientSnapshotChunks(bytes), options);
|
|
375
|
+
}
|
|
376
|
+
/** Uploads with one acknowledged worker chunk in flight; cancellation leaves resumable state. */
|
|
377
|
+
async importSnapshotStream(source, options = {}) {
|
|
265
378
|
const handleId = crypto.randomUUID();
|
|
266
379
|
const { onProgress } = options;
|
|
267
380
|
this.#events.set(handleId, {
|
|
@@ -274,17 +387,25 @@ export class MinnowDatabaseClient {
|
|
|
274
387
|
}),
|
|
275
388
|
});
|
|
276
389
|
try {
|
|
390
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
277
391
|
await this.#call("importSnapshotOpen", [handleId]);
|
|
278
|
-
for (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
392
|
+
for await (const input of source) {
|
|
393
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
394
|
+
if (!(input instanceof Uint8Array)) {
|
|
395
|
+
throw new TypeError("Snapshot stream chunks must be Uint8Array values");
|
|
396
|
+
}
|
|
397
|
+
for (let offset = 0; offset < input.byteLength; offset += SNAPSHOT_CHUNK_BYTES) {
|
|
398
|
+
// Copy the bounded window: cloning a subarray would otherwise transfer its whole backing
|
|
399
|
+
// file on every request.
|
|
400
|
+
const chunk = input.slice(offset, offset + SNAPSHOT_CHUNK_BYTES);
|
|
401
|
+
await this.#post("rpc-call", handleId, "write", [chunk], [chunk.buffer]);
|
|
402
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
403
|
+
}
|
|
282
404
|
}
|
|
283
405
|
await this._invoke(handleId, "finish", []);
|
|
284
406
|
}
|
|
285
407
|
catch (error) {
|
|
286
|
-
|
|
287
|
-
await this._invoke(handleId, "close", []).catch(() => undefined);
|
|
408
|
+
await this._invoke(handleId, options.signal?.aborted === true ? "cancel" : "close", []).catch(() => undefined);
|
|
288
409
|
throw error;
|
|
289
410
|
}
|
|
290
411
|
finally {
|
|
@@ -295,14 +416,31 @@ export class MinnowDatabaseClient {
|
|
|
295
416
|
async bufferPoolStats() {
|
|
296
417
|
return (await this.#call("bufferPoolStats", []));
|
|
297
418
|
}
|
|
419
|
+
async maintenanceStatus() {
|
|
420
|
+
return (await this.#call("maintenanceStatus", []));
|
|
421
|
+
}
|
|
422
|
+
async checkIntegrity(options = {}) {
|
|
423
|
+
return (await this.#call("checkIntegrity", [options]));
|
|
424
|
+
}
|
|
425
|
+
async storageStats() {
|
|
426
|
+
return (await this.#call("storageStats", []));
|
|
427
|
+
}
|
|
428
|
+
async inspectInterruptedImport() {
|
|
429
|
+
return (await this.#call("inspectInterruptedImport", []));
|
|
430
|
+
}
|
|
431
|
+
async abortInterruptedImport(identity) {
|
|
432
|
+
return (await this.#call("abortInterruptedImport", [
|
|
433
|
+
identity,
|
|
434
|
+
]));
|
|
435
|
+
}
|
|
298
436
|
liveQueries(options = {}) {
|
|
299
437
|
const handleId = crypto.randomUUID();
|
|
300
438
|
const created = this.#call("liveQueries", [handleId, options]);
|
|
301
439
|
return new ClientLiveQuerySet(this, handleId, created);
|
|
302
440
|
}
|
|
303
441
|
// --- Maintenance ------------------------------------------------------------------------------
|
|
304
|
-
async
|
|
305
|
-
return (await this.#call("
|
|
442
|
+
async listVisibleSegmentPage(tableName, options) {
|
|
443
|
+
return (await this.#call("listVisibleSegmentPage", options === undefined ? [tableName] : [tableName, options]));
|
|
306
444
|
}
|
|
307
445
|
async cleanupQuerySpill(options) {
|
|
308
446
|
return (await this.#call("cleanupQuerySpill", options === undefined ? [] : [options]));
|
|
@@ -348,6 +486,9 @@ export class MinnowDatabaseClient {
|
|
|
348
486
|
}
|
|
349
487
|
finally {
|
|
350
488
|
this.#events.clear();
|
|
489
|
+
this.#transport.removeEventListener?.("message", this.#onMessage);
|
|
490
|
+
this.#transport.removeEventListener?.("error", this.#onError);
|
|
491
|
+
this.#transport.removeEventListener?.("messageerror", this.#onMessageError);
|
|
351
492
|
if (options.terminateWorker === true)
|
|
352
493
|
this.#transport.terminate?.();
|
|
353
494
|
}
|
|
@@ -355,6 +496,8 @@ export class MinnowDatabaseClient {
|
|
|
355
496
|
// --- Channel internals ------------------------------------------------------------------------
|
|
356
497
|
/** @internal */
|
|
357
498
|
async _invoke(handleId, method, args) {
|
|
499
|
+
if (this.#closed)
|
|
500
|
+
throw new Error("Database client is closed");
|
|
358
501
|
return this.#post("rpc-call", handleId, method, args);
|
|
359
502
|
}
|
|
360
503
|
/** @internal */
|
|
@@ -370,15 +513,18 @@ export class MinnowDatabaseClient {
|
|
|
370
513
|
throw new Error("Database client is closed");
|
|
371
514
|
return this.#post("rpc-call", null, method, args);
|
|
372
515
|
}
|
|
373
|
-
async #post(kind, handleId, method, args) {
|
|
516
|
+
async #post(kind, handleId, method, args, transfer, bypassLimit = kind === "rpc-init" || method === "dispose") {
|
|
374
517
|
if (this.#fatal !== undefined)
|
|
375
518
|
throw this.#fatal;
|
|
519
|
+
if (!bypassLimit && this.#pending.size >= MAX_DATABASE_RPC_IN_FLIGHT) {
|
|
520
|
+
throw new RangeError(`A database worker connection cannot hold more than ${String(MAX_DATABASE_RPC_IN_FLIGHT)} in-flight requests`);
|
|
521
|
+
}
|
|
376
522
|
const requestId = crypto.randomUUID();
|
|
377
523
|
return new Promise((resolve, reject) => {
|
|
378
524
|
this.#pending.set(requestId, { resolve, reject });
|
|
379
525
|
this.#transport.postMessage(kind === "rpc-init"
|
|
380
526
|
? { version: protocolVersion, requestId, kind, payload: args[0] }
|
|
381
|
-
: { version: protocolVersion, requestId, kind, handleId, method, args });
|
|
527
|
+
: { version: protocolVersion, requestId, kind, handleId, method, args }, transfer === undefined ? undefined : { transfer });
|
|
382
528
|
});
|
|
383
529
|
}
|
|
384
530
|
#receive(message) {
|
|
@@ -391,6 +537,9 @@ export class MinnowDatabaseClient {
|
|
|
391
537
|
return;
|
|
392
538
|
if (response.event === "change")
|
|
393
539
|
route.onChange?.(decodeQueryResult(response.payload));
|
|
540
|
+
else if (response.event === "invalidate") {
|
|
541
|
+
route.onInvalidate?.(response.payload);
|
|
542
|
+
}
|
|
394
543
|
else if (response.event === "error") {
|
|
395
544
|
route.onError?.(rehydrateError(response.payload));
|
|
396
545
|
}
|
|
@@ -415,6 +564,7 @@ export class MinnowDatabaseClient {
|
|
|
415
564
|
this.#fatal = error;
|
|
416
565
|
const pending = [...this.#pending.values()];
|
|
417
566
|
this.#pending.clear();
|
|
567
|
+
this.#events.clear();
|
|
418
568
|
for (const call of pending)
|
|
419
569
|
call.reject(error);
|
|
420
570
|
}
|
|
@@ -507,6 +657,33 @@ export class ClientLiveQuerySet {
|
|
|
507
657
|
throw error;
|
|
508
658
|
}
|
|
509
659
|
}
|
|
660
|
+
/** Registers dependency observation while leaving execution/result mapping to an adapter. */
|
|
661
|
+
async observe(query, options) {
|
|
662
|
+
await this.#created;
|
|
663
|
+
const subscriptionId = crypto.randomUUID();
|
|
664
|
+
const state = { completed: false };
|
|
665
|
+
this.client._routeEvents(subscriptionId, {
|
|
666
|
+
onInvalidate: options.onInvalidate.bind(options),
|
|
667
|
+
...(options.onError === undefined ? {} : { onError: options.onError.bind(options) }),
|
|
668
|
+
onComplete: () => {
|
|
669
|
+
state.completed = true;
|
|
670
|
+
this.#subscriptionIds.delete(subscriptionId);
|
|
671
|
+
options.onComplete?.();
|
|
672
|
+
},
|
|
673
|
+
});
|
|
674
|
+
try {
|
|
675
|
+
const created = (await this.client._invoke(this.handleId, "observe", [
|
|
676
|
+
subscriptionId,
|
|
677
|
+
query,
|
|
678
|
+
]));
|
|
679
|
+
this.#subscriptionIds.add(subscriptionId);
|
|
680
|
+
return new ClientLiveSubscription(this.client, subscriptionId, created.dependencyTableIds, () => this.#subscriptionIds.delete(subscriptionId), state);
|
|
681
|
+
}
|
|
682
|
+
catch (error) {
|
|
683
|
+
this.client._unrouteEvents(subscriptionId);
|
|
684
|
+
throw error;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
510
687
|
async stats() {
|
|
511
688
|
await this.#created;
|
|
512
689
|
return (await this.client._invoke(this.handleId, "stats", []));
|
|
@@ -560,4 +737,3 @@ export class ClientLiveSubscription {
|
|
|
560
737
|
}
|
|
561
738
|
}
|
|
562
739
|
}
|
|
563
|
-
//# sourceMappingURL=client.js.map
|