@minnowdb/core 0.7.0 → 0.7.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 (95) hide show
  1. package/dist/block-format/block.js +254 -325
  2. package/dist/block-format/checksum.js +30 -52
  3. package/dist/block-format/codecs.js +112 -125
  4. package/dist/block-format/column.js +187 -205
  5. package/dist/block-format/index.js +5 -1
  6. package/dist/block-format/physical.js +371 -387
  7. package/dist/block-format/types.js +4 -1
  8. package/dist/block-format/unicode.js +30 -39
  9. package/dist/date-value.js +67 -78
  10. package/dist/engine/artifact-cache.js +106 -116
  11. package/dist/engine/batch.js +36 -44
  12. package/dist/engine/buffered-writer.js +156 -158
  13. package/dist/engine/byte-estimates.js +20 -22
  14. package/dist/engine/cache-limits.js +24 -25
  15. package/dist/engine/cancellation.js +5 -3
  16. package/dist/engine/catalog.js +64 -73
  17. package/dist/engine/client.js +715 -780
  18. package/dist/engine/database.js +15941 -19313
  19. package/dist/engine/defaults.js +104 -111
  20. package/dist/engine/errors.js +113 -128
  21. package/dist/engine/fts.js +233 -280
  22. package/dist/engine/group-index.js +287 -321
  23. package/dist/engine/index.js +23 -12
  24. package/dist/engine/join-index.js +177 -186
  25. package/dist/engine/keyed-live.js +168 -173
  26. package/dist/engine/live-api.js +0 -1
  27. package/dist/engine/live-equal.js +32 -37
  28. package/dist/engine/live.js +603 -647
  29. package/dist/engine/memory.js +109 -111
  30. package/dist/engine/optimizer.d.ts +19 -1
  31. package/dist/engine/optimizer.js +2633 -2934
  32. package/dist/engine/point-read.js +122 -182
  33. package/dist/engine/query-api.js +12 -3
  34. package/dist/engine/query-cache.js +61 -69
  35. package/dist/engine/query.d.ts +6 -0
  36. package/dist/engine/query.js +8503 -9980
  37. package/dist/engine/result-wire.js +221 -252
  38. package/dist/engine/schema-wire.js +97 -112
  39. package/dist/engine/schema.js +1099 -1254
  40. package/dist/engine/sort-keys.js +315 -389
  41. package/dist/engine/sql-domains.js +646 -769
  42. package/dist/engine/sql-driver.js +0 -1
  43. package/dist/engine/sql-functions.js +1031 -1125
  44. package/dist/engine/sql-json.js +199 -218
  45. package/dist/engine/sql-semantics.js +572 -639
  46. package/dist/engine/typed-live.js +271 -293
  47. package/dist/engine/vector.js +4484 -5145
  48. package/dist/engine/worker-host.d.ts +8 -63
  49. package/dist/engine/worker-host.js +28 -1130
  50. package/dist/engine/worker-indexeddb.d.ts +1 -0
  51. package/dist/engine/worker-indexeddb.js +3 -0
  52. package/dist/engine/worker-memory.d.ts +1 -0
  53. package/dist/engine/worker-memory.js +3 -0
  54. package/dist/engine/worker-opfs.d.ts +1 -0
  55. package/dist/engine/worker-opfs.js +3 -0
  56. package/dist/engine/worker-server.d.ts +93 -0
  57. package/dist/engine/worker-server.js +999 -0
  58. package/dist/engine/worker-store-indexeddb.d.ts +2 -0
  59. package/dist/engine/worker-store-indexeddb.js +11 -0
  60. package/dist/engine/worker-store-memory.d.ts +2 -0
  61. package/dist/engine/worker-store-memory.js +6 -0
  62. package/dist/engine/worker-store-opfs.d.ts +2 -0
  63. package/dist/engine/worker-store-opfs.js +9 -0
  64. package/dist/engine/worker.js +0 -12
  65. package/dist/engine/write-block-planner.js +93 -100
  66. package/dist/plan/index.js +15 -16
  67. package/dist/plan/model.d.ts +6 -0
  68. package/dist/plan/model.js +20 -27
  69. package/dist/storage/index.js +0 -12
  70. package/dist/storage/indexeddb.js +11776 -13171
  71. package/dist/storage/memory.js +1198 -1241
  72. package/dist/storage/opfs/files.js +238 -295
  73. package/dist/storage/opfs/index.js +9 -3
  74. package/dist/storage/opfs/leader.js +4386 -4856
  75. package/dist/storage/opfs/rpc.js +223 -259
  76. package/dist/storage/opfs/snapshot-ledger.js +219 -260
  77. package/dist/storage/opfs/store.js +884 -1003
  78. package/dist/storage/persistence.js +59 -74
  79. package/dist/storage/snapshot-stream.js +788 -826
  80. package/dist/storage/snapshot.js +36 -37
  81. package/dist/storage/toolkit/extents.js +444 -520
  82. package/dist/storage/toolkit/index.js +28 -29
  83. package/dist/storage/toolkit/record-core.js +5792 -6377
  84. package/dist/storage/toolkit/sync-file.js +34 -42
  85. package/dist/storage/toolkit/wal.js +92 -127
  86. package/dist/storage/toolkit/wire.js +232 -256
  87. package/dist/storage/types.js +2948 -3250
  88. package/dist/testing/block-store-conformance.js +1556 -1629
  89. package/dist/testing/index.js +310 -291
  90. package/dist/testing/opfs-shim.js +280 -312
  91. package/dist/testing/simulator.js +496 -534
  92. package/dist/testing/sqllogictest.js +425 -365
  93. package/dist/transactions/index.js +1464 -1712
  94. package/dist/worker-protocol/index.js +70 -69
  95. package/package.json +20 -2
@@ -1,50 +1,42 @@
1
- /**
2
- * Writes the complete buffer, retrying the short transfers permitted by sync-access handles.
3
- * The common path is one `write` call; retries pass subarray views and never copy payload bytes.
4
- */
5
- export function writeFully(handle, buffer, at, operation = "writing file bytes") {
6
- assertTransferRange(at, buffer.byteLength, "write", operation);
7
- let transferred = 0;
8
- while (transferred < buffer.byteLength) {
9
- const remaining = buffer.byteLength - transferred;
10
- const chunk = transferred === 0 ? buffer : buffer.subarray(transferred);
11
- const count = handle.write(chunk, { at: at + transferred });
12
- assertTransferCount(count, remaining, "write", operation, at + transferred);
13
- if (count === 0) {
14
- throw new Error(`OPFS write made no progress while ${operation}: wrote ${String(transferred)} of ` +
15
- `${String(buffer.byteLength)} bytes (stalled at file offset ${String(at + transferred)})`);
16
- }
17
- transferred += count;
1
+ function writeFully(handle, buffer, at, operation = "writing file bytes") {
2
+ assertTransferRange(at, buffer.byteLength, "write", operation);
3
+ let transferred = 0;
4
+ while (transferred < buffer.byteLength) {
5
+ const remaining = buffer.byteLength - transferred;
6
+ const chunk = transferred === 0 ? buffer : buffer.subarray(transferred);
7
+ const count = handle.write(chunk, { at: at + transferred });
8
+ assertTransferCount(count, remaining, "write", operation, at + transferred);
9
+ if (count === 0) {
10
+ throw new Error(`OPFS write made no progress while ${operation}: wrote ${String(transferred)} of ${String(buffer.byteLength)} bytes (stalled at file offset ${String(at + transferred)})`);
18
11
  }
12
+ transferred += count;
13
+ }
19
14
  }
20
- /**
21
- * Reads exactly `buffer.byteLength` bytes, retrying short transfers without copying data. A zero
22
- * transfer before the requested range is complete is an unexpected EOF, not a successful read.
23
- */
24
- export function readFully(handle, buffer, at, operation = "reading file bytes") {
25
- assertTransferRange(at, buffer.byteLength, "read", operation);
26
- let transferred = 0;
27
- while (transferred < buffer.byteLength) {
28
- const remaining = buffer.byteLength - transferred;
29
- const chunk = transferred === 0 ? buffer : buffer.subarray(transferred);
30
- const count = handle.read(chunk, { at: at + transferred });
31
- assertTransferCount(count, remaining, "read", operation, at + transferred);
32
- if (count === 0) {
33
- throw new Error(`Unexpected EOF while ${operation}: read ${String(transferred)} of ` +
34
- `${String(buffer.byteLength)} bytes (EOF at file offset ${String(at + transferred)})`);
35
- }
36
- transferred += count;
15
+ function readFully(handle, buffer, at, operation = "reading file bytes") {
16
+ assertTransferRange(at, buffer.byteLength, "read", operation);
17
+ let transferred = 0;
18
+ while (transferred < buffer.byteLength) {
19
+ const remaining = buffer.byteLength - transferred;
20
+ const chunk = transferred === 0 ? buffer : buffer.subarray(transferred);
21
+ const count = handle.read(chunk, { at: at + transferred });
22
+ assertTransferCount(count, remaining, "read", operation, at + transferred);
23
+ if (count === 0) {
24
+ throw new Error(`Unexpected EOF while ${operation}: read ${String(transferred)} of ${String(buffer.byteLength)} bytes (EOF at file offset ${String(at + transferred)})`);
37
25
  }
26
+ transferred += count;
27
+ }
38
28
  }
39
29
  function assertTransferRange(at, length, kind, operation) {
40
- if (!Number.isSafeInteger(at) || at < 0 || length > Number.MAX_SAFE_INTEGER - at) {
41
- throw new RangeError(`Invalid OPFS ${kind} range while ${operation}: ${String(at)}+${String(length)} ` +
42
- `must be a non-negative safe-integer byte range`);
43
- }
30
+ if (!Number.isSafeInteger(at) || at < 0 || length > Number.MAX_SAFE_INTEGER - at) {
31
+ throw new RangeError(`Invalid OPFS ${kind} range while ${operation}: ${String(at)}+${String(length)} must be a non-negative safe-integer byte range`);
32
+ }
44
33
  }
45
34
  function assertTransferCount(count, remaining, kind, operation, at) {
46
- if (!Number.isSafeInteger(count) || count < 0 || count > remaining) {
47
- throw new Error(`Invalid OPFS ${kind} result while ${operation}: handle reported ${String(count)} bytes ` +
48
- `for a ${String(remaining)}-byte request at file offset ${String(at)}`);
49
- }
35
+ if (!Number.isSafeInteger(count) || count < 0 || count > remaining) {
36
+ throw new Error(`Invalid OPFS ${kind} result while ${operation}: handle reported ${String(count)} bytes for a ${String(remaining)}-byte request at file offset ${String(at)}`);
37
+ }
50
38
  }
39
+ export {
40
+ readFully,
41
+ writeFully
42
+ };
@@ -1,141 +1,106 @@
1
1
  import { crc32 } from "../../block-format/index.js";
2
2
  import { decodeRecordJson, encodeRecordJson } from "./wire.js";
3
3
  import { readFully, writeFully } from "./sync-file.js";
4
- /**
5
- * The write-ahead log, framed over one synchronous file handle its single writer holds open.
6
- *
7
- * Frame layout, little-endian: `u32 magic | u32 payloadLength | u32 crc32(payload) | payload`.
8
- * An append is one complete synchronous transfer at the tail — normally one microsecond-scale
9
- * write on a held handle, with retries if the platform reports a short transfer. The only
10
- * artifact a crash can leave is a truncated final frame, which reads as "not written". A
11
- * complete frame with foreign marker or checksum-invalid bytes is corruption and fails closed.
12
- * Frames carry a global sequence number inside the payload; the file is truncated to zero only
13
- * after a checkpoint covering every frame has been flushed, so replay is always
14
- * newest-checkpoint-plus-tail.
15
- */
16
- const FRAME_MAGIC = 0x4c574e4d; // "MNWL"
4
+ const FRAME_MAGIC = 1280790093;
17
5
  const FRAME_HEADER_BYTES = 12;
18
- /**
19
- * A WAL frame is control data, never bulk block data. Bounding it prevents a corrupt length
20
- * field (or an accidentally enormous catalog mutation) from turning recovery into an
21
- * unbounded allocation. Snapshot/block payloads live in extents, so 64 MiB leaves generous
22
- * headroom for legitimate schema and transaction records.
23
- */
24
- export const MAX_WAL_FRAME_BYTES = 64 * 1024 * 1024;
25
- export class WalWriter {
26
- #handle;
27
- #offset;
28
- constructor(handle, offset) {
29
- if (!Number.isSafeInteger(offset) || offset < 0) {
30
- throw new RangeError("WAL offset is outside the safe integer range");
31
- }
32
- this.#handle = handle;
33
- this.#offset = offset;
6
+ const MAX_WAL_FRAME_BYTES = 64 * 1024 * 1024;
7
+ class WalWriter {
8
+ #handle;
9
+ #offset;
10
+ constructor(handle, offset) {
11
+ if (!Number.isSafeInteger(offset) || offset < 0) {
12
+ throw new RangeError("WAL offset is outside the safe integer range");
34
13
  }
35
- get byteLength() {
36
- return this.#offset;
14
+ this.#handle = handle;
15
+ this.#offset = offset;
16
+ }
17
+ get byteLength() {
18
+ return this.#offset;
19
+ }
20
+ append(payload, flush) {
21
+ const bytes = encodeRecordJson(payload);
22
+ if (bytes.byteLength > MAX_WAL_FRAME_BYTES) {
23
+ throw new RangeError(`WAL frame payload exceeds the ${String(MAX_WAL_FRAME_BYTES)} byte limit: ` + String(bytes.byteLength));
37
24
  }
38
- /** Appends one frame; with `flush`, durable before return. Synchronous — never yields. */
39
- append(payload, flush) {
40
- const bytes = encodeRecordJson(payload);
41
- if (bytes.byteLength > MAX_WAL_FRAME_BYTES) {
42
- throw new RangeError(`WAL frame payload exceeds the ${String(MAX_WAL_FRAME_BYTES)} byte limit: ` +
43
- String(bytes.byteLength));
44
- }
45
- const frame = new Uint8Array(FRAME_HEADER_BYTES + bytes.byteLength);
46
- if (this.#offset > Number.MAX_SAFE_INTEGER - frame.byteLength) {
47
- throw new RangeError("WAL offset exceeds the safe integer range");
48
- }
49
- const view = new DataView(frame.buffer);
50
- view.setUint32(0, FRAME_MAGIC, true);
51
- view.setUint32(4, bytes.byteLength, true);
52
- view.setUint32(8, crc32(bytes), true);
53
- frame.set(bytes, FRAME_HEADER_BYTES);
54
- const start = this.#offset;
55
- try {
56
- writeFully(this.#handle, frame, start, "appending a WAL frame");
57
- if (flush)
58
- this.#handle.flush();
59
- this.#offset = start + frame.byteLength;
60
- }
61
- catch (error) {
62
- // A complete write followed by a failed flush is not an acknowledged frame either.
63
- // Remove it before unpublished extent bytes are rolled back. If truncation itself
64
- // fails, recovery will fail closed when it verifies the frame's referenced payloads.
65
- try {
66
- this.#handle.truncate(start);
67
- }
68
- catch {
69
- // Preserve the original quota/I/O error.
70
- }
71
- throw error;
72
- }
25
+ const frame = new Uint8Array(FRAME_HEADER_BYTES + bytes.byteLength);
26
+ if (this.#offset > Number.MAX_SAFE_INTEGER - frame.byteLength) {
27
+ throw new RangeError("WAL offset exceeds the safe integer range");
73
28
  }
74
- /** Empties the log after a flushed checkpoint has covered every frame in it. */
75
- reset() {
76
- this.#handle.truncate(0);
77
- // truncate() already changed the handle's logical file position. Keep the writer aligned
78
- // with it even if the durability flush is refused; a later append must start at zero rather
79
- // than leave a sparse, unreplayable gap after the checkpoint.
80
- this.#offset = 0;
29
+ const view = new DataView(frame.buffer);
30
+ view.setUint32(0, FRAME_MAGIC, true);
31
+ view.setUint32(4, bytes.byteLength, true);
32
+ view.setUint32(8, crc32(bytes), true);
33
+ frame.set(bytes, FRAME_HEADER_BYTES);
34
+ const start = this.#offset;
35
+ try {
36
+ writeFully(this.#handle, frame, start, "appending a WAL frame");
37
+ if (flush)
81
38
  this.#handle.flush();
39
+ this.#offset = start + frame.byteLength;
40
+ } catch (error) {
41
+ try {
42
+ this.#handle.truncate(start);
43
+ } catch {
44
+ }
45
+ throw error;
82
46
  }
83
- flush() {
84
- this.#handle.flush();
85
- }
86
- close() {
87
- this.#handle.close();
88
- }
47
+ }
48
+ reset() {
49
+ this.#handle.truncate(0);
50
+ this.#offset = 0;
51
+ this.#handle.flush();
52
+ }
53
+ flush() {
54
+ this.#handle.flush();
55
+ }
56
+ close() {
57
+ this.#handle.close();
58
+ }
89
59
  }
90
- /**
91
- * Reads every whole, checksum-valid frame from the handle's current content, in order. A
92
- * truncated final frame is ignored; complete foreign or corrupt bytes are rejected. Returns the
93
- * payloads and the byte offset where appending should resume (the end of the last valid frame —
94
- * a truncated tail is overwritten).
95
- */
96
- export function replayWalFrames(handle) {
97
- const payloads = [];
98
- const frameEnds = [];
99
- let endOffset = 0;
100
- for (const frame of iterateWalFrames(handle)) {
101
- payloads.push(frame.payload);
102
- frameEnds.push(frame.frameEnd);
103
- endOffset = frame.frameEnd;
104
- }
105
- return { payloads, frameEnds, endOffset };
60
+ function replayWalFrames(handle) {
61
+ const payloads = [];
62
+ const frameEnds = [];
63
+ let endOffset = 0;
64
+ for (const frame of iterateWalFrames(handle)) {
65
+ payloads.push(frame.payload);
66
+ frameEnds.push(frame.frameEnd);
67
+ endOffset = frame.frameEnd;
68
+ }
69
+ return { payloads, frameEnds, endOffset };
106
70
  }
107
- /**
108
- * Streams checksum-valid frames with O(max-frame) memory. A truncated final frame is ignored;
109
- * complete foreign or checksum-invalid bytes are corruption and fail closed.
110
- */
111
- export function* iterateWalFrames(handle) {
112
- const size = handle.getSize();
113
- if (!Number.isSafeInteger(size) || size < 0) {
114
- throw new Error(`Invalid WAL byte length: ${String(size)}`);
71
+ function* iterateWalFrames(handle) {
72
+ const size = handle.getSize();
73
+ if (!Number.isSafeInteger(size) || size < 0) {
74
+ throw new Error(`Invalid WAL byte length: ${String(size)}`);
75
+ }
76
+ const header = new Uint8Array(FRAME_HEADER_BYTES);
77
+ const headerView = new DataView(header.buffer);
78
+ let offset = 0;
79
+ while (offset + FRAME_HEADER_BYTES <= size) {
80
+ readFully(handle, header, offset, "reading a WAL frame header for recovery");
81
+ if (headerView.getUint32(0, true) !== FRAME_MAGIC) {
82
+ throw new Error(`WAL frame marker mismatch at offset ${String(offset)}`);
83
+ }
84
+ const length = headerView.getUint32(4, true);
85
+ const checksum = headerView.getUint32(8, true);
86
+ if (length > MAX_WAL_FRAME_BYTES) {
87
+ throw new Error(`WAL frame at offset ${String(offset)} exceeds the ${String(MAX_WAL_FRAME_BYTES)} byte limit: ${String(length)}`);
115
88
  }
116
- const header = new Uint8Array(FRAME_HEADER_BYTES);
117
- const headerView = new DataView(header.buffer);
118
- let offset = 0;
119
- while (offset + FRAME_HEADER_BYTES <= size) {
120
- readFully(handle, header, offset, "reading a WAL frame header for recovery");
121
- if (headerView.getUint32(0, true) !== FRAME_MAGIC) {
122
- throw new Error(`WAL frame marker mismatch at offset ${String(offset)}`);
123
- }
124
- const length = headerView.getUint32(4, true);
125
- const checksum = headerView.getUint32(8, true);
126
- if (length > MAX_WAL_FRAME_BYTES) {
127
- throw new Error(`WAL frame at offset ${String(offset)} exceeds the ${String(MAX_WAL_FRAME_BYTES)} ` +
128
- `byte limit: ${String(length)}`);
129
- }
130
- const end = offset + FRAME_HEADER_BYTES + length;
131
- if (!Number.isSafeInteger(end) || end > size)
132
- break;
133
- const payloadBytes = new Uint8Array(length);
134
- readFully(handle, payloadBytes, offset + FRAME_HEADER_BYTES, "reading a WAL frame payload for recovery");
135
- if (crc32(payloadBytes) !== checksum) {
136
- throw new Error(`WAL frame checksum mismatch at offset ${String(offset)}`);
137
- }
138
- yield { payload: decodeRecordJson(payloadBytes), frameEnd: end };
139
- offset = end;
89
+ const end = offset + FRAME_HEADER_BYTES + length;
90
+ if (!Number.isSafeInteger(end) || end > size)
91
+ break;
92
+ const payloadBytes = new Uint8Array(length);
93
+ readFully(handle, payloadBytes, offset + FRAME_HEADER_BYTES, "reading a WAL frame payload for recovery");
94
+ if (crc32(payloadBytes) !== checksum) {
95
+ throw new Error(`WAL frame checksum mismatch at offset ${String(offset)}`);
140
96
  }
97
+ yield { payload: decodeRecordJson(payloadBytes), frameEnd: end };
98
+ offset = end;
99
+ }
141
100
  }
101
+ export {
102
+ MAX_WAL_FRAME_BYTES,
103
+ WalWriter,
104
+ iterateWalFrames,
105
+ replayWalFrames
106
+ };