@minnowdb/core 0.7.2 → 0.7.7

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.
@@ -3,6 +3,27 @@ import { verifyStoredBlock } from "../block-format/index.js";
3
3
  import { crc32 } from "../block-format/checksum.js";
4
4
  import { decodeSnapshotMetadataItems, encodeSnapshotFrameStreamFooter, encodeSnapshotFrameStreamHeader, encodeSnapshotMetadataPage, extendSnapshotFrameStreamChecksum, prepareSnapshotFrameStreamHeader, snapshotFrameEnvelopeParts, snapshotFrameStreamHeaderIdentity } from "./snapshot-stream.js";
5
5
  import { RecordCore, validateFtsPostingChunks, validateId, validateTempRunPage, validateTempRunPageIdentity } from "./toolkit/record-core.js";
6
+ const commitTurn = (() => {
7
+ const immediate = globalThis.setImmediate;
8
+ if (immediate !== void 0 && !Reflect.has(immediate, "clock")) {
9
+ return () => new Promise((resolve) => {
10
+ immediate(resolve);
11
+ });
12
+ }
13
+ if (typeof MessageChannel === "undefined") {
14
+ return () => new Promise((resolve) => setTimeout(resolve, 0));
15
+ }
16
+ let channel;
17
+ const waiting = [];
18
+ return () => new Promise((resolve) => {
19
+ if (channel === void 0) {
20
+ channel = new MessageChannel();
21
+ channel.port1.onmessage = () => waiting.shift()?.();
22
+ }
23
+ waiting.push(resolve);
24
+ channel.port2.postMessage(void 0);
25
+ });
26
+ })();
6
27
  class MemoryBlockStore {
7
28
  #blocks = /* @__PURE__ */ new Map();
8
29
  #blockChecksums = /* @__PURE__ */ new Map();
@@ -549,15 +570,19 @@ class MemoryBlockStore {
549
570
  });
550
571
  }
551
572
  async commitTransaction(input) {
552
- return this.#runAtomic(() => this.#core.commitTransaction(input));
573
+ const summary = await this.#runAtomic(() => this.#core.commitTransaction(input));
574
+ await commitTurn();
575
+ return summary;
553
576
  }
554
577
  async writeTransaction(input) {
555
- return this.#runAtomic(() => {
556
- const summary = this.#core.writeTransaction(input);
578
+ const summary = await this.#runAtomic(() => {
579
+ const summary2 = this.#core.writeTransaction(input);
557
580
  for (const block of input.blocks)
558
581
  this.#putBlock(block.id, block.bytes);
559
- return summary;
582
+ return summary2;
560
583
  });
584
+ await commitTurn();
585
+ return summary;
561
586
  }
562
587
  async createLease(record) {
563
588
  return this.#runAtomic(() => {
@@ -3567,7 +3567,12 @@ class RecordCore {
3567
3567
  throw new GarbageCollectionJobConflictError(input.jobId, input.expectedRevision, current?.revision ?? null);
3568
3568
  }
3569
3569
  const updated = updateGarbageCollectionPlanningRecord(current, input);
3570
- assertGarbageCollectionCandidateProvenance(updated, this.#manifests, this.#manifestBlocks, this.#segments, this.#transactions, this.#roots);
3570
+ assertGarbageCollectionCandidateProvenance({
3571
+ candidateManifestVersions: input.candidateManifestVersions ?? [],
3572
+ candidateSegmentIds: input.candidateSegmentIds ?? [],
3573
+ candidateBlockIds: input.candidateBlockIds ?? [],
3574
+ candidateTransactionIds: input.candidateTransactionIds ?? []
3575
+ }, this.#manifests, this.#manifestBlocks, this.#segments, this.#transactions, this.#roots);
3571
3576
  this.#garbageCollectionJobs.set(updated.id, updated);
3572
3577
  return cloneRecord(updated);
3573
3578
  }
@@ -5661,23 +5666,23 @@ function assertPendingArtifactsAvailable(transaction, physical, segments, valida
5661
5666
  }
5662
5667
  }
5663
5668
  }
5664
- function assertGarbageCollectionCandidateProvenance(job, manifests, manifestBlocks, segments, transactions, roots) {
5665
- for (const version of job.candidateManifestVersions) {
5669
+ function assertGarbageCollectionCandidateProvenance(candidates, manifests, manifestBlocks, segments, transactions, roots) {
5670
+ for (const version of candidates.candidateManifestVersions) {
5666
5671
  if (!manifests.has(version)) {
5667
5672
  throw new Error(`Garbage collection candidate manifest is missing: ${String(version)}`);
5668
5673
  }
5669
5674
  }
5670
- for (const id of job.candidateTransactionIds) {
5675
+ for (const id of candidates.candidateTransactionIds) {
5671
5676
  const transaction = transactions.get(id);
5672
5677
  if (transaction === void 0 || transaction.status !== "aborted" && (transaction.status !== "committed" || transaction.committedVersion === null)) {
5673
5678
  throw new Error(`Garbage collection transaction candidate is not terminal: ${id}`);
5674
5679
  }
5675
5680
  }
5676
- const unprovenBlockId = job.candidateBlockIds.find((id) => !manifestBlocks.has(id) && roots.abortedTransactionBlockCount(id) === 0 && roots.terminalJobBlockCount(id) === 0);
5681
+ const unprovenBlockId = candidates.candidateBlockIds.find((id) => !manifestBlocks.has(id) && roots.abortedTransactionBlockCount(id) === 0 && roots.terminalJobBlockCount(id) === 0);
5677
5682
  if (unprovenBlockId !== void 0) {
5678
5683
  throw new Error(`Garbage collection block candidate has no persisted provenance: ${unprovenBlockId}`);
5679
5684
  }
5680
- const unprovenSegmentId = job.candidateSegmentIds.find((id) => {
5685
+ const unprovenSegmentId = candidates.candidateSegmentIds.find((id) => {
5681
5686
  return !segments.has(id);
5682
5687
  });
5683
5688
  if (unprovenSegmentId !== void 0) {
@@ -815,6 +815,17 @@ export interface UpdateGarbageCollectionPlanningInput {
815
815
  discovery: GarbageCollectionDiscovery;
816
816
  updatedAt: string;
817
817
  }
818
+ /**
819
+ * One page's (or one job's full) proposed garbage-collection candidates, checked for persisted
820
+ * provenance before being accepted. Shared by every adapter's provenance assertion so a candidate
821
+ * kind added to one cannot be forgotten in another.
822
+ */
823
+ export interface GarbageCollectionCandidateSet {
824
+ readonly candidateManifestVersions: readonly number[];
825
+ readonly candidateSegmentIds: readonly string[];
826
+ readonly candidateBlockIds: readonly string[];
827
+ readonly candidateTransactionIds: readonly string[];
828
+ }
818
829
  export interface GarbageCollectionJobRecord {
819
830
  id: string;
820
831
  candidateManifestVersions: number[];
@@ -166,7 +166,9 @@ function parseExpected(lines, file, line) {
166
166
  return { kind: "hash", valueCount, hash: (match[2] ?? "").toLowerCase() };
167
167
  }
168
168
  function tokenizeDirective(line) {
169
- return line.trim().split(/\s+/u);
169
+ const hash = line.indexOf("#");
170
+ const directive = hash === -1 ? line : line.slice(0, hash);
171
+ return directive.trim().split(/\s+/u);
170
172
  }
171
173
  function sqlLines(lines) {
172
174
  return lines.map((line) => line.text).join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnowdb/core",
3
- "version": "0.7.2",
3
+ "version": "0.7.7",
4
4
  "description": "A columnar SQL database for the browser: PostgreSQL-style SQL over durable IndexedDB or OPFS data, with no server or WebAssembly module.",
5
5
  "license": "MIT",
6
6
  "author": "Eric Wilhite",
@@ -31,7 +31,7 @@
31
31
  {
32
32
  "id": "expression.arithmetic",
33
33
  "classification": "different",
34
- "reason": "Minnow has one JavaScript number type: integer-looking division stays fractional, and division by zero returns NULL instead of raising PostgreSQL's error."
34
+ "reason": "Division by zero returns NULL instead of raising PostgreSQL's error. Integer division itself follows PostgreSQL: two integer operands truncate toward zero."
35
35
  },
36
36
  {
37
37
  "id": "expression.round",
@@ -43,6 +43,11 @@
43
43
  "classification": "extension",
44
44
  "reason": "PostgreSQL uses $n parameters. Minnow also accepts ? as adapter-friendly shorthand."
45
45
  },
46
+ {
47
+ "id": "mutation.truncate",
48
+ "classification": "different",
49
+ "reason": "TRUNCATE reports the number of rows it removed, where PostgreSQL's command tag carries no count. The resulting table state is identical."
50
+ },
46
51
  {
47
52
  "id": "mutation.upsert-replace",
48
53
  "classification": "extension",
@@ -91,7 +96,7 @@
91
96
  {
92
97
  "id": "expression.modulo",
93
98
  "classification": "different",
94
- "reason": "Minnow permits % on its number type; PostgreSQL has no % operator for double precision."
99
+ "reason": "Minnow permits % on double precision values; PostgreSQL has no % operator for double precision."
95
100
  },
96
101
  {
97
102
  "id": "expression.date-trunc",
@@ -101,7 +106,7 @@
101
106
  {
102
107
  "id": "function.numeric-core",
103
108
  "classification": "different",
104
- "reason": "Minnow's one number type accepts combinations that PostgreSQL's distinct integer, numeric, and double-precision overloads do not."
109
+ "reason": "Minnow's numeric functions accept integer, exact, and double-precision arguments interchangeably, where PostgreSQL's distinct integer, numeric, and double-precision overloads do not."
105
110
  },
106
111
  {
107
112
  "id": "function.string-extended",
@@ -247,7 +252,7 @@
247
252
  {
248
253
  "id": "transaction.isolation-level",
249
254
  "classification": "inapplicable",
250
- "reason": "Minnow exposes one snapshot transaction model, so there is no session isolation level to configure."
255
+ "reason": "Every transaction reads one snapshot and commits atomically, which satisfies READ UNCOMMITTED, READ COMMITTED, and REPEATABLE READ, so those levels are accepted and ignored. SERIALIZABLE promises more than one snapshot can, and is refused rather than silently downgraded."
251
256
  },
252
257
  {
253
258
  "id": "privileges.grant",