@minnowdb/core 0.7.0 → 0.7.1
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/dist/block-format/block.js +254 -325
- package/dist/block-format/checksum.js +30 -52
- package/dist/block-format/codecs.js +112 -125
- package/dist/block-format/column.js +187 -205
- package/dist/block-format/index.js +5 -1
- package/dist/block-format/physical.js +371 -387
- package/dist/block-format/types.js +4 -1
- package/dist/block-format/unicode.js +30 -39
- package/dist/date-value.js +67 -78
- package/dist/engine/artifact-cache.js +106 -116
- package/dist/engine/batch.js +36 -44
- package/dist/engine/buffered-writer.js +156 -158
- package/dist/engine/byte-estimates.js +20 -22
- package/dist/engine/cache-limits.js +24 -25
- package/dist/engine/cancellation.js +5 -3
- package/dist/engine/catalog.js +64 -73
- package/dist/engine/client.js +715 -780
- package/dist/engine/database.js +15941 -19313
- package/dist/engine/defaults.js +104 -111
- package/dist/engine/errors.js +113 -128
- package/dist/engine/fts.js +233 -280
- package/dist/engine/group-index.js +287 -321
- package/dist/engine/index.js +23 -12
- package/dist/engine/join-index.js +177 -186
- package/dist/engine/keyed-live.js +168 -173
- package/dist/engine/live-api.js +0 -1
- package/dist/engine/live-equal.js +32 -37
- package/dist/engine/live.js +603 -647
- package/dist/engine/memory.js +109 -111
- package/dist/engine/optimizer.d.ts +19 -1
- package/dist/engine/optimizer.js +2633 -2934
- package/dist/engine/point-read.js +122 -182
- package/dist/engine/query-api.js +12 -3
- package/dist/engine/query-cache.js +61 -69
- package/dist/engine/query.d.ts +6 -0
- package/dist/engine/query.js +8503 -9980
- package/dist/engine/result-wire.js +221 -252
- package/dist/engine/schema-wire.js +97 -112
- package/dist/engine/schema.js +1099 -1254
- package/dist/engine/sort-keys.js +315 -389
- package/dist/engine/sql-domains.js +646 -769
- package/dist/engine/sql-driver.js +0 -1
- package/dist/engine/sql-functions.js +1031 -1125
- package/dist/engine/sql-json.js +199 -218
- package/dist/engine/sql-semantics.js +572 -639
- package/dist/engine/typed-live.js +271 -293
- package/dist/engine/vector.js +4484 -5145
- package/dist/engine/worker-host.d.ts +8 -63
- package/dist/engine/worker-host.js +28 -1130
- package/dist/engine/worker-indexeddb.d.ts +1 -0
- package/dist/engine/worker-indexeddb.js +3 -0
- package/dist/engine/worker-memory.d.ts +1 -0
- package/dist/engine/worker-memory.js +3 -0
- package/dist/engine/worker-opfs.d.ts +1 -0
- package/dist/engine/worker-opfs.js +3 -0
- package/dist/engine/worker-server.d.ts +93 -0
- package/dist/engine/worker-server.js +999 -0
- package/dist/engine/worker-store-indexeddb.d.ts +2 -0
- package/dist/engine/worker-store-indexeddb.js +11 -0
- package/dist/engine/worker-store-memory.d.ts +2 -0
- package/dist/engine/worker-store-memory.js +6 -0
- package/dist/engine/worker-store-opfs.d.ts +2 -0
- package/dist/engine/worker-store-opfs.js +9 -0
- package/dist/engine/worker.js +0 -12
- package/dist/engine/write-block-planner.js +93 -100
- package/dist/plan/index.js +15 -16
- package/dist/plan/model.d.ts +6 -0
- package/dist/plan/model.js +20 -27
- package/dist/storage/index.js +0 -12
- package/dist/storage/indexeddb.js +11776 -13171
- package/dist/storage/memory.js +1198 -1241
- package/dist/storage/opfs/files.js +238 -295
- package/dist/storage/opfs/index.js +9 -3
- package/dist/storage/opfs/leader.js +4386 -4856
- package/dist/storage/opfs/rpc.js +223 -259
- package/dist/storage/opfs/snapshot-ledger.js +219 -260
- package/dist/storage/opfs/store.js +884 -1003
- package/dist/storage/persistence.js +59 -74
- package/dist/storage/snapshot-stream.js +788 -826
- package/dist/storage/snapshot.js +36 -37
- package/dist/storage/toolkit/extents.js +444 -520
- package/dist/storage/toolkit/index.js +28 -29
- package/dist/storage/toolkit/record-core.js +5792 -6377
- package/dist/storage/toolkit/sync-file.js +34 -42
- package/dist/storage/toolkit/wal.js +92 -127
- package/dist/storage/toolkit/wire.js +232 -256
- package/dist/storage/types.js +2948 -3250
- package/dist/testing/block-store-conformance.js +1556 -1629
- package/dist/testing/index.js +310 -291
- package/dist/testing/opfs-shim.js +280 -312
- package/dist/testing/simulator.js +496 -534
- package/dist/testing/sqllogictest.js +425 -365
- package/dist/transactions/index.js +1464 -1712
- package/dist/worker-protocol/index.js +70 -69
- package/package.json +20 -2
package/dist/engine/client.js
CHANGED
|
@@ -1,811 +1,746 @@
|
|
|
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
|
|
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
2
|
import { MAX_SNAPSHOT_STREAM_CHUNK_BYTES } from "../storage/snapshot.js";
|
|
3
|
-
import { parseRpcResponse, MAX_DATABASE_RPC_IN_FLIGHT, protocolVersion
|
|
3
|
+
import { parseRpcResponse, MAX_DATABASE_RPC_IN_FLIGHT, protocolVersion } from "../worker-protocol/index.js";
|
|
4
4
|
import { toColumnarBatch } from "./batch.js";
|
|
5
|
-
import { CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, DatabaseReadBacklogError, LiveQueryLimitError, MissingKeyError, SqlCompileError, UnknownTableError, UniqueConstraintError, VisibleSegmentCursorStaleError
|
|
5
|
+
import { CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, DatabaseReadBacklogError, LiveQueryLimitError, MissingKeyError, SqlCompileError, UnknownTableError, UniqueConstraintError, VisibleSegmentCursorStaleError } from "./errors.js";
|
|
6
6
|
import { QueryMemoryBudgetError } from "./memory.js";
|
|
7
7
|
import { decodeQueryResult } from "./result-wire.js";
|
|
8
8
|
import { serializeSchema } from "./schema-wire.js";
|
|
9
|
-
/**
|
|
10
|
-
* Bytes per slice when a snapshot crosses the channel. Large enough that a hundred-megabyte
|
|
11
|
-
* database is tens of round trips, small enough that no single structured clone is long enough
|
|
12
|
-
* to be felt as a dropped frame.
|
|
13
|
-
*/
|
|
14
9
|
const SNAPSHOT_CHUNK_BYTES = MAX_SNAPSHOT_STREAM_CHUNK_BYTES;
|
|
15
|
-
const MAX_SNAPSHOT_BYTE_ARRAY_LENGTH =
|
|
10
|
+
const MAX_SNAPSHOT_BYTE_ARRAY_LENGTH = 4294967295;
|
|
16
11
|
async function* clientSnapshotChunks(bytes) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
for (let offset = 0; offset < bytes.byteLength; offset += SNAPSHOT_CHUNK_BYTES) {
|
|
13
|
+
yield bytes.subarray(offset, offset + SNAPSHOT_CHUNK_BYTES);
|
|
14
|
+
}
|
|
20
15
|
}
|
|
21
16
|
function throwIfClientSnapshotAborted(signal) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
if (signal?.aborted !== true)
|
|
18
|
+
return;
|
|
19
|
+
if (signal.reason instanceof Error)
|
|
20
|
+
throw signal.reason;
|
|
21
|
+
const error = new Error("Snapshot operation was aborted", { cause: signal.reason });
|
|
22
|
+
error.name = "AbortError";
|
|
23
|
+
throw error;
|
|
29
24
|
}
|
|
30
25
|
const errorRegistry = new Map([
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
26
|
+
UniqueConstraintError,
|
|
27
|
+
MissingKeyError,
|
|
28
|
+
UnknownTableError,
|
|
29
|
+
CompactionBacklogError,
|
|
30
|
+
CompactionMemoryBudgetError,
|
|
31
|
+
CompactionWriteAmplificationError,
|
|
32
|
+
CompactionJobCancelledError,
|
|
33
|
+
MaintenanceBacklogError,
|
|
34
|
+
DatabaseReadBacklogError,
|
|
35
|
+
LiveQueryLimitError,
|
|
36
|
+
SqlCompileError,
|
|
37
|
+
QueryMemoryBudgetError,
|
|
38
|
+
VisibleSegmentCursorStaleError,
|
|
39
|
+
BlockReadBatchTooLargeError,
|
|
40
|
+
WriteConflictError,
|
|
41
|
+
SchemaConflictError,
|
|
42
|
+
UniqueKeyConflictError,
|
|
43
|
+
UniqueIndexCoverageError,
|
|
44
|
+
TableRecordConflictError,
|
|
45
|
+
CompactionJobConflictError,
|
|
46
|
+
GarbageCollectionJobConflictError,
|
|
47
|
+
IndexedDbSchemaUpgradeBlockedError,
|
|
48
|
+
SnapshotManifestMissingError,
|
|
49
|
+
SnapshotImportConflictError,
|
|
50
|
+
TableInUseError,
|
|
51
|
+
TransactionRecordConflictError,
|
|
52
|
+
LeaseConflictError,
|
|
53
|
+
LeaseExpiredError,
|
|
54
|
+
LeaseOwnerConflictError,
|
|
55
|
+
StorageResourceLimitError,
|
|
56
|
+
TempOwnerConflictError,
|
|
57
|
+
UniqueKeyBuildConflictError,
|
|
58
|
+
PostingBuildConflictError,
|
|
59
|
+
StorageCorruptionError,
|
|
60
|
+
StorageFormatVersionError,
|
|
61
|
+
OpfsUncertainOutcomeError
|
|
67
62
|
].map((constructor) => [constructor.name, constructor]));
|
|
68
63
|
function rehydrateError(serialized) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
64
|
+
const constructor = errorRegistry.get(serialized.name);
|
|
65
|
+
const error = constructor === void 0 ? new Error(serialized.message) : Object.create(constructor.prototype);
|
|
66
|
+
Object.defineProperty(error, "message", {
|
|
67
|
+
value: serialized.message,
|
|
68
|
+
writable: true,
|
|
69
|
+
configurable: true
|
|
70
|
+
});
|
|
71
|
+
Object.defineProperty(error, "name", {
|
|
72
|
+
value: serialized.name,
|
|
73
|
+
writable: true,
|
|
74
|
+
configurable: true
|
|
75
|
+
});
|
|
76
|
+
if (serialized.stack !== void 0) {
|
|
77
|
+
Object.defineProperty(error, "stack", {
|
|
78
|
+
value: serialized.stack,
|
|
79
|
+
writable: true,
|
|
80
|
+
configurable: true
|
|
82
81
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
configurable: true,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
if (serialized.props !== undefined)
|
|
91
|
-
Object.assign(error, serialized.props);
|
|
92
|
-
return error;
|
|
82
|
+
}
|
|
83
|
+
if (serialized.props !== void 0)
|
|
84
|
+
Object.assign(error, serialized.props);
|
|
85
|
+
return error;
|
|
93
86
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
87
|
+
function rehydrateResponseError(payload) {
|
|
88
|
+
const candidate = payload;
|
|
89
|
+
if (typeof candidate !== "object" || candidate === null || typeof candidate.name !== "string" || typeof candidate.message !== "string") {
|
|
90
|
+
return new Error("The database worker reported a failure without a readable error payload");
|
|
91
|
+
}
|
|
92
|
+
return rehydrateError(candidate);
|
|
93
|
+
}
|
|
94
|
+
class MinnowDatabaseClient {
|
|
95
|
+
#transport;
|
|
96
|
+
#pending = /* @__PURE__ */ new Map();
|
|
97
|
+
#events = /* @__PURE__ */ new Map();
|
|
98
|
+
#ready;
|
|
99
|
+
#fatal;
|
|
100
|
+
#closed = false;
|
|
101
|
+
#onVisibilityChange;
|
|
102
|
+
#onMessage = (event) => {
|
|
103
|
+
this.#receive(event.data);
|
|
104
|
+
};
|
|
105
|
+
#onError = () => {
|
|
106
|
+
this.#fail(new Error("The database worker failed; see the worker's own error output"));
|
|
107
|
+
};
|
|
108
|
+
#onMessageError = () => {
|
|
109
|
+
this.#fail(new Error("A database worker message could not be deserialized"));
|
|
110
|
+
};
|
|
111
|
+
constructor(transport, options = {}) {
|
|
112
|
+
this.#transport = transport;
|
|
113
|
+
transport.addEventListener("message", this.#onMessage);
|
|
114
|
+
transport.addEventListener("error", this.#onError);
|
|
115
|
+
transport.addEventListener("messageerror", this.#onMessageError);
|
|
116
|
+
const payload = {
|
|
117
|
+
store: options.store ?? { kind: "indexeddb", name: "minnow" },
|
|
118
|
+
...options.databaseOptions === void 0 ? {} : { options: options.databaseOptions }
|
|
110
119
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
120
|
+
this.#ready = this.#post("rpc-init", null, "init", [payload]).then(() => void 0);
|
|
121
|
+
this.#ready.catch(() => void 0);
|
|
122
|
+
if (typeof document !== "undefined" && typeof document.addEventListener === "function") {
|
|
123
|
+
const report = () => {
|
|
124
|
+
if (this.#closed)
|
|
125
|
+
return;
|
|
126
|
+
this.#post("rpc-call", null, "setVisibility", [
|
|
127
|
+
document.visibilityState === "visible"
|
|
128
|
+
]).catch(() => void 0);
|
|
129
|
+
};
|
|
130
|
+
this.#onVisibilityChange = report;
|
|
131
|
+
document.addEventListener("visibilitychange", report);
|
|
132
|
+
report();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async ready() {
|
|
136
|
+
return this.#ready;
|
|
137
|
+
}
|
|
138
|
+
async createTable(input) {
|
|
139
|
+
await this.#call("createTable", [input]);
|
|
140
|
+
}
|
|
141
|
+
async createView(name, sql, options = {}) {
|
|
142
|
+
await this.#call("createView", [name, sql, options]);
|
|
143
|
+
}
|
|
144
|
+
async dropView(name, options = {}) {
|
|
145
|
+
return await this.#call("dropView", [name, options]);
|
|
146
|
+
}
|
|
147
|
+
async dropColumn(tableName, columnName, options = {}) {
|
|
148
|
+
return await this.#call("dropColumn", [tableName, columnName, options]);
|
|
149
|
+
}
|
|
150
|
+
async dropTable(tableName, options = {}) {
|
|
151
|
+
return await this.#call("dropTable", [tableName, options]);
|
|
152
|
+
}
|
|
153
|
+
async createIndex(indexName, tableName, requestedColumns, options = {}) {
|
|
154
|
+
await this.#call("createIndex", [indexName, tableName, requestedColumns, options]);
|
|
155
|
+
}
|
|
156
|
+
async dropIndex(indexName, options = {}) {
|
|
157
|
+
return await this.#call("dropIndex", [indexName, options]);
|
|
158
|
+
}
|
|
159
|
+
async buildFtsIndex(tableName, columnName) {
|
|
160
|
+
await this.#call("buildFtsIndex", [tableName, columnName]);
|
|
161
|
+
}
|
|
162
|
+
async introspect() {
|
|
163
|
+
return await this.#call("introspect", []);
|
|
164
|
+
}
|
|
165
|
+
async listTables() {
|
|
166
|
+
return await this.#call("listTables", []);
|
|
167
|
+
}
|
|
168
|
+
async migrate(definition, options = {}) {
|
|
169
|
+
return await this.#call("migrate", [
|
|
170
|
+
serializeSchema(definition),
|
|
171
|
+
options
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
async insertBatch(tableName, input) {
|
|
175
|
+
const batch = toColumnarBatch(input);
|
|
176
|
+
return await this.#call("insertBatch", [tableName, batch]);
|
|
177
|
+
}
|
|
178
|
+
async insert(tableName, row) {
|
|
179
|
+
return await this.#call("insert", [tableName, row]);
|
|
180
|
+
}
|
|
181
|
+
async upsertBatch(tableName, input, options = {}) {
|
|
182
|
+
const batch = toColumnarBatch(input);
|
|
183
|
+
return await this.#call("upsertBatch", [tableName, batch, options]);
|
|
184
|
+
}
|
|
185
|
+
async upsert(tableName, row, options = {}) {
|
|
186
|
+
return await this.#call("upsert", [tableName, row, options]);
|
|
187
|
+
}
|
|
188
|
+
async updateBatch(tableName, input) {
|
|
189
|
+
return await this.#call("updateBatch", [tableName, input]);
|
|
190
|
+
}
|
|
191
|
+
async update(tableName, key, changes) {
|
|
192
|
+
return await this.#call("update", [tableName, key, changes]);
|
|
193
|
+
}
|
|
194
|
+
async deleteBatch(tableName, input) {
|
|
195
|
+
return await this.#call("deleteBatch", [tableName, input]);
|
|
196
|
+
}
|
|
197
|
+
async delete(tableName, key) {
|
|
198
|
+
return await this.#call("delete", [tableName, key]);
|
|
199
|
+
}
|
|
200
|
+
bufferedWriter(tableName, options = {}) {
|
|
201
|
+
const handleId = crypto.randomUUID();
|
|
202
|
+
const { onError, ...wireOptions } = options;
|
|
203
|
+
this.#events.set(handleId, {
|
|
204
|
+
...onError === void 0 ? {} : { onError }
|
|
205
|
+
});
|
|
206
|
+
const created = this.#call("bufferedWriter", [handleId, tableName, wireOptions]);
|
|
207
|
+
return new ClientBufferedWriter(this, handleId, created);
|
|
208
|
+
}
|
|
209
|
+
async readTable(tableName, versionOrOptions) {
|
|
210
|
+
return decodeQueryResult(await this.#call("readTable", versionOrOptions === void 0 ? [tableName] : [tableName, versionOrOptions])).rows;
|
|
211
|
+
}
|
|
212
|
+
async query(sql, options) {
|
|
213
|
+
const { signal, onStats, ...wireOptions } = options ?? {};
|
|
214
|
+
return decodeQueryResult(await this.#call("query", [sql, wireOptions, onStats !== void 0], { signal, onStats }));
|
|
215
|
+
}
|
|
216
|
+
queryCursor(sql, options = {}) {
|
|
217
|
+
const call = this.#call.bind(this);
|
|
218
|
+
const invoke = this._invoke.bind(this);
|
|
219
|
+
const routeEvents = this._routeEvents.bind(this);
|
|
220
|
+
const unrouteEvents = this._unrouteEvents.bind(this);
|
|
221
|
+
const handleId = crypto.randomUUID();
|
|
222
|
+
const { signal, onStats, ...wireOptions } = options;
|
|
223
|
+
async function* batches() {
|
|
224
|
+
let opened = false;
|
|
225
|
+
let closing;
|
|
226
|
+
const close = () => {
|
|
227
|
+
if (!opened)
|
|
228
|
+
return Promise.resolve();
|
|
229
|
+
closing ??= invoke(handleId, "close", []).catch(() => void 0);
|
|
230
|
+
return closing;
|
|
231
|
+
};
|
|
232
|
+
const onAbort = () => {
|
|
233
|
+
void close();
|
|
234
|
+
};
|
|
235
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
236
|
+
if (onStats !== void 0)
|
|
237
|
+
routeEvents(handleId, { onStats });
|
|
238
|
+
try {
|
|
239
|
+
signal?.throwIfAborted();
|
|
240
|
+
await call("queryCursorOpen", [handleId, sql, wireOptions, onStats !== void 0]);
|
|
241
|
+
opened = true;
|
|
242
|
+
if (signal?.aborted === true) {
|
|
243
|
+
await close();
|
|
244
|
+
signal.throwIfAborted();
|
|
137
245
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// --- Catalog and writes -----------------------------------------------------------------------
|
|
144
|
-
async createTable(input) {
|
|
145
|
-
await this.#call("createTable", [input]);
|
|
146
|
-
}
|
|
147
|
-
async createView(name, sql, options = {}) {
|
|
148
|
-
await this.#call("createView", [name, sql, options]);
|
|
149
|
-
}
|
|
150
|
-
async dropView(name, options = {}) {
|
|
151
|
-
return (await this.#call("dropView", [name, options]));
|
|
152
|
-
}
|
|
153
|
-
async dropColumn(tableName, columnName, options = {}) {
|
|
154
|
-
return (await this.#call("dropColumn", [tableName, columnName, options]));
|
|
155
|
-
}
|
|
156
|
-
async dropTable(tableName, options = {}) {
|
|
157
|
-
return (await this.#call("dropTable", [tableName, options]));
|
|
158
|
-
}
|
|
159
|
-
async createIndex(indexName, tableName, requestedColumns, options = {}) {
|
|
160
|
-
await this.#call("createIndex", [indexName, tableName, requestedColumns, options]);
|
|
161
|
-
}
|
|
162
|
-
async dropIndex(indexName, options = {}) {
|
|
163
|
-
return (await this.#call("dropIndex", [indexName, options]));
|
|
164
|
-
}
|
|
165
|
-
async buildFtsIndex(tableName, columnName) {
|
|
166
|
-
await this.#call("buildFtsIndex", [tableName, columnName]);
|
|
167
|
-
}
|
|
168
|
-
/** The published catalog; see `MinnowDatabase.introspect()`. */
|
|
169
|
-
async introspect() {
|
|
170
|
-
return (await this.#call("introspect", []));
|
|
171
|
-
}
|
|
172
|
-
async listTables() {
|
|
173
|
-
return (await this.#call("listTables", []));
|
|
174
|
-
}
|
|
175
|
-
async migrate(definition, options = {}) {
|
|
176
|
-
return (await this.#call("migrate", [
|
|
177
|
-
serializeSchema(definition),
|
|
178
|
-
options,
|
|
179
|
-
]));
|
|
180
|
-
}
|
|
181
|
-
async insertBatch(tableName, input) {
|
|
182
|
-
// Pivoted here rather than in the worker: the columnar form is the cheaper structured clone,
|
|
183
|
-
// and it keeps the worker protocol's payload shape the same whichever form the caller used.
|
|
184
|
-
const batch = toColumnarBatch(input);
|
|
185
|
-
return (await this.#call("insertBatch", [tableName, batch]));
|
|
186
|
-
}
|
|
187
|
-
async insert(tableName, row) {
|
|
188
|
-
return (await this.#call("insert", [tableName, row]));
|
|
189
|
-
}
|
|
190
|
-
async upsertBatch(tableName, input, options = {}) {
|
|
191
|
-
const batch = toColumnarBatch(input);
|
|
192
|
-
return (await this.#call("upsertBatch", [tableName, batch, options]));
|
|
193
|
-
}
|
|
194
|
-
async upsert(tableName, row, options = {}) {
|
|
195
|
-
return (await this.#call("upsert", [tableName, row, options]));
|
|
196
|
-
}
|
|
197
|
-
async updateBatch(tableName, input) {
|
|
198
|
-
return (await this.#call("updateBatch", [tableName, input]));
|
|
199
|
-
}
|
|
200
|
-
async update(tableName, key, changes) {
|
|
201
|
-
return (await this.#call("update", [tableName, key, changes]));
|
|
202
|
-
}
|
|
203
|
-
async deleteBatch(tableName, input) {
|
|
204
|
-
return (await this.#call("deleteBatch", [tableName, input]));
|
|
205
|
-
}
|
|
206
|
-
async delete(tableName, key) {
|
|
207
|
-
return (await this.#call("delete", [tableName, key]));
|
|
208
|
-
}
|
|
209
|
-
bufferedWriter(tableName, options = {}) {
|
|
210
|
-
const handleId = crypto.randomUUID();
|
|
211
|
-
const { onError, ...wireOptions } = options;
|
|
212
|
-
this.#events.set(handleId, {
|
|
213
|
-
...(onError === undefined ? {} : { onError }),
|
|
214
|
-
});
|
|
215
|
-
const created = this.#call("bufferedWriter", [handleId, tableName, wireOptions]);
|
|
216
|
-
return new ClientBufferedWriter(this, handleId, created);
|
|
217
|
-
}
|
|
218
|
-
// --- Reads ------------------------------------------------------------------------------------
|
|
219
|
-
async readTable(tableName, versionOrOptions) {
|
|
220
|
-
return decodeQueryResult(await this.#call("readTable", versionOrOptions === undefined ? [tableName] : [tableName, versionOrOptions])).rows;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Results cross the channel as one array per column (typed arrays for numbers, booleans, and
|
|
224
|
-
* datetimes) and are rebuilt into row objects here; see `result-wire.ts`.
|
|
225
|
-
*/
|
|
226
|
-
async query(sql, options) {
|
|
227
|
-
const { signal, onStats, ...wireOptions } = options ?? {};
|
|
228
|
-
return decodeQueryResult(await this.#call("query", [sql, wireOptions, onStats !== undefined], { signal, onStats }));
|
|
229
|
-
}
|
|
230
|
-
/** Pulls one transferred columnar page at a time, preserving worker backpressure. */
|
|
231
|
-
queryCursor(sql, options = {}) {
|
|
232
|
-
const call = this.#call.bind(this);
|
|
233
|
-
const invoke = this._invoke.bind(this);
|
|
234
|
-
const routeEvents = this._routeEvents.bind(this);
|
|
235
|
-
const unrouteEvents = this._unrouteEvents.bind(this);
|
|
236
|
-
const handleId = crypto.randomUUID();
|
|
237
|
-
const { signal, onStats, ...wireOptions } = options;
|
|
238
|
-
async function* batches() {
|
|
239
|
-
let opened = false;
|
|
240
|
-
let closing;
|
|
241
|
-
const close = () => {
|
|
242
|
-
if (!opened)
|
|
243
|
-
return Promise.resolve();
|
|
244
|
-
closing ??= invoke(handleId, "close", []).catch(() => undefined);
|
|
245
|
-
return closing;
|
|
246
|
-
};
|
|
247
|
-
const onAbort = () => {
|
|
248
|
-
void close();
|
|
249
|
-
};
|
|
250
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
251
|
-
if (onStats !== undefined)
|
|
252
|
-
routeEvents(handleId, { onStats });
|
|
253
|
-
try {
|
|
254
|
-
signal?.throwIfAborted();
|
|
255
|
-
await call("queryCursorOpen", [handleId, sql, wireOptions, onStats !== undefined]);
|
|
256
|
-
opened = true;
|
|
257
|
-
if (signal?.aborted === true) {
|
|
258
|
-
await close();
|
|
259
|
-
signal.throwIfAborted();
|
|
260
|
-
}
|
|
261
|
-
for (;;) {
|
|
262
|
-
const next = (await invoke(handleId, "next", []));
|
|
263
|
-
if (next.done)
|
|
264
|
-
return undefined;
|
|
265
|
-
yield decodeQueryResult(next.batch);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
finally {
|
|
269
|
-
signal?.removeEventListener("abort", onAbort);
|
|
270
|
-
await close();
|
|
271
|
-
unrouteEvents(handleId);
|
|
272
|
-
}
|
|
246
|
+
for (; ; ) {
|
|
247
|
+
const next = await invoke(handleId, "next", []);
|
|
248
|
+
if (next.done)
|
|
249
|
+
return void 0;
|
|
250
|
+
yield decodeQueryResult(next.batch);
|
|
273
251
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
252
|
+
} finally {
|
|
253
|
+
signal?.removeEventListener("abort", onAbort);
|
|
254
|
+
await close();
|
|
255
|
+
unrouteEvents(handleId);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return batches();
|
|
259
|
+
}
|
|
260
|
+
async run(query) {
|
|
261
|
+
return decodeQueryResult(await this.#call("run", [query])).rows;
|
|
262
|
+
}
|
|
263
|
+
async explain(sql) {
|
|
264
|
+
return await this.#call("explain", [sql]);
|
|
265
|
+
}
|
|
266
|
+
async execute(sql, params, options = {}) {
|
|
267
|
+
const { signal, onStats, ...wireOptions } = options;
|
|
268
|
+
return await this.#call("execute", [sql, params, wireOptions, onStats !== void 0], {
|
|
269
|
+
signal,
|
|
270
|
+
onStats
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
async runStatement(statement, options) {
|
|
274
|
+
return await this.#call("runStatement", options === void 0 ? [statement] : [statement, options]);
|
|
275
|
+
}
|
|
276
|
+
async snapshot(action) {
|
|
277
|
+
const opened = await this.#call("snapshotOpen", []);
|
|
278
|
+
try {
|
|
279
|
+
const session = {
|
|
280
|
+
version: opened.version,
|
|
281
|
+
query: (sql, options = {}) => this.query(sql, {
|
|
282
|
+
...options,
|
|
283
|
+
...opened.version === null ? {} : { version: opened.version }
|
|
284
|
+
})
|
|
285
|
+
};
|
|
286
|
+
return await action(session);
|
|
287
|
+
} finally {
|
|
288
|
+
await this._invoke(opened.handleId, "close", []);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async write(action) {
|
|
292
|
+
const opened = await this.#call("writeOpen", []);
|
|
293
|
+
const stage = (op, tableName, input) => this._invoke(opened.handleId, "stage", [op, tableName, input]);
|
|
294
|
+
const session = {
|
|
295
|
+
query: async (sql, options = {}) => {
|
|
283
296
|
const { signal, onStats, ...wireOptions } = options;
|
|
284
|
-
return (await this
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
},
|
|
328
|
-
execute: (sql, params) => this._invoke(opened.handleId, "execute", params === undefined ? [sql] : [sql, params]),
|
|
329
|
-
insertBatch: (tableName, input) => stage("insertBatch", tableName, input),
|
|
330
|
-
upsertBatch: (tableName, input) => stage("upsertBatch", tableName, input),
|
|
331
|
-
updateBatch: (tableName, input) => stage("updateBatch", tableName, input),
|
|
332
|
-
deleteBatch: (tableName, input) => stage("deleteBatch", tableName, input),
|
|
333
|
-
};
|
|
334
|
-
try {
|
|
335
|
-
const result = await action(session);
|
|
336
|
-
const committed = (await this._invoke(opened.handleId, "commit", []));
|
|
337
|
-
return { result, version: committed.version };
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
await this._invoke(opened.handleId, "abort", []).catch(() => undefined);
|
|
341
|
-
throw error;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Copies the database out as a portable snapshot file, exactly like the in-worker
|
|
346
|
-
* `MinnowDatabase.exportSnapshot()`. The worker encodes it and hands it back in slices, so the
|
|
347
|
-
* main thread copies a few megabytes at a time instead of stalling on one clone of the whole
|
|
348
|
-
* database; `onProgress` reports each slice as it lands.
|
|
349
|
-
*/
|
|
350
|
-
async exportSnapshot(options = {}) {
|
|
351
|
-
const chunks = [];
|
|
352
|
-
let byteLength = 0;
|
|
353
|
-
for await (const chunk of this.exportSnapshotStream(options)) {
|
|
354
|
-
byteLength += chunk.byteLength;
|
|
355
|
-
if (!Number.isSafeInteger(byteLength) || byteLength > MAX_SNAPSHOT_BYTE_ARRAY_LENGTH) {
|
|
356
|
-
throw new RangeError("Snapshot exceeds the byte-array API's 4 GiB limit");
|
|
357
|
-
}
|
|
358
|
-
chunks.push(chunk);
|
|
359
|
-
}
|
|
360
|
-
const bytes = new Uint8Array(byteLength);
|
|
361
|
-
let offset = 0;
|
|
362
|
-
for (const chunk of chunks) {
|
|
363
|
-
bytes.set(chunk, offset);
|
|
364
|
-
offset += chunk.byteLength;
|
|
365
|
-
}
|
|
366
|
-
return bytes;
|
|
367
|
-
}
|
|
368
|
-
/** Pulls at most one bounded worker chunk at a time; closing releases the worker-side pin. */
|
|
369
|
-
async *exportSnapshotStream(options = {}) {
|
|
297
|
+
return decodeQueryResult(await this._invokeControlled(opened.handleId, "query", [sql, wireOptions, onStats !== void 0], { signal, onStats }));
|
|
298
|
+
},
|
|
299
|
+
execute: (sql, params) => this._invoke(opened.handleId, "execute", params === void 0 ? [sql] : [sql, params]),
|
|
300
|
+
insertBatch: (tableName, input) => stage("insertBatch", tableName, input),
|
|
301
|
+
upsertBatch: (tableName, input) => stage("upsertBatch", tableName, input),
|
|
302
|
+
updateBatch: (tableName, input) => stage("updateBatch", tableName, input),
|
|
303
|
+
deleteBatch: (tableName, input) => stage("deleteBatch", tableName, input)
|
|
304
|
+
};
|
|
305
|
+
try {
|
|
306
|
+
const result = await action(session);
|
|
307
|
+
const committed = await this._invoke(opened.handleId, "commit", []);
|
|
308
|
+
return { result, version: committed.version };
|
|
309
|
+
} catch (error) {
|
|
310
|
+
await this._invoke(opened.handleId, "abort", []).catch(() => void 0);
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async exportSnapshot(options = {}) {
|
|
315
|
+
const chunks = [];
|
|
316
|
+
let byteLength = 0;
|
|
317
|
+
for await (const chunk of this.exportSnapshotStream(options)) {
|
|
318
|
+
byteLength += chunk.byteLength;
|
|
319
|
+
if (!Number.isSafeInteger(byteLength) || byteLength > MAX_SNAPSHOT_BYTE_ARRAY_LENGTH) {
|
|
320
|
+
throw new RangeError("Snapshot exceeds the byte-array API's 4 GiB limit");
|
|
321
|
+
}
|
|
322
|
+
chunks.push(chunk);
|
|
323
|
+
}
|
|
324
|
+
const bytes = new Uint8Array(byteLength);
|
|
325
|
+
let offset = 0;
|
|
326
|
+
for (const chunk of chunks) {
|
|
327
|
+
bytes.set(chunk, offset);
|
|
328
|
+
offset += chunk.byteLength;
|
|
329
|
+
}
|
|
330
|
+
return bytes;
|
|
331
|
+
}
|
|
332
|
+
async *exportSnapshotStream(options = {}) {
|
|
333
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
334
|
+
options.onProgress?.({ phase: "reading", transferredBytes: 0, totalBytes: 0 });
|
|
335
|
+
const opened = await this.#call("exportSnapshotOpen", []);
|
|
336
|
+
let transferredBytes = 0;
|
|
337
|
+
let succeeded = false;
|
|
338
|
+
try {
|
|
339
|
+
for (; ; ) {
|
|
370
340
|
throwIfClientSnapshotAborted(options.signal);
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
throwIfClientSnapshotAborted(options.signal);
|
|
378
|
-
const next = (await this._invoke(opened.handleId, "read", []));
|
|
379
|
-
if (next.done)
|
|
380
|
-
break;
|
|
381
|
-
throwIfClientSnapshotAborted(options.signal);
|
|
382
|
-
if (!(next.chunk instanceof Uint8Array) || next.chunk.byteLength === 0) {
|
|
383
|
-
throw new Error("Snapshot transfer returned an invalid chunk");
|
|
384
|
-
}
|
|
385
|
-
yield next.chunk;
|
|
386
|
-
transferredBytes += next.chunk.byteLength;
|
|
387
|
-
options.onProgress?.({ phase: "transfer", transferredBytes, totalBytes: 0 });
|
|
388
|
-
}
|
|
389
|
-
succeeded = true;
|
|
390
|
-
}
|
|
391
|
-
finally {
|
|
392
|
-
await this._invoke(opened.handleId, "close", []).catch(() => undefined);
|
|
393
|
-
if (succeeded) {
|
|
394
|
-
options.onProgress?.({
|
|
395
|
-
phase: "done",
|
|
396
|
-
transferredBytes,
|
|
397
|
-
totalBytes: transferredBytes,
|
|
398
|
-
});
|
|
399
|
-
}
|
|
341
|
+
const next = await this._invoke(opened.handleId, "read", []);
|
|
342
|
+
if (next.done)
|
|
343
|
+
break;
|
|
344
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
345
|
+
if (!(next.chunk instanceof Uint8Array) || next.chunk.byteLength === 0) {
|
|
346
|
+
throw new Error("Snapshot transfer returned an invalid chunk");
|
|
400
347
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const { onProgress } = options;
|
|
414
|
-
this.#events.set(handleId, {
|
|
415
|
-
...(onProgress === undefined
|
|
416
|
-
? {}
|
|
417
|
-
: {
|
|
418
|
-
onProgress: (progress) => {
|
|
419
|
-
onProgress(progress);
|
|
420
|
-
},
|
|
421
|
-
}),
|
|
348
|
+
yield next.chunk;
|
|
349
|
+
transferredBytes += next.chunk.byteLength;
|
|
350
|
+
options.onProgress?.({ phase: "transfer", transferredBytes, totalBytes: 0 });
|
|
351
|
+
}
|
|
352
|
+
succeeded = true;
|
|
353
|
+
} finally {
|
|
354
|
+
await this._invoke(opened.handleId, "close", []).catch(() => void 0);
|
|
355
|
+
if (succeeded) {
|
|
356
|
+
options.onProgress?.({
|
|
357
|
+
phase: "done",
|
|
358
|
+
transferredBytes,
|
|
359
|
+
totalBytes: transferredBytes
|
|
422
360
|
});
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
throwIfClientSnapshotAborted(options.signal);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
await this._invoke(handleId, "finish", []);
|
|
440
|
-
}
|
|
441
|
-
catch (error) {
|
|
442
|
-
await this._invoke(handleId, options.signal?.aborted === true ? "cancel" : "close", []).catch(() => undefined);
|
|
443
|
-
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async importSnapshot(bytes, options = {}) {
|
|
365
|
+
await this.importSnapshotStream(clientSnapshotChunks(bytes), options);
|
|
366
|
+
}
|
|
367
|
+
async importSnapshotStream(source, options = {}) {
|
|
368
|
+
const handleId = crypto.randomUUID();
|
|
369
|
+
const { onProgress } = options;
|
|
370
|
+
this.#events.set(handleId, {
|
|
371
|
+
...onProgress === void 0 ? {} : {
|
|
372
|
+
onProgress: (progress) => {
|
|
373
|
+
onProgress(progress);
|
|
444
374
|
}
|
|
445
|
-
|
|
446
|
-
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
try {
|
|
378
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
379
|
+
await this.#call("importSnapshotOpen", [handleId]);
|
|
380
|
+
for await (const input of source) {
|
|
381
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
382
|
+
if (!(input instanceof Uint8Array)) {
|
|
383
|
+
throw new TypeError("Snapshot stream chunks must be Uint8Array values");
|
|
447
384
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
async maintenanceStatus() {
|
|
454
|
-
return (await this.#call("maintenanceStatus", []));
|
|
455
|
-
}
|
|
456
|
-
async checkIntegrity(options = {}) {
|
|
457
|
-
return (await this.#call("checkIntegrity", [options]));
|
|
458
|
-
}
|
|
459
|
-
async storageStats() {
|
|
460
|
-
return (await this.#call("storageStats", []));
|
|
461
|
-
}
|
|
462
|
-
async inspectInterruptedImport() {
|
|
463
|
-
return (await this.#call("inspectInterruptedImport", []));
|
|
464
|
-
}
|
|
465
|
-
async abortInterruptedImport(identity) {
|
|
466
|
-
return (await this.#call("abortInterruptedImport", [
|
|
467
|
-
identity,
|
|
468
|
-
]));
|
|
469
|
-
}
|
|
470
|
-
liveQueries(options = {}) {
|
|
471
|
-
const handleId = crypto.randomUUID();
|
|
472
|
-
const created = this.#call("liveQueries", [handleId, options]);
|
|
473
|
-
return new ClientLiveQuerySet(this, handleId, created);
|
|
474
|
-
}
|
|
475
|
-
// --- Maintenance ------------------------------------------------------------------------------
|
|
476
|
-
async listVisibleSegmentPage(tableName, options) {
|
|
477
|
-
return (await this.#call("listVisibleSegmentPage", options === undefined ? [tableName] : [tableName, options]));
|
|
478
|
-
}
|
|
479
|
-
async cleanupQuerySpill(options) {
|
|
480
|
-
return (await this.#call("cleanupQuerySpill", options === undefined ? [] : [options]));
|
|
481
|
-
}
|
|
482
|
-
async compactTable(tableName, options) {
|
|
483
|
-
return (await this.#call("compactTable", options === undefined ? [tableName] : [tableName, options]));
|
|
484
|
-
}
|
|
485
|
-
async compactTableStep(tableName, options) {
|
|
486
|
-
return (await this.#call("compactTableStep", options === undefined ? [tableName] : [tableName, options]));
|
|
487
|
-
}
|
|
488
|
-
async resumeCompactionJob(jobId, options) {
|
|
489
|
-
return (await this.#call("resumeCompactionJob", options === undefined ? [jobId] : [jobId, options]));
|
|
490
|
-
}
|
|
491
|
-
async listCompactionJobs(tableName) {
|
|
492
|
-
return (await this.#call("listCompactionJobs", tableName === undefined ? [] : [tableName]));
|
|
493
|
-
}
|
|
494
|
-
async cancelCompactionJob(jobId) {
|
|
495
|
-
return (await this.#call("cancelCompactionJob", [jobId]));
|
|
496
|
-
}
|
|
497
|
-
async collectGarbage(options) {
|
|
498
|
-
return (await this.#call("collectGarbage", options === undefined ? [] : [options]));
|
|
499
|
-
}
|
|
500
|
-
async collectGarbageStep(options) {
|
|
501
|
-
return (await this.#call("collectGarbageStep", options === undefined ? [] : [options]));
|
|
502
|
-
}
|
|
503
|
-
async resumeGarbageCollectionJob(jobId, options) {
|
|
504
|
-
return (await this.#call("resumeGarbageCollectionJob", options === undefined ? [jobId] : [jobId, options]));
|
|
505
|
-
}
|
|
506
|
-
async listGarbageCollectionJobs() {
|
|
507
|
-
return (await this.#call("listGarbageCollectionJobs", []));
|
|
508
|
-
}
|
|
509
|
-
// --- Lifecycle --------------------------------------------------------------------------------
|
|
510
|
-
/** Disposes every worker-side handle, closes the store, and optionally terminates the worker. */
|
|
511
|
-
async close(options = {}) {
|
|
512
|
-
if (this.#closed)
|
|
513
|
-
return;
|
|
514
|
-
this.#closed = true;
|
|
515
|
-
if (this.#onVisibilityChange !== undefined && typeof document !== "undefined") {
|
|
516
|
-
document.removeEventListener("visibilitychange", this.#onVisibilityChange);
|
|
385
|
+
for (let offset = 0; offset < input.byteLength; offset += SNAPSHOT_CHUNK_BYTES) {
|
|
386
|
+
const chunk = input.slice(offset, offset + SNAPSHOT_CHUNK_BYTES);
|
|
387
|
+
await this.#post("rpc-call", handleId, "write", [chunk], [chunk.buffer]);
|
|
388
|
+
throwIfClientSnapshotAborted(options.signal);
|
|
517
389
|
}
|
|
390
|
+
}
|
|
391
|
+
await this._invoke(handleId, "finish", []);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
await this._invoke(handleId, options.signal?.aborted === true ? "cancel" : "close", []).catch(() => void 0);
|
|
394
|
+
throw error;
|
|
395
|
+
} finally {
|
|
396
|
+
this.#events.delete(handleId);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async bufferPoolStats() {
|
|
400
|
+
return await this.#call("bufferPoolStats", []);
|
|
401
|
+
}
|
|
402
|
+
async maintenanceStatus() {
|
|
403
|
+
return await this.#call("maintenanceStatus", []);
|
|
404
|
+
}
|
|
405
|
+
async checkIntegrity(options = {}) {
|
|
406
|
+
return await this.#call("checkIntegrity", [options]);
|
|
407
|
+
}
|
|
408
|
+
async storageStats() {
|
|
409
|
+
return await this.#call("storageStats", []);
|
|
410
|
+
}
|
|
411
|
+
async inspectInterruptedImport() {
|
|
412
|
+
return await this.#call("inspectInterruptedImport", []);
|
|
413
|
+
}
|
|
414
|
+
async abortInterruptedImport(identity) {
|
|
415
|
+
return await this.#call("abortInterruptedImport", [
|
|
416
|
+
identity
|
|
417
|
+
]);
|
|
418
|
+
}
|
|
419
|
+
liveQueries(options = {}) {
|
|
420
|
+
const handleId = crypto.randomUUID();
|
|
421
|
+
const created = this.#call("liveQueries", [handleId, options]);
|
|
422
|
+
return new ClientLiveQuerySet(this, handleId, created);
|
|
423
|
+
}
|
|
424
|
+
async listVisibleSegmentPage(tableName, options) {
|
|
425
|
+
return await this.#call("listVisibleSegmentPage", options === void 0 ? [tableName] : [tableName, options]);
|
|
426
|
+
}
|
|
427
|
+
async cleanupQuerySpill(options) {
|
|
428
|
+
return await this.#call("cleanupQuerySpill", options === void 0 ? [] : [options]);
|
|
429
|
+
}
|
|
430
|
+
async compactTable(tableName, options) {
|
|
431
|
+
return await this.#call("compactTable", options === void 0 ? [tableName] : [tableName, options]);
|
|
432
|
+
}
|
|
433
|
+
async compactTableStep(tableName, options) {
|
|
434
|
+
return await this.#call("compactTableStep", options === void 0 ? [tableName] : [tableName, options]);
|
|
435
|
+
}
|
|
436
|
+
async resumeCompactionJob(jobId, options) {
|
|
437
|
+
return await this.#call("resumeCompactionJob", options === void 0 ? [jobId] : [jobId, options]);
|
|
438
|
+
}
|
|
439
|
+
async listCompactionJobs(tableName) {
|
|
440
|
+
return await this.#call("listCompactionJobs", tableName === void 0 ? [] : [tableName]);
|
|
441
|
+
}
|
|
442
|
+
async cancelCompactionJob(jobId) {
|
|
443
|
+
return await this.#call("cancelCompactionJob", [jobId]);
|
|
444
|
+
}
|
|
445
|
+
async collectGarbage(options) {
|
|
446
|
+
return await this.#call("collectGarbage", options === void 0 ? [] : [options]);
|
|
447
|
+
}
|
|
448
|
+
async collectGarbageStep(options) {
|
|
449
|
+
return await this.#call("collectGarbageStep", options === void 0 ? [] : [options]);
|
|
450
|
+
}
|
|
451
|
+
async resumeGarbageCollectionJob(jobId, options) {
|
|
452
|
+
return await this.#call("resumeGarbageCollectionJob", options === void 0 ? [jobId] : [jobId, options]);
|
|
453
|
+
}
|
|
454
|
+
async listGarbageCollectionJobs() {
|
|
455
|
+
return await this.#call("listGarbageCollectionJobs", []);
|
|
456
|
+
}
|
|
457
|
+
async close(options = {}) {
|
|
458
|
+
if (this.#closed)
|
|
459
|
+
return;
|
|
460
|
+
this.#closed = true;
|
|
461
|
+
if (this.#onVisibilityChange !== void 0 && typeof document !== "undefined") {
|
|
462
|
+
document.removeEventListener("visibilitychange", this.#onVisibilityChange);
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
await this.#post("rpc-call", null, "dispose", []);
|
|
466
|
+
} finally {
|
|
467
|
+
this.#events.clear();
|
|
468
|
+
this.#transport.removeEventListener?.("message", this.#onMessage);
|
|
469
|
+
this.#transport.removeEventListener?.("error", this.#onError);
|
|
470
|
+
this.#transport.removeEventListener?.("messageerror", this.#onMessageError);
|
|
471
|
+
if (options.terminateWorker === true)
|
|
472
|
+
this.#transport.terminate?.();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
async _invoke(handleId, method, args) {
|
|
476
|
+
if (this.#closed)
|
|
477
|
+
throw new Error("Database client is closed");
|
|
478
|
+
return this.#post("rpc-call", handleId, method, args);
|
|
479
|
+
}
|
|
480
|
+
async _invokeControlled(handleId, method, args, controls) {
|
|
481
|
+
if (this.#closed)
|
|
482
|
+
throw new Error("Database client is closed");
|
|
483
|
+
return this.#post("rpc-call", handleId, method, args, void 0, false, controls);
|
|
484
|
+
}
|
|
485
|
+
_routeEvents(handleId, route) {
|
|
486
|
+
this.#events.set(handleId, route);
|
|
487
|
+
}
|
|
488
|
+
_unrouteEvents(handleId) {
|
|
489
|
+
this.#events.delete(handleId);
|
|
490
|
+
}
|
|
491
|
+
async #call(method, args, controls = {}) {
|
|
492
|
+
if (this.#closed)
|
|
493
|
+
throw new Error("Database client is closed");
|
|
494
|
+
return this.#post("rpc-call", null, method, args, void 0, false, controls);
|
|
495
|
+
}
|
|
496
|
+
async #post(kind, handleId, method, args, transfer, bypassLimit = kind === "rpc-init" || method === "dispose", controls = {}) {
|
|
497
|
+
if (this.#fatal !== void 0)
|
|
498
|
+
throw this.#fatal;
|
|
499
|
+
controls.signal?.throwIfAborted();
|
|
500
|
+
if (!bypassLimit && this.#pending.size >= MAX_DATABASE_RPC_IN_FLIGHT) {
|
|
501
|
+
throw new RangeError(`A database worker connection cannot hold more than ${String(MAX_DATABASE_RPC_IN_FLIGHT)} in-flight requests`);
|
|
502
|
+
}
|
|
503
|
+
const requestId = crypto.randomUUID();
|
|
504
|
+
return new Promise((resolve, reject) => {
|
|
505
|
+
const onAbort = () => {
|
|
518
506
|
try {
|
|
519
|
-
|
|
507
|
+
this.#transport.postMessage({ version: protocolVersion, requestId, kind: "rpc-cancel" });
|
|
508
|
+
} catch {
|
|
520
509
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
return
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
else if (response.event === "invalidate") {
|
|
607
|
-
route.onInvalidate?.(response.payload);
|
|
608
|
-
}
|
|
609
|
-
else if (response.event === "error") {
|
|
610
|
-
route.onError?.(rehydrateError(response.payload));
|
|
611
|
-
}
|
|
612
|
-
else if (response.event === "progress")
|
|
613
|
-
route.onProgress?.(response.payload);
|
|
614
|
-
else if (response.event === "stats") {
|
|
615
|
-
route.onStats?.(response.payload);
|
|
616
|
-
}
|
|
617
|
-
else if (response.event === "complete") {
|
|
618
|
-
this.#events.delete(response.handleId);
|
|
619
|
-
route.onComplete?.();
|
|
620
|
-
}
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
const pending = this.#pending.get(response.requestId);
|
|
624
|
-
if (pending === undefined)
|
|
625
|
-
return;
|
|
626
|
-
this.#pending.delete(response.requestId);
|
|
627
|
-
pending.cleanup?.();
|
|
628
|
-
if (response.kind === "rpc-result")
|
|
629
|
-
pending.resolve(response.result);
|
|
630
|
-
else
|
|
631
|
-
pending.reject(rehydrateError(response.error));
|
|
632
|
-
}
|
|
633
|
-
#fail(error) {
|
|
634
|
-
this.#fatal = error;
|
|
635
|
-
const pending = [...this.#pending.values()];
|
|
636
|
-
this.#pending.clear();
|
|
637
|
-
this.#events.clear();
|
|
638
|
-
for (const call of pending) {
|
|
639
|
-
call.cleanup?.();
|
|
640
|
-
call.reject(error);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
510
|
+
};
|
|
511
|
+
const cleanup = () => {
|
|
512
|
+
controls.signal?.removeEventListener("abort", onAbort);
|
|
513
|
+
if (controls.onStats !== void 0)
|
|
514
|
+
this.#events.delete(requestId);
|
|
515
|
+
};
|
|
516
|
+
controls.signal?.addEventListener("abort", onAbort, { once: true });
|
|
517
|
+
if (controls.onStats !== void 0) {
|
|
518
|
+
this.#events.set(requestId, { onStats: controls.onStats });
|
|
519
|
+
}
|
|
520
|
+
this.#pending.set(requestId, { resolve, reject, cleanup });
|
|
521
|
+
try {
|
|
522
|
+
this.#transport.postMessage(kind === "rpc-init" ? { version: protocolVersion, requestId, kind, payload: args[0] } : { version: protocolVersion, requestId, kind, handleId, method, args }, transfer === void 0 ? void 0 : { transfer });
|
|
523
|
+
} catch (error) {
|
|
524
|
+
this.#pending.delete(requestId);
|
|
525
|
+
cleanup();
|
|
526
|
+
reject(error instanceof Error ? error : new Error("Database request failed", { cause: error }));
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
#receive(message) {
|
|
531
|
+
let response;
|
|
532
|
+
try {
|
|
533
|
+
response = parseRpcResponse(message);
|
|
534
|
+
} catch (cause) {
|
|
535
|
+
this.#rejectUnreadable(message, cause);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
if (response === null)
|
|
539
|
+
return;
|
|
540
|
+
if (response.kind === "rpc-event") {
|
|
541
|
+
const route = this.#events.get(response.handleId);
|
|
542
|
+
if (route === void 0)
|
|
543
|
+
return;
|
|
544
|
+
if (response.event === "change")
|
|
545
|
+
route.onChange?.(decodeQueryResult(response.payload));
|
|
546
|
+
else if (response.event === "invalidate") {
|
|
547
|
+
route.onInvalidate?.(response.payload);
|
|
548
|
+
} else if (response.event === "error") {
|
|
549
|
+
route.onError?.(rehydrateResponseError(response.payload));
|
|
550
|
+
} else if (response.event === "progress")
|
|
551
|
+
route.onProgress?.(response.payload);
|
|
552
|
+
else if (response.event === "stats") {
|
|
553
|
+
route.onStats?.(response.payload);
|
|
554
|
+
} else if (response.event === "complete") {
|
|
555
|
+
this.#events.delete(response.handleId);
|
|
556
|
+
route.onComplete?.();
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const pending = this.#pending.get(response.requestId);
|
|
561
|
+
if (pending === void 0)
|
|
562
|
+
return;
|
|
563
|
+
this.#pending.delete(response.requestId);
|
|
564
|
+
pending.cleanup?.();
|
|
565
|
+
if (response.kind === "rpc-result")
|
|
566
|
+
pending.resolve(response.result);
|
|
567
|
+
else
|
|
568
|
+
pending.reject(rehydrateResponseError(response.error));
|
|
569
|
+
}
|
|
570
|
+
#rejectUnreadable(message, cause) {
|
|
571
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
572
|
+
const error = new Error(`The database worker sent a frame this client cannot read: ${reason}`, {
|
|
573
|
+
cause
|
|
574
|
+
});
|
|
575
|
+
const requestId = message.requestId;
|
|
576
|
+
const pending = typeof requestId === "string" ? this.#pending.get(requestId) : void 0;
|
|
577
|
+
if (pending === void 0 || typeof requestId !== "string") {
|
|
578
|
+
this.#fail(error);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
this.#pending.delete(requestId);
|
|
582
|
+
pending.cleanup?.();
|
|
583
|
+
pending.reject(error);
|
|
584
|
+
}
|
|
585
|
+
#fail(error) {
|
|
586
|
+
this.#fatal = error;
|
|
587
|
+
const pending = [...this.#pending.values()];
|
|
588
|
+
this.#pending.clear();
|
|
589
|
+
this.#events.clear();
|
|
590
|
+
for (const call of pending) {
|
|
591
|
+
call.cleanup?.();
|
|
592
|
+
call.reject(error);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
643
595
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
async close() {
|
|
682
|
-
await this.#created;
|
|
683
|
-
try {
|
|
684
|
-
return (await this.client._invoke(this.handleId, "close", []));
|
|
685
|
-
}
|
|
686
|
-
finally {
|
|
687
|
-
this.client._unrouteEvents(this.handleId);
|
|
688
|
-
}
|
|
689
|
-
}
|
|
596
|
+
class ClientBufferedWriter {
|
|
597
|
+
client;
|
|
598
|
+
handleId;
|
|
599
|
+
constructor(client, handleId, created) {
|
|
600
|
+
this.client = client;
|
|
601
|
+
this.handleId = handleId;
|
|
602
|
+
this.#created = created;
|
|
603
|
+
created.catch(() => void 0);
|
|
604
|
+
}
|
|
605
|
+
#created;
|
|
606
|
+
async add(row) {
|
|
607
|
+
await this.#created;
|
|
608
|
+
return await this.client._invoke(this.handleId, "add", [row]);
|
|
609
|
+
}
|
|
610
|
+
async flush() {
|
|
611
|
+
await this.#created;
|
|
612
|
+
return await this.client._invoke(this.handleId, "flush", []);
|
|
613
|
+
}
|
|
614
|
+
requestFlush() {
|
|
615
|
+
void this.#created.then(() => this.client._invoke(this.handleId, "requestFlush", [])).catch(() => void 0);
|
|
616
|
+
}
|
|
617
|
+
async stats() {
|
|
618
|
+
await this.#created;
|
|
619
|
+
return await this.client._invoke(this.handleId, "stats", []);
|
|
620
|
+
}
|
|
621
|
+
async discard() {
|
|
622
|
+
await this.#created;
|
|
623
|
+
return await this.client._invoke(this.handleId, "discard", []);
|
|
624
|
+
}
|
|
625
|
+
async close() {
|
|
626
|
+
await this.#created;
|
|
627
|
+
try {
|
|
628
|
+
return await this.client._invoke(this.handleId, "close", []);
|
|
629
|
+
} finally {
|
|
630
|
+
this.client._unrouteEvents(this.handleId);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
690
633
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
async close() {
|
|
775
|
-
await this.#created;
|
|
776
|
-
try {
|
|
777
|
-
await this.client._invoke(this.handleId, "close", []);
|
|
778
|
-
}
|
|
779
|
-
finally {
|
|
780
|
-
for (const subscriptionId of this.#subscriptionIds) {
|
|
781
|
-
this.client._unrouteEvents(subscriptionId);
|
|
782
|
-
}
|
|
783
|
-
this.#subscriptionIds.clear();
|
|
784
|
-
}
|
|
785
|
-
}
|
|
634
|
+
class ClientLiveQuerySet {
|
|
635
|
+
client;
|
|
636
|
+
handleId;
|
|
637
|
+
#subscriptionIds = /* @__PURE__ */ new Set();
|
|
638
|
+
constructor(client, handleId, created) {
|
|
639
|
+
this.client = client;
|
|
640
|
+
this.handleId = handleId;
|
|
641
|
+
this.#created = created;
|
|
642
|
+
created.catch(() => void 0);
|
|
643
|
+
}
|
|
644
|
+
#created;
|
|
645
|
+
async subscribe(query, options) {
|
|
646
|
+
await this.#created;
|
|
647
|
+
const subscriptionId = crypto.randomUUID();
|
|
648
|
+
const state = { completed: false };
|
|
649
|
+
this.client._routeEvents(subscriptionId, {
|
|
650
|
+
onChange: options.onChange.bind(options),
|
|
651
|
+
...options.onError === void 0 ? {} : { onError: options.onError.bind(options) },
|
|
652
|
+
onComplete: () => {
|
|
653
|
+
state.completed = true;
|
|
654
|
+
this.#subscriptionIds.delete(subscriptionId);
|
|
655
|
+
options.onComplete?.();
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
try {
|
|
659
|
+
const created = await this.client._invoke(this.handleId, "subscribe", [
|
|
660
|
+
subscriptionId,
|
|
661
|
+
query
|
|
662
|
+
]);
|
|
663
|
+
this.#subscriptionIds.add(subscriptionId);
|
|
664
|
+
return new ClientLiveSubscription(this.client, subscriptionId, created.dependencyTableIds, () => this.#subscriptionIds.delete(subscriptionId), state);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
this.client._unrouteEvents(subscriptionId);
|
|
667
|
+
throw error;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async observe(query, options) {
|
|
671
|
+
await this.#created;
|
|
672
|
+
const subscriptionId = crypto.randomUUID();
|
|
673
|
+
const state = { completed: false };
|
|
674
|
+
this.client._routeEvents(subscriptionId, {
|
|
675
|
+
onInvalidate: options.onInvalidate.bind(options),
|
|
676
|
+
...options.onError === void 0 ? {} : { onError: options.onError.bind(options) },
|
|
677
|
+
onComplete: () => {
|
|
678
|
+
state.completed = true;
|
|
679
|
+
this.#subscriptionIds.delete(subscriptionId);
|
|
680
|
+
options.onComplete?.();
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
try {
|
|
684
|
+
const created = await this.client._invoke(this.handleId, "observe", [
|
|
685
|
+
subscriptionId,
|
|
686
|
+
query
|
|
687
|
+
]);
|
|
688
|
+
this.#subscriptionIds.add(subscriptionId);
|
|
689
|
+
return new ClientLiveSubscription(this.client, subscriptionId, created.dependencyTableIds, () => this.#subscriptionIds.delete(subscriptionId), state);
|
|
690
|
+
} catch (error) {
|
|
691
|
+
this.client._unrouteEvents(subscriptionId);
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
async stats() {
|
|
696
|
+
await this.#created;
|
|
697
|
+
return await this.client._invoke(this.handleId, "stats", []);
|
|
698
|
+
}
|
|
699
|
+
notifyLocalCommit() {
|
|
700
|
+
void this.#created.then(() => this.client._invoke(this.handleId, "notifyLocalCommit", [])).catch(() => void 0);
|
|
701
|
+
}
|
|
702
|
+
async refresh() {
|
|
703
|
+
await this.#created;
|
|
704
|
+
await this.client._invoke(this.handleId, "refresh", []);
|
|
705
|
+
}
|
|
706
|
+
async close() {
|
|
707
|
+
await this.#created;
|
|
708
|
+
try {
|
|
709
|
+
await this.client._invoke(this.handleId, "close", []);
|
|
710
|
+
} finally {
|
|
711
|
+
for (const subscriptionId of this.#subscriptionIds) {
|
|
712
|
+
this.client._unrouteEvents(subscriptionId);
|
|
713
|
+
}
|
|
714
|
+
this.#subscriptionIds.clear();
|
|
715
|
+
}
|
|
716
|
+
}
|
|
786
717
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
}
|
|
718
|
+
class ClientLiveSubscription {
|
|
719
|
+
client;
|
|
720
|
+
handleId;
|
|
721
|
+
dependencyTableIds;
|
|
722
|
+
onClosed;
|
|
723
|
+
state;
|
|
724
|
+
constructor(client, handleId, dependencyTableIds, onClosed, state = { completed: false }) {
|
|
725
|
+
this.client = client;
|
|
726
|
+
this.handleId = handleId;
|
|
727
|
+
this.dependencyTableIds = dependencyTableIds;
|
|
728
|
+
this.onClosed = onClosed;
|
|
729
|
+
this.state = state;
|
|
730
|
+
}
|
|
731
|
+
async close() {
|
|
732
|
+
try {
|
|
733
|
+
if (!this.state.completed)
|
|
734
|
+
await this.client._invoke(this.handleId, "close", []);
|
|
735
|
+
} finally {
|
|
736
|
+
this.client._unrouteEvents(this.handleId);
|
|
737
|
+
this.onClosed();
|
|
738
|
+
}
|
|
739
|
+
}
|
|
811
740
|
}
|
|
741
|
+
export {
|
|
742
|
+
ClientBufferedWriter,
|
|
743
|
+
ClientLiveQuerySet,
|
|
744
|
+
ClientLiveSubscription,
|
|
745
|
+
MinnowDatabaseClient
|
|
746
|
+
};
|