@minnowdb/core 0.7.5 → 0.7.8
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/engine/batch.d.ts +14 -1
- package/dist/engine/batch.js +23 -2
- package/dist/engine/buffered-writer.d.ts +7 -4
- package/dist/engine/client.d.ts +42 -25
- package/dist/engine/client.js +23 -8
- package/dist/engine/database.d.ts +44 -22
- package/dist/engine/database.js +285 -103
- package/dist/engine/schema.d.ts +85 -2
- package/dist/engine/worker-server.js +4 -1
- package/dist/storage/indexeddb.js +13 -8
- package/dist/storage/toolkit/record-core.js +11 -6
- package/dist/storage/types.d.ts +11 -0
- package/dist/worker-protocol/index.d.ts +1 -1
- package/dist/worker-protocol/index.js +1 -1
- package/package.json +1 -1
package/dist/engine/batch.d.ts
CHANGED
|
@@ -31,5 +31,18 @@ export interface ColumnarBatch {
|
|
|
31
31
|
}
|
|
32
32
|
/** What `insertBatch` and `upsertBatch` take: rows, or columns for a bulk load. */
|
|
33
33
|
export type InsertBatchInput = readonly BatchRow[] | ColumnarBatch;
|
|
34
|
+
/**
|
|
35
|
+
* `ColumnarBatch` as the schema-typed overloads hand it to the erased implementation: a column
|
|
36
|
+
* vector or omission mask may be `undefined`, meaning that column is not in the batch. Only the
|
|
37
|
+
* type is looser — `toColumnarBatch` drops such entries, so nothing downstream sees them.
|
|
38
|
+
*/
|
|
39
|
+
export interface ColumnarBatchLike {
|
|
40
|
+
readonly columns: Readonly<Record<string, readonly BatchValue[] | undefined>>;
|
|
41
|
+
readonly omitted?: Readonly<Record<string, readonly boolean[] | undefined>>;
|
|
42
|
+
readonly rowCount?: number;
|
|
43
|
+
}
|
|
44
|
+
export type InsertBatchInputLike = readonly BatchRow[] | ColumnarBatchLike;
|
|
45
|
+
/** The entries of a per-column record whose vector is present. */
|
|
46
|
+
export declare function definedVectors<T>(vectors: Readonly<Record<string, T | undefined>>): Record<string, T>;
|
|
34
47
|
/** Pivots rows into the engine's columnar form; a columnar batch passes straight through. */
|
|
35
|
-
export declare function toColumnarBatch(input:
|
|
48
|
+
export declare function toColumnarBatch(input: InsertBatchInputLike): ColumnarBatch;
|
package/dist/engine/batch.js
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
+
function definedVectors(vectors) {
|
|
2
|
+
const defined = {};
|
|
3
|
+
for (const [name, vector] of Object.entries(vectors)) {
|
|
4
|
+
if (vector !== void 0)
|
|
5
|
+
defined[name] = vector;
|
|
6
|
+
}
|
|
7
|
+
return defined;
|
|
8
|
+
}
|
|
9
|
+
function hasUndefinedVector(vectors) {
|
|
10
|
+
return Object.values(vectors).includes(void 0);
|
|
11
|
+
}
|
|
1
12
|
function isColumnarBatch(input) {
|
|
2
13
|
return !Array.isArray(input);
|
|
3
14
|
}
|
|
4
15
|
function toColumnarBatch(input) {
|
|
5
|
-
if (isColumnarBatch(input))
|
|
6
|
-
|
|
16
|
+
if (isColumnarBatch(input)) {
|
|
17
|
+
const columnsComplete = !hasUndefinedVector(input.columns);
|
|
18
|
+
const omittedComplete = input.omitted === void 0 || !hasUndefinedVector(input.omitted);
|
|
19
|
+
if (columnsComplete && omittedComplete)
|
|
20
|
+
return input;
|
|
21
|
+
return {
|
|
22
|
+
columns: definedVectors(input.columns),
|
|
23
|
+
...input.omitted === void 0 ? {} : { omitted: definedVectors(input.omitted) },
|
|
24
|
+
...input.rowCount === void 0 ? {} : { rowCount: input.rowCount }
|
|
25
|
+
};
|
|
26
|
+
}
|
|
7
27
|
if (input.length === 0)
|
|
8
28
|
throw new TypeError("A batch needs at least one row");
|
|
9
29
|
const columnsByName = /* @__PURE__ */ new Map();
|
|
@@ -36,5 +56,6 @@ function toColumnarBatch(input) {
|
|
|
36
56
|
};
|
|
37
57
|
}
|
|
38
58
|
export {
|
|
59
|
+
definedVectors,
|
|
39
60
|
toColumnarBatch
|
|
40
61
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BatchRow } from "./batch.js";
|
|
2
2
|
import type { InsertBatchResult, MinnowDatabase, UpsertBatchResult } from "./database.js";
|
|
3
3
|
export interface BufferedWriterOptions {
|
|
4
4
|
mode?: "insert" | "upsert";
|
|
@@ -26,15 +26,18 @@ export interface LifecycleFlushOptions {
|
|
|
26
26
|
}
|
|
27
27
|
/** Maximum accepted `add()` calls that have not completed. Callers must await for backpressure. */
|
|
28
28
|
export declare const MAX_BUFFERED_WRITER_PENDING_ADDS = 64;
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Batches row-oriented writes by row count, estimated bytes, or age. `TRow` is the declared
|
|
31
|
+
* table's insert row when the database that opened the writer carries a schema.
|
|
32
|
+
*/
|
|
33
|
+
export declare class BufferedTableWriter<TRow extends BatchRow = BatchRow> {
|
|
31
34
|
#private;
|
|
32
35
|
private readonly database;
|
|
33
36
|
private readonly tableName;
|
|
34
37
|
constructor(database: MinnowDatabase, tableName: string, options?: BufferedWriterOptions);
|
|
35
38
|
get pendingRowCount(): number;
|
|
36
39
|
get estimatedBytes(): number;
|
|
37
|
-
add(row:
|
|
40
|
+
add(row: TRow): Promise<BufferedFlushResult | undefined>;
|
|
38
41
|
flush(): Promise<BufferedFlushResult | undefined>;
|
|
39
42
|
requestFlush(): void;
|
|
40
43
|
close(): Promise<BufferedFlushResult | undefined>;
|
package/dist/engine/client.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type CompactionJobRecord, type GarbageCollectionJobRecord, type StorageIntegrityMode, type StorageIntegrityReport, type StorageStats, type InterruptedSnapshotImport, type InterruptedSnapshotImportAbortResult } from "../storage/types.js";
|
|
2
|
-
import { type BatchRow
|
|
2
|
+
import { type BatchRow } from "./batch.js";
|
|
3
3
|
import type { Catalog } from "./catalog.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { BufferPoolStats, StagedWriteResult, StagedUpsertResult, BufferedFlushResult, BufferedWriterOptions, CancelCompactionJobResult, CollectGarbageOptions, CollectGarbageStepOptions, CompactTableOptions, CompactTableResult, CompactTableStepOptions, CompactionJobProgress, CreateTableInput, MigrateOptions, DeleteBatchResult, ExecuteOptions, ExecuteResult, GarbageCollectionProgress, GarbageCollectionResult, MaintenanceStatus, InsertBatchResult, QueryOptions, QueryExecutionStats, QueryCursorOptions, QuerySpillCleanupOptions, QuerySpillCleanupResult, RunStatementOptions, SnapshotExportOptions, SnapshotImportOptions, TableDefinition, UpdateBatchResult, UpsertBatchResult, VisibleSegmentPage, VisibleSegmentPageOptions } from "./database.js";
|
|
5
5
|
import type { LiveQueryInput, LiveQueryInvalidation, LiveQueryObserveOptions, LiveQueryStats, LiveQuerySubscribeOptions } from "./live.js";
|
|
6
6
|
import type { CompiledQuery, CompiledStatement, QueryResult, QueryValue } from "./query.js";
|
|
7
|
-
import type { AnyTable, SchemaDefinition } from "./schema.js";
|
|
7
|
+
import type { AnySchema, UntypedSchema, AnyTable, BatchColumnName, BatchDeleteInput, BatchInsertInput, BatchInsertRow, BatchKeyValue, BatchReadOptions, BatchReadRow, BatchUpdateChanges, BatchUpdateInput, BatchUpsertOptions, SchemaDefinition, TableName } from "./schema.js";
|
|
8
8
|
import { type WireMigrationStep } from "./schema-wire.js";
|
|
9
9
|
import type { StoreDescriptor, WireDatabaseOptions } from "./worker-host.js";
|
|
10
10
|
/**
|
|
@@ -31,7 +31,13 @@ export interface ClientTransport {
|
|
|
31
31
|
removeEventListener?(type: "error" | "messageerror", listener: () => void): void;
|
|
32
32
|
terminate?(): void;
|
|
33
33
|
}
|
|
34
|
-
export interface MinnowDatabaseClientOptions {
|
|
34
|
+
export interface MinnowDatabaseClientOptions<TSchema extends AnySchema = UntypedSchema> {
|
|
35
|
+
/**
|
|
36
|
+
* The schema this database is declared against. It types every batch method by table name and
|
|
37
|
+
* is what a bare `migrate()` applies. It stays on the main thread: the worker learns the
|
|
38
|
+
* schema from `migrate()`, not from construction.
|
|
39
|
+
*/
|
|
40
|
+
schema?: TSchema;
|
|
35
41
|
/**
|
|
36
42
|
* Defaults to `{ kind: "indexeddb", name: "minnow" }`. The `opfs` kind selects
|
|
37
43
|
* `OpfsBlockStore`, which needs the worker to be a dedicated worker (it always is with
|
|
@@ -71,9 +77,9 @@ interface RpcCallControls {
|
|
|
71
77
|
signal?: AbortSignal | undefined;
|
|
72
78
|
onStats?: ((stats: QueryExecutionStats) => void) | undefined;
|
|
73
79
|
}
|
|
74
|
-
export declare class MinnowDatabaseClient {
|
|
80
|
+
export declare class MinnowDatabaseClient<TSchema extends AnySchema = UntypedSchema> {
|
|
75
81
|
#private;
|
|
76
|
-
constructor(transport: ClientTransport, options?: MinnowDatabaseClientOptions);
|
|
82
|
+
constructor(transport: ClientTransport, options?: MinnowDatabaseClientOptions<TSchema>);
|
|
77
83
|
/** Resolves once the worker has opened the store and constructed the database. */
|
|
78
84
|
ready(): Promise<void>;
|
|
79
85
|
createTable(input: CreateTableInput): Promise<void>;
|
|
@@ -103,17 +109,28 @@ export declare class MinnowDatabaseClient {
|
|
|
103
109
|
/** The published catalog; see `MinnowDatabase.introspect()`. */
|
|
104
110
|
introspect(): Promise<Catalog>;
|
|
105
111
|
listTables(): Promise<TableDefinition[]>;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Brings storage in line with a schema declaration. With no argument it applies the schema the
|
|
114
|
+
* client was constructed with.
|
|
115
|
+
*/
|
|
116
|
+
migrate(definition?: SchemaDefinition<readonly AnyTable[]> | undefined, options?: MigrateOptions): Promise<ClientMigrationResult>;
|
|
117
|
+
insertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>): Promise<InsertBatchResult>;
|
|
118
|
+
insert<TName extends TableName<TSchema>>(tableName: TName, row: BatchInsertRow<TSchema, TName>): Promise<InsertBatchResult>;
|
|
119
|
+
upsertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<UpsertBatchResult>;
|
|
120
|
+
upsert<TName extends TableName<TSchema>>(tableName: TName, row: BatchInsertRow<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<UpsertBatchResult>;
|
|
121
|
+
updateBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchUpdateInput<TSchema, TName>): Promise<UpdateBatchResult>;
|
|
122
|
+
/**
|
|
123
|
+
* Changes one row by the table's unique key. An explicitly `undefined` change leaves that
|
|
124
|
+
* column untouched, so a patch spread from optional fields needs no filtering first.
|
|
125
|
+
*/
|
|
126
|
+
update<TName extends TableName<TSchema>>(tableName: TName, key: BatchKeyValue<TSchema, TName>, changes: BatchUpdateChanges<TSchema, TName>): Promise<UpdateBatchResult>;
|
|
127
|
+
deleteBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchDeleteInput<TSchema, TName>): Promise<DeleteBatchResult>;
|
|
128
|
+
delete<TName extends TableName<TSchema>>(tableName: TName, key: BatchKeyValue<TSchema, TName>): Promise<DeleteBatchResult>;
|
|
129
|
+
bufferedWriter<TName extends TableName<TSchema>>(tableName: TName, options?: BufferedWriterOptions): ClientBufferedWriter<BatchInsertRow<TSchema, TName>>;
|
|
130
|
+
readTable<TName extends TableName<TSchema>, const TColumns extends ReadonlyArray<BatchColumnName<TSchema, TName>>>(tableName: TName, options: BatchReadOptions<TSchema, TName, TColumns> & {
|
|
131
|
+
readonly columns: TColumns;
|
|
132
|
+
}): Promise<Array<Pick<BatchReadRow<TSchema, TName>, TColumns[number]>>>;
|
|
133
|
+
readTable<TName extends TableName<TSchema>>(tableName: TName, versionOrOptions?: number | BatchReadOptions<TSchema, TName>): Promise<Array<BatchReadRow<TSchema, TName>>>;
|
|
117
134
|
/**
|
|
118
135
|
* Results cross the channel as one array per column (typed arrays for numbers, booleans, and
|
|
119
136
|
* datetimes) and are rebuilt into row objects here; see `result-wire.ts`.
|
|
@@ -142,7 +159,7 @@ export declare class MinnowDatabaseClient {
|
|
|
142
159
|
* transaction and publishes as one atomic commit when the callback returns; an error
|
|
143
160
|
* aborts the scope with nothing published.
|
|
144
161
|
*/
|
|
145
|
-
write<T>(action: (session: ClientWriteSession) => Promise<T>): Promise<{
|
|
162
|
+
write<T>(action: (session: ClientWriteSession<TSchema>) => Promise<T>): Promise<{
|
|
146
163
|
result: T;
|
|
147
164
|
version: number | null;
|
|
148
165
|
}>;
|
|
@@ -205,14 +222,14 @@ export declare class MinnowDatabaseClient {
|
|
|
205
222
|
* consistent with each other for the lifetime of the callback.
|
|
206
223
|
*/
|
|
207
224
|
/** The scope handed to the client `write()`; mirrors the in-worker WriteSession. */
|
|
208
|
-
export interface ClientWriteSession {
|
|
225
|
+
export interface ClientWriteSession<TSchema extends AnySchema = UntypedSchema> {
|
|
209
226
|
/** Read-your-writes: observes the pre-scope snapshot plus everything staged so far. */
|
|
210
227
|
query(sql: string, options?: QueryOptions): Promise<QueryResult>;
|
|
211
228
|
execute(sql: string, params?: readonly QueryValue[]): Promise<ExecuteResult>;
|
|
212
|
-
insertBatch(tableName:
|
|
213
|
-
upsertBatch(tableName:
|
|
214
|
-
updateBatch(tableName:
|
|
215
|
-
deleteBatch(tableName:
|
|
229
|
+
insertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
230
|
+
upsertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<StagedUpsertResult>;
|
|
231
|
+
updateBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchUpdateInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
232
|
+
deleteBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchDeleteInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
216
233
|
}
|
|
217
234
|
export interface ClientSnapshotSession {
|
|
218
235
|
/** The pinned manifest version; null only on a database with no commits yet. */
|
|
@@ -223,12 +240,12 @@ export interface ClientSnapshotSession {
|
|
|
223
240
|
* Proxy of a worker-side BufferedTableWriter. The age timer runs on the worker's clock, and
|
|
224
241
|
* onError fires for background flush failures exactly as in-worker — delivered as an event.
|
|
225
242
|
*/
|
|
226
|
-
export declare class ClientBufferedWriter {
|
|
243
|
+
export declare class ClientBufferedWriter<TRow extends BatchRow = BatchRow> {
|
|
227
244
|
#private;
|
|
228
245
|
private readonly client;
|
|
229
246
|
private readonly handleId;
|
|
230
247
|
constructor(client: MinnowDatabaseClient, handleId: string, created: Promise<unknown>);
|
|
231
|
-
add(row:
|
|
248
|
+
add(row: TRow): Promise<BufferedFlushResult | undefined>;
|
|
232
249
|
flush(): Promise<BufferedFlushResult | undefined>;
|
|
233
250
|
/** Fire-and-forget: flush failures surface through onError, matching the in-worker contract. */
|
|
234
251
|
requestFlush(): void;
|
package/dist/engine/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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
3
|
import { parseRpcResponse, MAX_DATABASE_RPC_IN_FLIGHT, protocolVersion } from "../worker-protocol/index.js";
|
|
4
|
-
import { toColumnarBatch } from "./batch.js";
|
|
4
|
+
import { definedVectors, toColumnarBatch } from "./batch.js";
|
|
5
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";
|
|
@@ -92,6 +92,10 @@ function rehydrateResponseError(payload) {
|
|
|
92
92
|
return rehydrateError(candidate);
|
|
93
93
|
}
|
|
94
94
|
class MinnowDatabaseClient {
|
|
95
|
+
#schema;
|
|
96
|
+
get #erased() {
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
95
99
|
#transport;
|
|
96
100
|
#pending = /* @__PURE__ */ new Map();
|
|
97
101
|
#events = /* @__PURE__ */ new Map();
|
|
@@ -109,6 +113,7 @@ class MinnowDatabaseClient {
|
|
|
109
113
|
this.#fail(new Error("A database worker message could not be deserialized"));
|
|
110
114
|
};
|
|
111
115
|
constructor(transport, options = {}) {
|
|
116
|
+
this.#schema = options.schema;
|
|
112
117
|
this.#transport = transport;
|
|
113
118
|
transport.addEventListener("message", this.#onMessage);
|
|
114
119
|
transport.addEventListener("error", this.#onError);
|
|
@@ -165,7 +170,10 @@ class MinnowDatabaseClient {
|
|
|
165
170
|
async listTables() {
|
|
166
171
|
return await this.#call("listTables", []);
|
|
167
172
|
}
|
|
168
|
-
async migrate(definition, options = {}) {
|
|
173
|
+
async migrate(definition = this.#schema, options = {}) {
|
|
174
|
+
if (definition === void 0) {
|
|
175
|
+
throw new TypeError("migrate() needs a schema: pass a definition, or construct the client with { schema }");
|
|
176
|
+
}
|
|
169
177
|
return await this.#call("migrate", [
|
|
170
178
|
serializeSchema(definition),
|
|
171
179
|
options
|
|
@@ -186,10 +194,12 @@ class MinnowDatabaseClient {
|
|
|
186
194
|
return await this.#call("upsert", [tableName, row, options]);
|
|
187
195
|
}
|
|
188
196
|
async updateBatch(tableName, input) {
|
|
189
|
-
|
|
197
|
+
const wire = { keys: input.keys, changes: definedVectors(input.changes) };
|
|
198
|
+
return await this.#call("updateBatch", [tableName, wire]);
|
|
190
199
|
}
|
|
191
200
|
async update(tableName, key, changes) {
|
|
192
|
-
|
|
201
|
+
const present = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== void 0));
|
|
202
|
+
return await this.#call("update", [tableName, key, present]);
|
|
193
203
|
}
|
|
194
204
|
async deleteBatch(tableName, input) {
|
|
195
205
|
return await this.#call("deleteBatch", [tableName, input]);
|
|
@@ -204,7 +214,7 @@ class MinnowDatabaseClient {
|
|
|
204
214
|
...onError === void 0 ? {} : { onError }
|
|
205
215
|
});
|
|
206
216
|
const created = this.#call("bufferedWriter", [handleId, tableName, wireOptions]);
|
|
207
|
-
return new ClientBufferedWriter(this, handleId, created);
|
|
217
|
+
return new ClientBufferedWriter(this.#erased, handleId, created);
|
|
208
218
|
}
|
|
209
219
|
async readTable(tableName, versionOrOptions) {
|
|
210
220
|
return decodeQueryResult(await this.#call("readTable", versionOrOptions === void 0 ? [tableName] : [tableName, versionOrOptions])).rows;
|
|
@@ -290,7 +300,12 @@ class MinnowDatabaseClient {
|
|
|
290
300
|
}
|
|
291
301
|
async write(action) {
|
|
292
302
|
const opened = await this.#call("writeOpen", []);
|
|
293
|
-
const stage = (op, tableName, input) => this._invoke(opened.handleId, "stage", [
|
|
303
|
+
const stage = (op, tableName, input, options) => this._invoke(opened.handleId, "stage", [
|
|
304
|
+
op,
|
|
305
|
+
tableName,
|
|
306
|
+
input,
|
|
307
|
+
options
|
|
308
|
+
]);
|
|
294
309
|
const session = {
|
|
295
310
|
query: async (sql, options = {}) => {
|
|
296
311
|
const { signal, onStats, ...wireOptions } = options;
|
|
@@ -298,7 +313,7 @@ class MinnowDatabaseClient {
|
|
|
298
313
|
},
|
|
299
314
|
execute: (sql, params) => this._invoke(opened.handleId, "execute", params === void 0 ? [sql] : [sql, params]),
|
|
300
315
|
insertBatch: (tableName, input) => stage("insertBatch", tableName, input),
|
|
301
|
-
upsertBatch: (tableName, input) => stage("upsertBatch", tableName, input),
|
|
316
|
+
upsertBatch: (tableName, input, options) => stage("upsertBatch", tableName, input, options),
|
|
302
317
|
updateBatch: (tableName, input) => stage("updateBatch", tableName, input),
|
|
303
318
|
deleteBatch: (tableName, input) => stage("deleteBatch", tableName, input)
|
|
304
319
|
};
|
|
@@ -419,7 +434,7 @@ class MinnowDatabaseClient {
|
|
|
419
434
|
liveQueries(options = {}) {
|
|
420
435
|
const handleId = crypto.randomUUID();
|
|
421
436
|
const created = this.#call("liveQueries", [handleId, options]);
|
|
422
|
-
return new ClientLiveQuerySet(this, handleId, created);
|
|
437
|
+
return new ClientLiveQuerySet(this.#erased, handleId, created);
|
|
423
438
|
}
|
|
424
439
|
async listVisibleSegmentPage(tableName, options) {
|
|
425
440
|
return await this.#call("listVisibleSegmentPage", options === void 0 ? [tableName] : [tableName, options]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BatchValue } from "./batch.js";
|
|
2
2
|
import { BufferedTableWriter, type BufferedWriterOptions } from "./buffered-writer.js";
|
|
3
3
|
export { attachLifecycleFlush, BufferedTableWriter, MAX_BUFFERED_WRITER_PENDING_ADDS, type BufferedFlushResult, type BufferedWriterOptions, type LifecycleDocumentTarget, type LifecycleFlushOptions, type LifecycleFlushRequester, type LifecyclePageTarget, } from "./buffered-writer.js";
|
|
4
4
|
import { CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, MissingKeyError, SqlCompileError, UnknownTableError, UniqueConstraintError, VisibleSegmentCursorStaleError } from "./errors.js";
|
|
@@ -9,7 +9,7 @@ import type { SnapshotExportProgress, SnapshotLoadProgress } from "../storage/sn
|
|
|
9
9
|
import { type ComparisonOperator, type CompiledQuery, type CompiledStatement, type ForeignKeyDefinition, type QueryResult, type QueryRow, type QueryValue, type UniqueConstraintDefinition } from "./query.js";
|
|
10
10
|
import { LiveQuerySet, type LiveQuerySetOptions } from "./live.js";
|
|
11
11
|
import { type Catalog } from "./catalog.js";
|
|
12
|
-
import { type AnyTable, type MigrationStep, type SchemaDefinition } from "./schema.js";
|
|
12
|
+
import { type AnySchema, type UntypedSchema, type AnyTable, type BatchColumnName, type BatchDeleteInput, type BatchInsertInput, type BatchInsertRow, type BatchKeyValue, type BatchReadOptions, type BatchReadRow, type BatchUpdateChanges, type BatchUpdateInput, type BatchUpsertOptions, type MigrationStep, type SchemaDefinition, type TableName } from "./schema.js";
|
|
13
13
|
/** Nested SQL savepoints and their total cloned transaction state are both hard bounded. */
|
|
14
14
|
export declare const MAX_TRANSACTION_SAVEPOINTS = 64;
|
|
15
15
|
export declare const MAX_TRANSACTION_SAVEPOINT_BYTES: number;
|
|
@@ -95,6 +95,7 @@ export interface UpsertBatchResult extends Omit<InsertBatchResult, "segmentId">
|
|
|
95
95
|
requestedRowCount: number;
|
|
96
96
|
insertedRowCount: number;
|
|
97
97
|
updatedRowCount: number;
|
|
98
|
+
/** Input rows rejected by `conflictWhere`; always 0 without it. */
|
|
98
99
|
skippedRowCount: number;
|
|
99
100
|
}
|
|
100
101
|
export interface UpsertConflictWhere {
|
|
@@ -174,6 +175,10 @@ export interface StagedWriteResult {
|
|
|
174
175
|
/** Values filled by defaults or auto-increment while the rows were staged. */
|
|
175
176
|
generatedColumns?: Record<string, QueryValue[]>;
|
|
176
177
|
}
|
|
178
|
+
export interface StagedUpsertResult extends StagedWriteResult {
|
|
179
|
+
/** Input rows rejected by `conflictWhere`; always 0 without it. */
|
|
180
|
+
skippedRowCount: number;
|
|
181
|
+
}
|
|
177
182
|
/**
|
|
178
183
|
* The scope handed to `write()`: every mutation stages into one transaction and publishes
|
|
179
184
|
* as one commit — all of it or none of it, in every tab. Reads observe the pre-scope snapshot
|
|
@@ -185,7 +190,7 @@ export interface StagedWriteResult {
|
|
|
185
190
|
* caught the original error. A mutation that fails validation before registering anything
|
|
186
191
|
* leaves the scope usable.
|
|
187
192
|
*/
|
|
188
|
-
export interface WriteSession {
|
|
193
|
+
export interface WriteSession<TSchema extends AnySchema = UntypedSchema> {
|
|
189
194
|
/**
|
|
190
195
|
* Read-your-writes: the query observes the pre-scope snapshot PLUS everything this scope
|
|
191
196
|
* has staged so far, ordered after all committed data — without publishing anything.
|
|
@@ -193,10 +198,10 @@ export interface WriteSession {
|
|
|
193
198
|
query(sql: string, options?: QueryOptions): Promise<QueryResult>;
|
|
194
199
|
/** Runs a SELECT, INSERT, UPDATE, or DELETE inside this write scope. */
|
|
195
200
|
execute(sql: string, params?: readonly QueryValue[]): Promise<ExecuteResult>;
|
|
196
|
-
insertBatch(tableName:
|
|
197
|
-
upsertBatch(tableName:
|
|
198
|
-
updateBatch(tableName:
|
|
199
|
-
deleteBatch(tableName:
|
|
201
|
+
insertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
202
|
+
upsertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<StagedUpsertResult>;
|
|
203
|
+
updateBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchUpdateInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
204
|
+
deleteBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchDeleteInput<TSchema, TName>): Promise<StagedWriteResult>;
|
|
200
205
|
}
|
|
201
206
|
/** What one statement's execution cost, reported by the engine that ran it. */
|
|
202
207
|
export interface QueryExecutionStats {
|
|
@@ -440,7 +445,13 @@ export interface TableDefinition {
|
|
|
440
445
|
}
|
|
441
446
|
export type DatabaseRow = Record<string, Exclude<BatchValue, null> | null>;
|
|
442
447
|
export { CompactionBacklogError, CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, MissingKeyError, SqlCompileError, UnknownTableError, TableInUseError, UniqueConstraintError, VisibleSegmentCursorStaleError, };
|
|
443
|
-
export interface MinnowDatabaseOptions {
|
|
448
|
+
export interface MinnowDatabaseOptions<TSchema extends AnySchema = UntypedSchema> {
|
|
449
|
+
/**
|
|
450
|
+
* The schema this database is declared against. It types every batch method by table name —
|
|
451
|
+
* rows, keys, update changes, `conflictWhere`, `readTable` results — and is what a bare
|
|
452
|
+
* `migrate()` applies. Without it the batch API addresses tables by plain string, as SQL does.
|
|
453
|
+
*/
|
|
454
|
+
schema?: TSchema;
|
|
444
455
|
/**
|
|
445
456
|
* Block codec for newly written blocks; defaults to "gzip", which is also what compaction
|
|
446
457
|
* rewrites to, so a table's blocks are encoded the same way however they got there.
|
|
@@ -699,10 +710,10 @@ export interface SnapshotImportOptions {
|
|
|
699
710
|
/** Cancels transfer and atomically removes this caller's staged import. */
|
|
700
711
|
signal?: AbortSignal;
|
|
701
712
|
}
|
|
702
|
-
export declare class MinnowDatabase {
|
|
713
|
+
export declare class MinnowDatabase<TSchema extends AnySchema = UntypedSchema> {
|
|
703
714
|
#private;
|
|
704
715
|
private readonly store;
|
|
705
|
-
constructor(store: BlockStore, options?: MinnowDatabaseOptions);
|
|
716
|
+
constructor(store: BlockStore, options?: MinnowDatabaseOptions<TSchema>);
|
|
706
717
|
/**
|
|
707
718
|
* Stops timers and background scheduling, rolls back an abandoned statement transaction,
|
|
708
719
|
* closes live-query resources, releases the engine's reader lease, and drops resident caches.
|
|
@@ -754,17 +765,24 @@ export declare class MinnowDatabase {
|
|
|
754
765
|
dropTable(tableName: string, options?: {
|
|
755
766
|
ifExists?: boolean;
|
|
756
767
|
}): Promise<boolean>;
|
|
757
|
-
insertBatch(tableName:
|
|
758
|
-
insert(tableName:
|
|
759
|
-
upsertBatch(tableName:
|
|
760
|
-
upsert(tableName:
|
|
761
|
-
updateBatch(tableName:
|
|
762
|
-
|
|
768
|
+
insertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>): Promise<InsertBatchResult>;
|
|
769
|
+
insert<TName extends TableName<TSchema>>(tableName: TName, row: BatchInsertRow<TSchema, TName>): Promise<InsertBatchResult>;
|
|
770
|
+
upsertBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchInsertInput<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<UpsertBatchResult>;
|
|
771
|
+
upsert<TName extends TableName<TSchema>>(tableName: TName, row: BatchInsertRow<TSchema, TName>, options?: BatchUpsertOptions<TSchema, TName>): Promise<UpsertBatchResult>;
|
|
772
|
+
updateBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchUpdateInput<TSchema, TName>): Promise<UpdateBatchResult>;
|
|
773
|
+
/**
|
|
774
|
+
* Changes one row by the table's unique key. An explicitly `undefined` change leaves that
|
|
775
|
+
* column untouched, so a patch spread from optional fields needs no filtering first.
|
|
776
|
+
*/
|
|
777
|
+
update<TName extends TableName<TSchema>>(tableName: TName, key: BatchKeyValue<TSchema, TName>, changes: BatchUpdateChanges<TSchema, TName>): Promise<UpdateBatchResult>;
|
|
763
778
|
/** Deletes one row by the table's unique key. */
|
|
764
|
-
delete(tableName:
|
|
765
|
-
deleteBatch(tableName:
|
|
766
|
-
bufferedWriter(tableName:
|
|
767
|
-
readTable(tableName:
|
|
779
|
+
delete<TName extends TableName<TSchema>>(tableName: TName, key: BatchKeyValue<TSchema, TName>): Promise<DeleteBatchResult>;
|
|
780
|
+
deleteBatch<TName extends TableName<TSchema>>(tableName: TName, input: BatchDeleteInput<TSchema, TName>): Promise<DeleteBatchResult>;
|
|
781
|
+
bufferedWriter<TName extends TableName<TSchema>>(tableName: TName, options?: BufferedWriterOptions): BufferedTableWriter<BatchInsertRow<TSchema, TName>>;
|
|
782
|
+
readTable<TName extends TableName<TSchema>, const TColumns extends ReadonlyArray<BatchColumnName<TSchema, TName>>>(tableName: TName, options: BatchReadOptions<TSchema, TName, TColumns> & {
|
|
783
|
+
readonly columns: TColumns;
|
|
784
|
+
}): Promise<Array<Pick<BatchReadRow<TSchema, TName>, TColumns[number]>>>;
|
|
785
|
+
readTable<TName extends TableName<TSchema>>(tableName: TName, versionOrOptions?: number | BatchReadOptions<TSchema, TName>): Promise<Array<BatchReadRow<TSchema, TName>>>;
|
|
768
786
|
/**
|
|
769
787
|
* Runs the callback against one pinned manifest version: every query inside the scope
|
|
770
788
|
* observes the same committed state, however many commits land meanwhile. This is the
|
|
@@ -807,7 +825,7 @@ export declare class MinnowDatabase {
|
|
|
807
825
|
* compaction landing mid-scope never fails it. An error thrown by the callback aborts the
|
|
808
826
|
* scope with nothing published. A scope that stages nothing publishes nothing.
|
|
809
827
|
*/
|
|
810
|
-
write<T>(action: (session: WriteSession) => Promise<T>): Promise<{
|
|
828
|
+
write<T>(action: (session: WriteSession<TSchema>) => Promise<T>): Promise<{
|
|
811
829
|
result: T;
|
|
812
830
|
version: number | null;
|
|
813
831
|
}>;
|
|
@@ -824,7 +842,11 @@ export declare class MinnowDatabase {
|
|
|
824
842
|
* steps — and every catalog alteration is one atomic compare-and-swap, so a concurrent
|
|
825
843
|
* migrator fails explicitly with a conflict instead of interleaving.
|
|
826
844
|
*/
|
|
827
|
-
|
|
845
|
+
/**
|
|
846
|
+
* Brings storage in line with a schema declaration. With no argument it applies the schema the
|
|
847
|
+
* database was constructed with.
|
|
848
|
+
*/
|
|
849
|
+
migrate(definition?: SchemaDefinition<readonly AnyTable[]> | undefined, options?: MigrateOptions): Promise<MigrateResult>;
|
|
828
850
|
/**
|
|
829
851
|
* Renders the optimized logical plan for a SELECT statement plus the physical strategy notes
|
|
830
852
|
* the prepared execution would choose, without executing it.
|