@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.
@@ -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: InsertBatchInput): ColumnarBatch;
48
+ export declare function toColumnarBatch(input: InsertBatchInputLike): ColumnarBatch;
@@ -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
- return input;
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 { BatchValue } from "./batch.js";
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
- /** Batches row-oriented writes by row count, estimated bytes, or age. */
30
- export declare class BufferedTableWriter {
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: Readonly<Record<string, BatchValue>>): Promise<BufferedFlushResult | undefined>;
40
+ add(row: TRow): Promise<BufferedFlushResult | undefined>;
38
41
  flush(): Promise<BufferedFlushResult | undefined>;
39
42
  requestFlush(): void;
40
43
  close(): Promise<BufferedFlushResult | undefined>;
@@ -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, type InsertBatchInput } from "./batch.js";
2
+ import { type BatchRow } from "./batch.js";
3
3
  import type { Catalog } from "./catalog.js";
4
- import type { BatchValue, BufferPoolStats, StagedWriteResult, BufferedFlushResult, BufferedWriterOptions, CancelCompactionJobResult, CollectGarbageOptions, CollectGarbageStepOptions, CompactTableOptions, CompactTableResult, CompactTableStepOptions, CompactionJobProgress, CreateTableInput, DatabaseRow, MigrateOptions, DeleteBatchInput, DeleteBatchResult, ExecuteOptions, ExecuteResult, GarbageCollectionProgress, GarbageCollectionResult, MaintenanceStatus, InsertBatchResult, QueryOptions, QueryExecutionStats, QueryCursorOptions, QuerySpillCleanupOptions, QuerySpillCleanupResult, ReadTableOptions, RunStatementOptions, SnapshotExportOptions, SnapshotImportOptions, TableDefinition, UpdateBatchInput, UpdateBatchResult, UpsertBatchResult, UpsertOptions, VisibleSegmentPage, VisibleSegmentPageOptions } from "./database.js";
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
- migrate(definition: SchemaDefinition<readonly AnyTable[]>, options?: MigrateOptions): Promise<ClientMigrationResult>;
107
- insertBatch(tableName: string, input: InsertBatchInput): Promise<InsertBatchResult>;
108
- insert(tableName: string, row: BatchRow): Promise<InsertBatchResult>;
109
- upsertBatch(tableName: string, input: InsertBatchInput, options?: UpsertOptions): Promise<UpsertBatchResult>;
110
- upsert(tableName: string, row: BatchRow, options?: UpsertOptions): Promise<UpsertBatchResult>;
111
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<UpdateBatchResult>;
112
- update(tableName: string, key: Exclude<BatchValue, null>, changes: Readonly<Record<string, BatchValue>>): Promise<UpdateBatchResult>;
113
- deleteBatch(tableName: string, input: DeleteBatchInput): Promise<DeleteBatchResult>;
114
- delete(tableName: string, key: Exclude<BatchValue, null>): Promise<DeleteBatchResult>;
115
- bufferedWriter(tableName: string, options?: BufferedWriterOptions): ClientBufferedWriter;
116
- readTable(tableName: string, versionOrOptions?: number | ReadTableOptions): Promise<DatabaseRow[]>;
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: string, input: InsertBatchInput): Promise<StagedWriteResult>;
213
- upsertBatch(tableName: string, input: InsertBatchInput): Promise<StagedWriteResult>;
214
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<StagedWriteResult>;
215
- deleteBatch(tableName: string, input: DeleteBatchInput): Promise<StagedWriteResult>;
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: Readonly<Record<string, BatchValue>>): Promise<BufferedFlushResult | undefined>;
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;
@@ -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
- return await this.#call("updateBatch", [tableName, input]);
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
- return await this.#call("update", [tableName, key, changes]);
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", [op, tableName, input]);
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 BatchRow, type BatchValue, type InsertBatchInput } from "./batch.js";
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: string, input: InsertBatchInput): Promise<StagedWriteResult>;
197
- upsertBatch(tableName: string, input: InsertBatchInput): Promise<StagedWriteResult>;
198
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<StagedWriteResult>;
199
- deleteBatch(tableName: string, input: DeleteBatchInput): Promise<StagedWriteResult>;
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: string, input: InsertBatchInput): Promise<InsertBatchResult>;
758
- insert(tableName: string, row: BatchRow): Promise<InsertBatchResult>;
759
- upsertBatch(tableName: string, input: InsertBatchInput, options?: UpsertOptions): Promise<UpsertBatchResult>;
760
- upsert(tableName: string, row: BatchRow, options?: UpsertOptions): Promise<UpsertBatchResult>;
761
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<UpdateBatchResult>;
762
- update(tableName: string, key: Exclude<BatchValue, null>, changes: Readonly<Record<string, BatchValue>>): Promise<UpdateBatchResult>;
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: string, key: Exclude<BatchValue, null>): Promise<DeleteBatchResult>;
765
- deleteBatch(tableName: string, input: DeleteBatchInput): Promise<DeleteBatchResult>;
766
- bufferedWriter(tableName: string, options?: BufferedWriterOptions): BufferedTableWriter;
767
- readTable(tableName: string, versionOrOptions?: number | ReadTableOptions): Promise<DatabaseRow[]>;
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
- migrate(definition: SchemaDefinition<readonly AnyTable[]>, options?: MigrateOptions): Promise<MigrateResult>;
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.