@minnowdb/core 0.6.9 → 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 (103) 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.d.ts +5 -0
  10. package/dist/date-value.js +71 -41
  11. package/dist/engine/artifact-cache.js +106 -116
  12. package/dist/engine/batch.js +36 -44
  13. package/dist/engine/buffered-writer.js +156 -158
  14. package/dist/engine/byte-estimates.js +20 -22
  15. package/dist/engine/cache-limits.js +24 -25
  16. package/dist/engine/cancellation.js +5 -3
  17. package/dist/engine/catalog.js +64 -73
  18. package/dist/engine/client.js +715 -780
  19. package/dist/engine/database.js +15952 -18465
  20. package/dist/engine/defaults.js +104 -111
  21. package/dist/engine/errors.js +113 -128
  22. package/dist/engine/fts.js +233 -280
  23. package/dist/engine/group-index.js +287 -321
  24. package/dist/engine/index.js +23 -12
  25. package/dist/engine/join-index.js +177 -186
  26. package/dist/engine/keyed-live.js +168 -173
  27. package/dist/engine/live-api.js +0 -1
  28. package/dist/engine/live-equal.js +32 -37
  29. package/dist/engine/live.js +603 -647
  30. package/dist/engine/memory.js +109 -111
  31. package/dist/engine/optimizer.d.ts +19 -1
  32. package/dist/engine/optimizer.js +2642 -2515
  33. package/dist/engine/point-read.d.ts +4 -2
  34. package/dist/engine/point-read.js +121 -171
  35. package/dist/engine/query-api.js +12 -3
  36. package/dist/engine/query-cache.js +61 -69
  37. package/dist/engine/query.d.ts +45 -0
  38. package/dist/engine/query.js +8506 -9375
  39. package/dist/engine/result-wire.js +221 -252
  40. package/dist/engine/schema-wire.js +97 -112
  41. package/dist/engine/schema.js +1099 -1254
  42. package/dist/engine/sort-keys.js +315 -389
  43. package/dist/engine/sql-domains.d.ts +7 -1
  44. package/dist/engine/sql-domains.js +648 -734
  45. package/dist/engine/sql-driver.js +0 -1
  46. package/dist/engine/sql-functions.d.ts +11 -0
  47. package/dist/engine/sql-functions.js +1092 -0
  48. package/dist/engine/sql-json.js +199 -218
  49. package/dist/engine/sql-semantics.d.ts +25 -4
  50. package/dist/engine/sql-semantics.js +579 -513
  51. package/dist/engine/typed-live.js +271 -293
  52. package/dist/engine/vector.d.ts +7 -1
  53. package/dist/engine/vector.js +4513 -4580
  54. package/dist/engine/worker-host.d.ts +8 -63
  55. package/dist/engine/worker-host.js +28 -1130
  56. package/dist/engine/worker-indexeddb.d.ts +1 -0
  57. package/dist/engine/worker-indexeddb.js +3 -0
  58. package/dist/engine/worker-memory.d.ts +1 -0
  59. package/dist/engine/worker-memory.js +3 -0
  60. package/dist/engine/worker-opfs.d.ts +1 -0
  61. package/dist/engine/worker-opfs.js +3 -0
  62. package/dist/engine/worker-server.d.ts +93 -0
  63. package/dist/engine/worker-server.js +999 -0
  64. package/dist/engine/worker-store-indexeddb.d.ts +2 -0
  65. package/dist/engine/worker-store-indexeddb.js +11 -0
  66. package/dist/engine/worker-store-memory.d.ts +2 -0
  67. package/dist/engine/worker-store-memory.js +6 -0
  68. package/dist/engine/worker-store-opfs.d.ts +2 -0
  69. package/dist/engine/worker-store-opfs.js +9 -0
  70. package/dist/engine/worker.js +0 -12
  71. package/dist/engine/write-block-planner.js +93 -100
  72. package/dist/plan/index.js +15 -16
  73. package/dist/plan/model.d.ts +9 -1
  74. package/dist/plan/model.js +20 -27
  75. package/dist/storage/index.js +0 -12
  76. package/dist/storage/indexeddb.js +11776 -13171
  77. package/dist/storage/memory.js +1198 -1241
  78. package/dist/storage/opfs/files.js +238 -295
  79. package/dist/storage/opfs/index.js +9 -3
  80. package/dist/storage/opfs/leader.js +4386 -4856
  81. package/dist/storage/opfs/rpc.js +223 -259
  82. package/dist/storage/opfs/snapshot-ledger.js +219 -260
  83. package/dist/storage/opfs/store.js +884 -1003
  84. package/dist/storage/persistence.js +59 -74
  85. package/dist/storage/snapshot-stream.js +788 -826
  86. package/dist/storage/snapshot.js +36 -37
  87. package/dist/storage/toolkit/extents.js +444 -520
  88. package/dist/storage/toolkit/index.js +28 -29
  89. package/dist/storage/toolkit/record-core.js +5792 -6377
  90. package/dist/storage/toolkit/sync-file.js +34 -42
  91. package/dist/storage/toolkit/wal.js +92 -127
  92. package/dist/storage/toolkit/wire.js +232 -256
  93. package/dist/storage/types.js +2952 -3228
  94. package/dist/testing/block-store-conformance.js +1556 -1629
  95. package/dist/testing/index.js +310 -291
  96. package/dist/testing/opfs-shim.js +280 -312
  97. package/dist/testing/simulator.js +496 -534
  98. package/dist/testing/sqllogictest.js +425 -365
  99. package/dist/transactions/index.js +1464 -1712
  100. package/dist/worker-protocol/index.js +70 -69
  101. package/package.json +20 -2
  102. package/postgres-feature-profile.json +15 -5
  103. package/sql-feature-matrix.json +252 -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
+ };