@minnowdb/core 0.7.7 → 0.7.10

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, StagedUpsertResult, 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";
5
- import type { LiveQueryInput, LiveQueryInvalidation, LiveQueryObserveOptions, LiveQueryStats, LiveQuerySubscribeOptions } from "./live.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
+ import type { LiveQueryDelivery, 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
@@ -59,7 +65,7 @@ export interface ClientMigrationResult {
59
65
  steps: WireMigrationStep[];
60
66
  }
61
67
  interface EventRoute {
62
- onChange?: (result: QueryResult) => void;
68
+ onChange?: (result: QueryResult, delivery: LiveQueryDelivery) => void;
63
69
  onInvalidate?: (invalidation: LiveQueryInvalidation) => void;
64
70
  onError?: (error: unknown) => void;
65
71
  onComplete?: () => void;
@@ -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, options?: UpsertOptions): Promise<StagedUpsertResult>;
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;
@@ -424,7 +434,7 @@ class MinnowDatabaseClient {
424
434
  liveQueries(options = {}) {
425
435
  const handleId = crypto.randomUUID();
426
436
  const created = this.#call("liveQueries", [handleId, options]);
427
- return new ClientLiveQuerySet(this, handleId, created);
437
+ return new ClientLiveQuerySet(this.#erased, handleId, created);
428
438
  }
429
439
  async listVisibleSegmentPage(tableName, options) {
430
440
  return await this.#call("listVisibleSegmentPage", options === void 0 ? [tableName] : [tableName, options]);
@@ -546,9 +556,10 @@ class MinnowDatabaseClient {
546
556
  const route = this.#events.get(response.handleId);
547
557
  if (route === void 0)
548
558
  return;
549
- if (response.event === "change")
550
- route.onChange?.(decodeQueryResult(response.payload));
551
- else if (response.event === "invalidate") {
559
+ if (response.event === "change") {
560
+ const { result, delivery } = response.payload;
561
+ route.onChange?.(decodeQueryResult(result), delivery);
562
+ } else if (response.event === "invalidate") {
552
563
  route.onInvalidate?.(response.payload);
553
564
  } else if (response.event === "error") {
554
565
  route.onError?.(rehydrateResponseError(response.payload));
@@ -688,7 +699,8 @@ class ClientLiveQuerySet {
688
699
  try {
689
700
  const created = await this.client._invoke(this.handleId, "observe", [
690
701
  subscriptionId,
691
- query
702
+ query,
703
+ { suppressUnchanged: options.suppressUnchanged === true }
692
704
  ]);
693
705
  this.#subscriptionIds.add(subscriptionId);
694
706
  return new ClientLiveSubscription(this.client, subscriptionId, created.dependencyTableIds, () => this.#subscriptionIds.delete(subscriptionId), state);
@@ -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 {
@@ -189,7 +190,7 @@ export interface StagedUpsertResult extends StagedWriteResult {
189
190
  * caught the original error. A mutation that fails validation before registering anything
190
191
  * leaves the scope usable.
191
192
  */
192
- export interface WriteSession {
193
+ export interface WriteSession<TSchema extends AnySchema = UntypedSchema> {
193
194
  /**
194
195
  * Read-your-writes: the query observes the pre-scope snapshot PLUS everything this scope
195
196
  * has staged so far, ordered after all committed data — without publishing anything.
@@ -197,10 +198,10 @@ export interface WriteSession {
197
198
  query(sql: string, options?: QueryOptions): Promise<QueryResult>;
198
199
  /** Runs a SELECT, INSERT, UPDATE, or DELETE inside this write scope. */
199
200
  execute(sql: string, params?: readonly QueryValue[]): Promise<ExecuteResult>;
200
- insertBatch(tableName: string, input: InsertBatchInput): Promise<StagedWriteResult>;
201
- upsertBatch(tableName: string, input: InsertBatchInput, options?: UpsertOptions): Promise<StagedUpsertResult>;
202
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<StagedWriteResult>;
203
- 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>;
204
205
  }
205
206
  /** What one statement's execution cost, reported by the engine that ran it. */
206
207
  export interface QueryExecutionStats {
@@ -444,7 +445,13 @@ export interface TableDefinition {
444
445
  }
445
446
  export type DatabaseRow = Record<string, Exclude<BatchValue, null> | null>;
446
447
  export { CompactionBacklogError, CompactionJobCancelledError, CompactionMemoryBudgetError, CompactionWriteAmplificationError, MaintenanceBacklogError, MissingKeyError, SqlCompileError, UnknownTableError, TableInUseError, UniqueConstraintError, VisibleSegmentCursorStaleError, };
447
- 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;
448
455
  /**
449
456
  * Block codec for newly written blocks; defaults to "gzip", which is also what compaction
450
457
  * rewrites to, so a table's blocks are encoded the same way however they got there.
@@ -703,10 +710,10 @@ export interface SnapshotImportOptions {
703
710
  /** Cancels transfer and atomically removes this caller's staged import. */
704
711
  signal?: AbortSignal;
705
712
  }
706
- export declare class MinnowDatabase {
713
+ export declare class MinnowDatabase<TSchema extends AnySchema = UntypedSchema> {
707
714
  #private;
708
715
  private readonly store;
709
- constructor(store: BlockStore, options?: MinnowDatabaseOptions);
716
+ constructor(store: BlockStore, options?: MinnowDatabaseOptions<TSchema>);
710
717
  /**
711
718
  * Stops timers and background scheduling, rolls back an abandoned statement transaction,
712
719
  * closes live-query resources, releases the engine's reader lease, and drops resident caches.
@@ -758,17 +765,24 @@ export declare class MinnowDatabase {
758
765
  dropTable(tableName: string, options?: {
759
766
  ifExists?: boolean;
760
767
  }): Promise<boolean>;
761
- insertBatch(tableName: string, input: InsertBatchInput): Promise<InsertBatchResult>;
762
- insert(tableName: string, row: BatchRow): Promise<InsertBatchResult>;
763
- upsertBatch(tableName: string, input: InsertBatchInput, options?: UpsertOptions): Promise<UpsertBatchResult>;
764
- upsert(tableName: string, row: BatchRow, options?: UpsertOptions): Promise<UpsertBatchResult>;
765
- updateBatch(tableName: string, input: UpdateBatchInput): Promise<UpdateBatchResult>;
766
- 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>;
767
778
  /** Deletes one row by the table's unique key. */
768
- delete(tableName: string, key: Exclude<BatchValue, null>): Promise<DeleteBatchResult>;
769
- deleteBatch(tableName: string, input: DeleteBatchInput): Promise<DeleteBatchResult>;
770
- bufferedWriter(tableName: string, options?: BufferedWriterOptions): BufferedTableWriter;
771
- 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>>>;
772
786
  /**
773
787
  * Runs the callback against one pinned manifest version: every query inside the scope
774
788
  * observes the same committed state, however many commits land meanwhile. This is the
@@ -811,7 +825,7 @@ export declare class MinnowDatabase {
811
825
  * compaction landing mid-scope never fails it. An error thrown by the callback aborts the
812
826
  * scope with nothing published. A scope that stages nothing publishes nothing.
813
827
  */
814
- write<T>(action: (session: WriteSession) => Promise<T>): Promise<{
828
+ write<T>(action: (session: WriteSession<TSchema>) => Promise<T>): Promise<{
815
829
  result: T;
816
830
  version: number | null;
817
831
  }>;
@@ -828,7 +842,11 @@ export declare class MinnowDatabase {
828
842
  * steps — and every catalog alteration is one atomic compare-and-swap, so a concurrent
829
843
  * migrator fails explicitly with a conflict instead of interleaving.
830
844
  */
831
- 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>;
832
850
  /**
833
851
  * Renders the optimized logical plan for a SELECT statement plus the physical strategy notes
834
852
  * the prepared execution would choose, without executing it.