@minnowdb/core 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/engine/artifact-cache.js +3 -2
  2. package/dist/engine/buffered-writer.js +12 -2
  3. package/dist/engine/client.d.ts +8 -2
  4. package/dist/engine/client.js +106 -22
  5. package/dist/engine/database.d.ts +5 -3
  6. package/dist/engine/database.js +2044 -1674
  7. package/dist/engine/errors.d.ts +19 -0
  8. package/dist/engine/errors.js +29 -0
  9. package/dist/engine/index.d.ts +1 -0
  10. package/dist/engine/index.js +4 -0
  11. package/dist/engine/live-accept.js +13 -0
  12. package/dist/engine/live-aggregate.js +54 -16
  13. package/dist/engine/live.d.ts +2 -0
  14. package/dist/engine/live.js +109 -108
  15. package/dist/engine/query-cache.js +17 -2
  16. package/dist/engine/query.d.ts +1 -2
  17. package/dist/engine/query.js +4 -7
  18. package/dist/engine/result-state.d.ts +7 -0
  19. package/dist/engine/result-state.js +15 -0
  20. package/dist/engine/vector.d.ts +4 -0
  21. package/dist/engine/vector.js +14 -14
  22. package/dist/engine/windows.js +15 -14
  23. package/dist/engine/worker-server.d.ts +1 -1
  24. package/dist/engine/worker-server.js +28 -15
  25. package/dist/engine/write-coordinator.js +54 -0
  26. package/dist/storage/indexeddb.js +134 -89
  27. package/dist/storage/opfs/index.d.ts +1 -1
  28. package/dist/storage/opfs/index.js +3 -1
  29. package/dist/storage/opfs/leader.js +20 -9
  30. package/dist/storage/opfs/rpc.js +3 -1
  31. package/dist/storage/opfs/store.d.ts +2 -0
  32. package/dist/storage/opfs/store.js +128 -40
  33. package/dist/storage/toolkit/record-core.js +2 -1
  34. package/dist/storage/types.d.ts +14 -0
  35. package/dist/storage/types.js +21 -0
  36. package/dist/transactions/index.d.ts +3 -0
  37. package/dist/transactions/index.js +4 -1
  38. package/dist/worker-protocol/index.d.ts +1 -1
  39. package/dist/worker-protocol/index.js +1 -1
  40. package/package.json +2 -2
  41. package/dist/date-value.d.ts +0 -20
  42. package/dist/engine/artifact-cache.d.ts +0 -29
  43. package/dist/engine/byte-estimates.d.ts +0 -11
  44. package/dist/engine/cancellation.d.ts +0 -2
  45. package/dist/engine/defaults.d.ts +0 -29
  46. package/dist/engine/group-index.d.ts +0 -33
  47. package/dist/engine/join-index.d.ts +0 -10
  48. package/dist/engine/live-aggregate.d.ts +0 -12
  49. package/dist/engine/live-equal.d.ts +0 -7
  50. package/dist/engine/point-read.d.ts +0 -59
  51. package/dist/engine/query-cache.d.ts +0 -21
  52. package/dist/engine/query-generations.d.ts +0 -8
  53. package/dist/engine/query-identity.d.ts +0 -3
  54. package/dist/engine/result-wire.d.ts +0 -70
  55. package/dist/engine/sort-keys.d.ts +0 -73
  56. package/dist/engine/sql-domains.d.ts +0 -97
  57. package/dist/engine/sql-functions.d.ts +0 -11
  58. package/dist/engine/sql-json.d.ts +0 -40
  59. package/dist/engine/sql-semantics.d.ts +0 -66
  60. package/dist/engine/worker-store-indexeddb.d.ts +0 -2
  61. package/dist/engine/worker-store-memory.d.ts +0 -2
  62. package/dist/engine/worker-store-opfs.d.ts +0 -2
  63. package/dist/engine/write-block-planner.d.ts +0 -19
  64. package/dist/storage/opfs/leader.d.ts +0 -460
  65. package/dist/storage/opfs/rpc.d.ts +0 -82
  66. package/dist/storage/opfs/snapshot-ledger.d.ts +0 -41
@@ -3,6 +3,25 @@
3
3
  * them (name, fields, instanceof) without bundling the executor.
4
4
  */
5
5
  type ErrorValue = boolean | number | string | Date;
6
+ /** An idle SQL transaction rolled back; later statements must not become autocommits. */
7
+ export declare class TransactionExpiredError extends Error {
8
+ readonly name = "TransactionExpiredError";
9
+ constructor();
10
+ }
11
+ /** The worker stopped responding within the configured request deadline. */
12
+ export declare class DatabaseWorkerTimeoutError extends Error {
13
+ readonly method: string;
14
+ readonly timeoutMs: number;
15
+ readonly name = "DatabaseWorkerTimeoutError";
16
+ constructor(method: string, timeoutMs: number);
17
+ }
18
+ /** A transport failure cannot prove whether this operation published before its reply was lost. */
19
+ export declare class DatabaseWorkerOutcomeUnknownError extends Error {
20
+ readonly method: string;
21
+ readonly requestId: string;
22
+ readonly name = "DatabaseWorkerOutcomeUnknownError";
23
+ constructor(method: string, requestId: string, options?: ErrorOptions);
24
+ }
6
25
  /** A database operation named a table that is not present in the current catalog. */
7
26
  export declare class UnknownTableError extends TypeError {
8
27
  readonly tableName: string;
@@ -1,3 +1,29 @@
1
+ class TransactionExpiredError extends Error {
2
+ name = "TransactionExpiredError";
3
+ constructor() {
4
+ super("The SQL transaction expired and rolled back; issue ROLLBACK or BEGIN before continuing");
5
+ }
6
+ }
7
+ class DatabaseWorkerTimeoutError extends Error {
8
+ method;
9
+ timeoutMs;
10
+ name = "DatabaseWorkerTimeoutError";
11
+ constructor(method, timeoutMs) {
12
+ super(`Database worker ${method} did not respond within ${String(timeoutMs)}ms`);
13
+ this.method = method;
14
+ this.timeoutMs = timeoutMs;
15
+ }
16
+ }
17
+ class DatabaseWorkerOutcomeUnknownError extends Error {
18
+ method;
19
+ requestId;
20
+ name = "DatabaseWorkerOutcomeUnknownError";
21
+ constructor(method, requestId, options) {
22
+ super(`Database worker outcome is unknown for ${method}; reconcile durable application IDs before retrying`, options);
23
+ this.method = method;
24
+ this.requestId = requestId;
25
+ }
26
+ }
1
27
  function formatValue(value) {
2
28
  return value instanceof Date ? dateIsoString(value) : String(value);
3
29
  }
@@ -117,10 +143,13 @@ export {
117
143
  CompactionMemoryBudgetError,
118
144
  CompactionWriteAmplificationError,
119
145
  DatabaseReadBacklogError,
146
+ DatabaseWorkerOutcomeUnknownError,
147
+ DatabaseWorkerTimeoutError,
120
148
  LiveQueryLimitError,
121
149
  MaintenanceBacklogError,
122
150
  MissingKeyError,
123
151
  SqlCompileError,
152
+ TransactionExpiredError,
124
153
  UniqueConstraintError,
125
154
  UnknownTableError,
126
155
  VisibleSegmentCursorStaleError
@@ -17,3 +17,4 @@ export type * from "./schema.js";
17
17
  export type { InsertValue, CompiledQuery, CompiledStatement, QueryResult, QueryRow, QueryExecutionOptions, QueryValue, SqlColumnSchema, SqlColumnType, } from "./query.js";
18
18
  export type { AsyncQueryExecutionOptions, QueryBatchExecutionOptions, QuerySpillStore, } from "./vector.js";
19
19
  export type { MinnowSqlDriver, MinnowSqlExecutor } from "./sql-driver.js";
20
+ export { OpfsCoordinationError, OpfsDatabaseInUseError, OpfsUncertainOutcomeError, } from "../storage/types.js";
@@ -4,6 +4,7 @@ export * from "./errors.js";
4
4
  import { QueryMemoryBudgetError } from "./memory.js";
5
5
  import { MAX_SQL_NESTING_DEPTH, MAX_SQL_NUMERIC_DIGITS, MAX_SQL_PARAMETERS, MAX_SQL_PATTERN_CHARACTERS, MAX_SQL_PATTERN_MATCH_STEPS, MAX_SQL_SCALAR_RESULT_CHARACTERS, MAX_SQL_STRUCTURED_VALUE_DEPTH, MAX_SQL_STRUCTURED_VALUE_ITEMS, MAX_SQL_TEXT_CHARACTERS, MAX_SQL_TOKENS } from "./cache-limits.js";
6
6
  import { column, foreignKeyName, isDestructiveStep, planMigration, schema, table, view } from "./schema.js";
7
+ import { OpfsCoordinationError, OpfsDatabaseInUseError, OpfsUncertainOutcomeError } from "../storage/types.js";
7
8
  export {
8
9
  MAX_SQL_NESTING_DEPTH,
9
10
  MAX_SQL_NUMERIC_DIGITS,
@@ -15,6 +16,9 @@ export {
15
16
  MAX_SQL_STRUCTURED_VALUE_ITEMS,
16
17
  MAX_SQL_TEXT_CHARACTERS,
17
18
  MAX_SQL_TOKENS,
19
+ OpfsCoordinationError,
20
+ OpfsDatabaseInUseError,
21
+ OpfsUncertainOutcomeError,
18
22
  QueryMemoryBudgetError,
19
23
  column,
20
24
  foreignKeyName,
@@ -0,0 +1,13 @@
1
+ const publications = /* @__PURE__ */ new WeakMap();
2
+ function stageLiveExecution(execution, publish) {
3
+ publications.set(execution, publish);
4
+ return execution;
5
+ }
6
+ function acceptLiveExecution(execution) {
7
+ publications.get(execution)?.();
8
+ publications.delete(execution);
9
+ }
10
+ export {
11
+ acceptLiveExecution,
12
+ stageLiveExecution
13
+ };
@@ -2,23 +2,35 @@ import { childExpressions, hasAggregate, mapChildExpressions, executeRowQueryInt
2
2
  import { encodeQueryIdentity } from "./query-identity.js";
3
3
  import { exactNumericBinary, exactNumericValue, externalSqlDomainValue, isExactNumeric } from "./sql-domains.js";
4
4
  import { encodeSqlEqualityValue } from "./sql-semantics.js";
5
+ function contributionBytes(key, row) {
6
+ return 64 + key.length * 2 + row.group.length * 2 + row.values.reduce((sum, value) => sum + (typeof value === "string" ? value.length * 2 + 16 : 16), 0);
7
+ }
8
+ function groupBytes(key, group) {
9
+ return 96 + key.length * 2 + group.sums.reduce((sum, value) => sum + value.length * 2 + 24, 0);
10
+ }
5
11
  class LiveAggregate {
6
12
  inputPlan;
7
13
  #outputPlan;
8
14
  #aggregates;
9
15
  #groupAliases;
10
- #rows;
16
+ #contributions;
17
+ #revision;
18
+ #pending;
19
+ #bytes;
11
20
  #groups;
12
21
  #domains;
13
22
  keyAlias;
14
23
  #schema;
15
- constructor(inputPlan, outputPlan, aggregates, groupAliases, keyAlias, rows = /* @__PURE__ */ new Map(), groups = /* @__PURE__ */ new Map(), domains = [], schema = []) {
24
+ constructor(inputPlan, outputPlan, aggregates, groupAliases, keyAlias, contributions = { rows: /* @__PURE__ */ new Map(), revision: 0 }, groups = /* @__PURE__ */ new Map(), domains = [], schema = [], bytes = 256 + encodeQueryIdentity(inputPlan).length * 2 + encodeQueryIdentity(outputPlan).length * 2, pending) {
16
25
  this.inputPlan = inputPlan;
17
26
  this.#outputPlan = outputPlan;
18
27
  this.#aggregates = aggregates;
19
28
  this.#groupAliases = groupAliases;
20
29
  this.keyAlias = keyAlias;
21
- this.#rows = rows;
30
+ this.#contributions = contributions;
31
+ this.#revision = contributions.revision;
32
+ this.#pending = pending;
33
+ this.#bytes = bytes;
22
34
  this.#groups = groups;
23
35
  this.#domains = domains;
24
36
  this.#schema = schema;
@@ -38,7 +50,7 @@ class LiveAggregate {
38
50
  if (grouped !== void 0)
39
51
  return { kind: "column", reference: grouped };
40
52
  if (expression.kind === "call" && hasAggregate(expression)) {
41
- if (!["COUNT", "SUM", "AVG"].includes(expression.name) || expression.arguments.length !== 1)
53
+ if (!["COUNT", "SUM", "AVG"].includes(expression.name) || expression.arguments.length !== 1 || expression.distinct === true)
42
54
  throw new TypeError("Not an additive live aggregate");
43
55
  const argument = expression.arguments[0];
44
56
  if (argument === void 0 || !(rowLocal(argument) || expression.name === "COUNT" && argument.kind === "wildcard"))
@@ -102,11 +114,17 @@ class LiveAggregate {
102
114
  }
103
115
  }
104
116
  patch(result, changed, token) {
105
- const rows = new Map(this.#rows);
117
+ if (this.#pending !== void 0 || this.#revision !== this.#contributions.revision)
118
+ throw new Error("Unaccepted or stale aggregate");
119
+ const rows = this.#contributions.rows;
120
+ const pending = /* @__PURE__ */ new Map();
121
+ let bytes = this.#bytes;
106
122
  const groups = new Map(this.#groups);
107
123
  const touched = /* @__PURE__ */ new Set();
108
124
  const groupFor = (key, keys = []) => {
109
125
  let group = groups.get(key);
126
+ if (group !== void 0 && !touched.has(key))
127
+ bytes -= groupBytes(key, group);
110
128
  if (group === void 0) {
111
129
  group = {
112
130
  keys,
@@ -158,15 +176,20 @@ class LiveAggregate {
158
176
  if (old === void 0)
159
177
  continue;
160
178
  apply(groupFor(old.group), old.values, -1);
161
- rows.delete(key);
179
+ pending.set(key, void 0);
180
+ bytes -= contributionBytes(key, old);
162
181
  }
163
182
  for (const row of result.rows) {
164
183
  const key = token(row[this.keyAlias] ?? null);
184
+ if (pending.get(key) !== void 0 || rows.has(key) && !changed.has(key))
185
+ throw new TypeError("Duplicate live input key");
165
186
  const keys = this.#groupAliases.map((alias) => row[alias] ?? null);
166
187
  const group = JSON.stringify(keys.map(encodeSqlEqualityValue));
167
188
  const values = this.#aggregates.map(({ alias }) => row[alias] ?? null);
168
189
  apply(groupFor(group, keys), values, 1);
169
- rows.set(key, { group, values });
190
+ const contribution = { group, values };
191
+ pending.set(key, contribution);
192
+ bytes += contributionBytes(key, contribution);
170
193
  }
171
194
  if (this.#groupAliases.length === 0)
172
195
  groupFor("[]");
@@ -174,17 +197,37 @@ class LiveAggregate {
174
197
  for (const key of touched)
175
198
  if (groups.get(key)?.members === 0)
176
199
  groups.delete(key);
200
+ for (const key of touched) {
201
+ const group = groups.get(key);
202
+ if (group !== void 0)
203
+ bytes += groupBytes(key, group);
204
+ }
177
205
  const domains = this.#aggregates.map(({ alias }, index) => result.columnDomains[result.columns.indexOf(alias)] ?? this.#domains[index] ?? null);
178
- return new LiveAggregate(this.inputPlan, this.#outputPlan, this.#aggregates, this.#groupAliases, this.keyAlias, rows, groups, domains, result.columns.map((name, index) => {
206
+ return new LiveAggregate(this.inputPlan, this.#outputPlan, this.#aggregates, this.#groupAliases, this.keyAlias, this.#contributions, groups, domains, result.columns.map((name, index) => {
179
207
  const domain = result.columnDomains[index];
180
208
  const value = result.rows.find((row) => row[name] !== null)?.[name];
181
209
  const aggregate = this.#aggregates.find((item) => item.alias === name);
182
210
  return {
183
211
  name,
184
- type: aggregate?.name === "COUNT" ? "number" : domain !== null && domain !== void 0 ? "string" : typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : value instanceof Date ? "datetime" : "string",
212
+ type: aggregate?.name === "COUNT" ? "number" : domain !== null && domain !== void 0 ? "string" : typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : value instanceof Date ? "datetime" : value === void 0 || value === null ? this.#schema[index]?.type ?? "string" : "string",
185
213
  ...domain === null || domain === void 0 || aggregate?.name === "COUNT" ? {} : { sqlDomain: domain }
186
214
  };
187
- }));
215
+ }), bytes, pending);
216
+ }
217
+ accept() {
218
+ const pending = this.#pending;
219
+ if (pending === void 0)
220
+ return;
221
+ if (this.#revision !== this.#contributions.revision)
222
+ throw new Error("Stale aggregate");
223
+ for (const [key, contribution] of pending) {
224
+ if (contribution === void 0)
225
+ this.#contributions.rows.delete(key);
226
+ else
227
+ this.#contributions.rows.set(key, contribution);
228
+ }
229
+ this.#revision = ++this.#contributions.revision;
230
+ this.#pending = void 0;
188
231
  }
189
232
  result() {
190
233
  const rows = [];
@@ -213,12 +256,7 @@ class LiveAggregate {
213
256
  return externalizeQueryResult(result);
214
257
  }
215
258
  get retainedBytes() {
216
- let bytes = 256 + encodeQueryIdentity(this.inputPlan).length * 2 + encodeQueryIdentity(this.#outputPlan).length * 2;
217
- for (const [key, row] of this.#rows)
218
- bytes += 64 + key.length * 2 + row.group.length * 2 + row.values.reduce((sum, value) => sum + (typeof value === "string" ? value.length * 2 + 16 : 16), 0);
219
- for (const [key, group] of this.#groups)
220
- bytes += 96 + key.length * 2 + group.sums.reduce((sum, value) => sum + value.length * 2 + 24, 0);
221
- return bytes;
259
+ return this.#bytes;
222
260
  }
223
261
  }
224
262
  export {
@@ -169,6 +169,8 @@ export interface LiveQueryHost {
169
169
  dependencyTableIds(query: LiveQueryInput, probe?: CatalogProbe): Promise<Set<string>>;
170
170
  /** Executes the statement; the returned result belongs to the set and is never shared. */
171
171
  execute(query: LiveQueryInput, context?: LiveQueryExecuteContext): Promise<QueryResult>;
172
+ /** Optionally publishes an accepted result for adapters that re-execute after invalidation. */
173
+ memoize?(query: LiveQueryInput, result: QueryResult, probe: CatalogProbe): Promise<void>;
172
174
  /**
173
175
  * Executes a statement the host can later maintain incrementally, or returns undefined when
174
176
  * the statement's shape rules that out; the set then executes it in full from then on.