@minnowdb/core 0.3.0 → 0.4.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.
- package/README.md +4 -2
- package/dist/block-format/block.d.ts +32 -13
- package/dist/block-format/block.js +179 -55
- package/dist/block-format/checksum.d.ts +2 -1
- package/dist/block-format/checksum.js +5 -2
- package/dist/block-format/codecs.d.ts +7 -2
- package/dist/block-format/codecs.js +53 -12
- package/dist/block-format/column.d.ts +3 -2
- package/dist/block-format/column.js +96 -30
- package/dist/block-format/index.d.ts +2 -2
- package/dist/block-format/index.js +2 -2
- package/dist/block-format/physical.d.ts +8 -2
- package/dist/block-format/physical.js +14 -6
- package/dist/block-format/types.d.ts +7 -3
- package/dist/block-format/types.js +0 -1
- package/dist/block-format/unicode.d.ts +11 -0
- package/dist/block-format/unicode.js +46 -0
- package/dist/date-value.d.ts +15 -0
- package/dist/date-value.js +64 -0
- package/dist/engine/artifact-cache.d.ts +0 -1
- package/dist/engine/artifact-cache.js +0 -1
- package/dist/engine/batch.d.ts +5 -2
- package/dist/engine/batch.js +0 -1
- package/dist/engine/buffered-writer.d.ts +2 -1
- package/dist/engine/buffered-writer.js +34 -9
- package/dist/engine/cache-limits.d.ts +25 -0
- package/dist/engine/cache-limits.js +25 -0
- package/dist/engine/catalog.d.ts +5 -6
- package/dist/engine/catalog.js +5 -5
- package/dist/engine/client.d.ts +27 -7
- package/dist/engine/client.js +197 -31
- package/dist/engine/database.d.ts +161 -38
- package/dist/engine/database.js +4326 -1523
- package/dist/engine/defaults.d.ts +0 -1
- package/dist/engine/defaults.js +2 -4
- package/dist/engine/errors.d.ts +19 -1
- package/dist/engine/errors.js +32 -2
- package/dist/engine/fts.d.ts +0 -6
- package/dist/engine/fts.js +39 -14
- package/dist/engine/group-index.d.ts +0 -1
- package/dist/engine/group-index.js +6 -11
- package/dist/engine/index.d.ts +16 -11
- package/dist/engine/index.js +12 -10
- package/dist/engine/join-index.d.ts +0 -1
- package/dist/engine/join-index.js +2 -2
- package/dist/engine/keyed-live.d.ts +57 -0
- package/dist/engine/keyed-live.js +226 -0
- package/dist/engine/live-api.d.ts +4 -0
- package/dist/engine/live-api.js +4 -0
- package/dist/engine/live.d.ts +43 -26
- package/dist/engine/live.js +499 -129
- package/dist/engine/memory.d.ts +2 -1
- package/dist/engine/memory.js +3 -2
- package/dist/engine/optimizer.d.ts +0 -1
- package/dist/engine/optimizer.js +4 -4
- package/dist/engine/query-api.d.ts +3 -0
- package/dist/engine/query-api.js +3 -0
- package/dist/engine/query-cache.d.ts +0 -1
- package/dist/engine/query-cache.js +4 -4
- package/dist/engine/query.d.ts +4 -7
- package/dist/engine/query.js +348 -155
- package/dist/engine/result-wire.d.ts +0 -1
- package/dist/engine/result-wire.js +2 -2
- package/dist/engine/schema-wire.d.ts +1 -2
- package/dist/engine/schema-wire.js +0 -1
- package/dist/engine/schema.d.ts +3 -8
- package/dist/engine/schema.js +41 -24
- package/dist/engine/sort-keys.d.ts +0 -1
- package/dist/engine/sort-keys.js +2 -2
- package/dist/engine/sql-domains.d.ts +6 -1
- package/dist/engine/sql-domains.js +209 -45
- package/dist/engine/sql-driver.d.ts +2 -2
- package/dist/engine/sql-driver.js +0 -1
- package/dist/engine/sql-json.d.ts +0 -1
- package/dist/engine/sql-json.js +53 -7
- package/dist/engine/sql-semantics.d.ts +8 -5
- package/dist/engine/sql-semantics.js +427 -65
- package/dist/engine/typed-live.d.ts +67 -0
- package/dist/engine/typed-live.js +349 -0
- package/dist/engine/vector.d.ts +7 -1
- package/dist/engine/vector.js +202 -58
- package/dist/engine/worker-host.d.ts +8 -2
- package/dist/engine/worker-host.js +616 -152
- package/dist/engine/worker.d.ts +0 -1
- package/dist/engine/worker.js +3 -3
- package/dist/engine/write-block-planner.d.ts +18 -0
- package/dist/engine/write-block-planner.js +112 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/plan/index.d.ts +0 -1
- package/dist/plan/index.js +0 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/indexeddb.d.ts +74 -65
- package/dist/storage/indexeddb.js +12301 -2076
- package/dist/storage/memory.d.ts +56 -66
- package/dist/storage/memory.js +1004 -139
- package/dist/storage/opfs/files.d.ts +18 -7
- package/dist/storage/opfs/files.js +115 -28
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +1 -1
- package/dist/storage/opfs/leader.d.ts +403 -91
- package/dist/storage/opfs/leader.js +4318 -602
- package/dist/storage/opfs/rpc.d.ts +15 -3
- package/dist/storage/opfs/rpc.js +223 -2
- package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
- package/dist/storage/opfs/snapshot-ledger.js +281 -0
- package/dist/storage/opfs/store.d.ts +26 -121
- package/dist/storage/opfs/store.js +436 -388
- package/dist/storage/persistence.d.ts +37 -0
- package/dist/storage/persistence.js +78 -0
- package/dist/storage/snapshot-stream.d.ts +24 -0
- package/dist/storage/snapshot-stream.js +897 -0
- package/dist/storage/snapshot.d.ts +9 -99
- package/dist/storage/snapshot.js +33 -309
- package/dist/storage/toolkit/extents.d.ts +41 -7
- package/dist/storage/toolkit/extents.js +402 -39
- package/dist/storage/toolkit/index.d.ts +3 -4
- package/dist/storage/toolkit/index.js +27 -3
- package/dist/storage/toolkit/record-core.d.ts +174 -61
- package/dist/storage/toolkit/record-core.js +5401 -849
- package/dist/storage/toolkit/sync-file.d.ts +10 -1
- package/dist/storage/toolkit/sync-file.js +50 -2
- package/dist/storage/toolkit/wal.d.ts +23 -5
- package/dist/storage/toolkit/wal.js +89 -27
- package/dist/storage/toolkit/wire.d.ts +4 -6
- package/dist/storage/toolkit/wire.js +80 -25
- package/dist/storage/types.d.ts +1271 -218
- package/dist/storage/types.js +1471 -138
- package/dist/testing/block-store-conformance.d.ts +0 -1
- package/dist/testing/block-store-conformance.js +877 -138
- package/dist/testing/index.d.ts +42 -31
- package/dist/testing/index.js +100 -68
- package/dist/testing/opfs-shim.d.ts +13 -3
- package/dist/testing/opfs-shim.js +40 -6
- package/dist/testing/simulator.d.ts +0 -1
- package/dist/testing/simulator.js +43 -12
- package/dist/testing/sqllogictest.d.ts +0 -1
- package/dist/testing/sqllogictest.js +0 -1
- package/dist/transactions/index.d.ts +81 -20
- package/dist/transactions/index.js +1050 -268
- package/dist/worker-protocol/index.d.ts +3 -2
- package/dist/worker-protocol/index.js +2 -5
- package/package.json +49 -1
- package/dist/block-format/block.d.ts.map +0 -1
- package/dist/block-format/block.js.map +0 -1
- package/dist/block-format/checksum.d.ts.map +0 -1
- package/dist/block-format/checksum.js.map +0 -1
- package/dist/block-format/codecs.d.ts.map +0 -1
- package/dist/block-format/codecs.js.map +0 -1
- package/dist/block-format/column.d.ts.map +0 -1
- package/dist/block-format/column.js.map +0 -1
- package/dist/block-format/index.d.ts.map +0 -1
- package/dist/block-format/index.js.map +0 -1
- package/dist/block-format/physical.d.ts.map +0 -1
- package/dist/block-format/physical.js.map +0 -1
- package/dist/block-format/types.d.ts.map +0 -1
- package/dist/block-format/types.js.map +0 -1
- package/dist/engine/artifact-cache.d.ts.map +0 -1
- package/dist/engine/artifact-cache.js.map +0 -1
- package/dist/engine/batch.d.ts.map +0 -1
- package/dist/engine/batch.js.map +0 -1
- package/dist/engine/buffered-writer.d.ts.map +0 -1
- package/dist/engine/buffered-writer.js.map +0 -1
- package/dist/engine/catalog.d.ts.map +0 -1
- package/dist/engine/catalog.js.map +0 -1
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/coordinator.d.ts +0 -17
- package/dist/engine/coordinator.d.ts.map +0 -1
- package/dist/engine/coordinator.js +0 -60
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/engine/database.d.ts.map +0 -1
- package/dist/engine/database.js.map +0 -1
- package/dist/engine/defaults.d.ts.map +0 -1
- package/dist/engine/defaults.js.map +0 -1
- package/dist/engine/errors.d.ts.map +0 -1
- package/dist/engine/errors.js.map +0 -1
- package/dist/engine/fts.d.ts.map +0 -1
- package/dist/engine/fts.js.map +0 -1
- package/dist/engine/group-index.d.ts.map +0 -1
- package/dist/engine/group-index.js.map +0 -1
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/join-index.d.ts.map +0 -1
- package/dist/engine/join-index.js.map +0 -1
- package/dist/engine/live.d.ts.map +0 -1
- package/dist/engine/live.js.map +0 -1
- package/dist/engine/memory.d.ts.map +0 -1
- package/dist/engine/memory.js.map +0 -1
- package/dist/engine/optimizer.d.ts.map +0 -1
- package/dist/engine/optimizer.js.map +0 -1
- package/dist/engine/query-cache.d.ts.map +0 -1
- package/dist/engine/query-cache.js.map +0 -1
- package/dist/engine/query.d.ts.map +0 -1
- package/dist/engine/query.js.map +0 -1
- package/dist/engine/result-wire.d.ts.map +0 -1
- package/dist/engine/result-wire.js.map +0 -1
- package/dist/engine/schema-wire.d.ts.map +0 -1
- package/dist/engine/schema-wire.js.map +0 -1
- package/dist/engine/schema.d.ts.map +0 -1
- package/dist/engine/schema.js.map +0 -1
- package/dist/engine/sort-keys.d.ts.map +0 -1
- package/dist/engine/sort-keys.js.map +0 -1
- package/dist/engine/sql-domains.d.ts.map +0 -1
- package/dist/engine/sql-domains.js.map +0 -1
- package/dist/engine/sql-driver.d.ts.map +0 -1
- package/dist/engine/sql-driver.js.map +0 -1
- package/dist/engine/sql-json.d.ts.map +0 -1
- package/dist/engine/sql-json.js.map +0 -1
- package/dist/engine/sql-semantics.d.ts.map +0 -1
- package/dist/engine/sql-semantics.js.map +0 -1
- package/dist/engine/vector.d.ts.map +0 -1
- package/dist/engine/vector.js.map +0 -1
- package/dist/engine/worker-host.d.ts.map +0 -1
- package/dist/engine/worker-host.js.map +0 -1
- package/dist/engine/worker.d.ts.map +0 -1
- package/dist/engine/worker.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js.map +0 -1
- package/dist/storage/fixture-shape.d.ts +0 -42
- package/dist/storage/fixture-shape.d.ts.map +0 -1
- package/dist/storage/fixture-shape.js +0 -146
- package/dist/storage/fixture-shape.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/indexeddb.d.ts.map +0 -1
- package/dist/storage/indexeddb.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/opfs/files.d.ts.map +0 -1
- package/dist/storage/opfs/files.js.map +0 -1
- package/dist/storage/opfs/index.d.ts.map +0 -1
- package/dist/storage/opfs/index.js.map +0 -1
- package/dist/storage/opfs/leader.d.ts.map +0 -1
- package/dist/storage/opfs/leader.js.map +0 -1
- package/dist/storage/opfs/rpc.d.ts.map +0 -1
- package/dist/storage/opfs/rpc.js.map +0 -1
- package/dist/storage/opfs/store.d.ts.map +0 -1
- package/dist/storage/opfs/store.js.map +0 -1
- package/dist/storage/snapshot.d.ts.map +0 -1
- package/dist/storage/snapshot.js.map +0 -1
- package/dist/storage/toolkit/extents.d.ts.map +0 -1
- package/dist/storage/toolkit/extents.js.map +0 -1
- package/dist/storage/toolkit/index.d.ts.map +0 -1
- package/dist/storage/toolkit/index.js.map +0 -1
- package/dist/storage/toolkit/record-core.d.ts.map +0 -1
- package/dist/storage/toolkit/record-core.js.map +0 -1
- package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
- package/dist/storage/toolkit/sync-file.js.map +0 -1
- package/dist/storage/toolkit/wal.d.ts.map +0 -1
- package/dist/storage/toolkit/wal.js.map +0 -1
- package/dist/storage/toolkit/wire.d.ts.map +0 -1
- package/dist/storage/toolkit/wire.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/testing/block-store-conformance.d.ts.map +0 -1
- package/dist/testing/block-store-conformance.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/opfs-shim.d.ts.map +0 -1
- package/dist/testing/opfs-shim.js.map +0 -1
- package/dist/testing/seeds.d.ts +0 -11
- package/dist/testing/seeds.d.ts.map +0 -1
- package/dist/testing/seeds.js +0 -50
- package/dist/testing/seeds.js.map +0 -1
- package/dist/testing/simulator.d.ts.map +0 -1
- package/dist/testing/simulator.js.map +0 -1
- package/dist/testing/sqllogictest.d.ts.map +0 -1
- package/dist/testing/sqllogictest.js.map +0 -1
- package/dist/transactions/index.d.ts.map +0 -1
- package/dist/transactions/index.js.map +0 -1
- package/dist/worker-protocol/index.d.ts.map +0 -1
- package/dist/worker-protocol/index.js.map +0 -1
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
* non-leading `.` pass through, everything else becomes `%XX` per UTF-8 byte — so names
|
|
7
7
|
* round-trip exactly and none can collide with or escape into another's path.
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type SyncFileHandle } from "../toolkit/sync-file.js";
|
|
10
|
+
/** Conservative cross-browser/filesystem ceiling for one already-encoded OPFS entry name. */
|
|
11
|
+
export declare const MAX_OPFS_ENCODED_SEGMENT_CHARACTERS = 240;
|
|
12
|
+
/** Directory handles have no close operation, so bound retained wrappers under path churn. */
|
|
13
|
+
export declare const MAX_OPFS_DIRECTORY_HANDLE_CACHE_ENTRIES = 128;
|
|
14
|
+
/** Internal layouts are shallow; reject adversarial/custom paths before recursive resolution. */
|
|
15
|
+
export declare const MAX_OPFS_PATH_SEGMENTS = 16;
|
|
10
16
|
export declare function encodeSegment(segment: string): string;
|
|
11
17
|
export declare function decodeSegment(encoded: string): string;
|
|
12
18
|
export interface WriteFileOptions {
|
|
@@ -20,6 +26,8 @@ export interface ReadFileOptions {
|
|
|
20
26
|
* default rethrows.
|
|
21
27
|
*/
|
|
22
28
|
lockedMeansAbsent?: boolean;
|
|
29
|
+
/** Refuse before `arrayBuffer()` allocates when the file exceeds this exact byte ceiling. */
|
|
30
|
+
maxBytes?: number;
|
|
23
31
|
}
|
|
24
32
|
/**
|
|
25
33
|
* A directory-handle cache plus the file operations the store needs. Handles are cached per
|
|
@@ -29,11 +37,15 @@ export interface ReadFileOptions {
|
|
|
29
37
|
export declare class OpfsTree {
|
|
30
38
|
#private;
|
|
31
39
|
constructor(root: FileSystemDirectoryHandle);
|
|
40
|
+
/** @internal Hardening-test visibility for the cache's fixed-memory invariant. */
|
|
41
|
+
get directoryCacheSizeForTests(): number;
|
|
32
42
|
getDirectory(path: readonly string[], create: boolean): Promise<FileSystemDirectoryHandle>;
|
|
33
43
|
/** Drops cached handles at and under a path — call after deleting a directory. */
|
|
34
44
|
invalidate(pathPrefix: readonly string[]): void;
|
|
35
45
|
/** `undefined` when the file (or any parent) does not exist. */
|
|
36
46
|
readFile(path: readonly string[], options?: ReadFileOptions): Promise<Uint8Array | undefined>;
|
|
47
|
+
/** File length without materializing its contents; `undefined` when the path is absent. */
|
|
48
|
+
fileSize(path: readonly string[]): Promise<number | undefined>;
|
|
37
49
|
/**
|
|
38
50
|
* Writes a whole file through an exclusive sync handle: truncate, write, optional flush,
|
|
39
51
|
* close. Quota errors escape unwrapped. The handle is always closed, so a failed write never
|
|
@@ -52,13 +64,12 @@ export declare class OpfsTree {
|
|
|
52
64
|
deleteFile(path: readonly string[]): Promise<boolean>;
|
|
53
65
|
/** Removes a whole subtree; missing is fine. */
|
|
54
66
|
deleteTree(path: readonly string[]): Promise<void>;
|
|
55
|
-
/** Names
|
|
56
|
-
|
|
57
|
-
/** Every file under a directory, with
|
|
58
|
-
walkFiles(path: readonly string[]):
|
|
67
|
+
/** Names directly inside a directory, streamed without retaining the directory cardinality. */
|
|
68
|
+
iterateNames(path: readonly string[]): AsyncGenerator<string>;
|
|
69
|
+
/** Every file under a directory, streamed with O(path depth) retained memory. */
|
|
70
|
+
walkFiles(path: readonly string[]): AsyncGenerator<{
|
|
59
71
|
path: string[];
|
|
60
72
|
size: number;
|
|
61
|
-
}
|
|
73
|
+
}>;
|
|
62
74
|
}
|
|
63
75
|
export declare function isDomError(error: unknown, name: string): boolean;
|
|
64
|
-
//# sourceMappingURL=files.d.ts.map
|
|
@@ -6,38 +6,86 @@
|
|
|
6
6
|
* non-leading `.` pass through, everything else becomes `%XX` per UTF-8 byte — so names
|
|
7
7
|
* round-trip exactly and none can collide with or escape into another's path.
|
|
8
8
|
*/
|
|
9
|
+
import { assertWellFormedString } from "../../block-format/unicode.js";
|
|
10
|
+
import { writeFully } from "../toolkit/sync-file.js";
|
|
9
11
|
const SAFE_SEGMENT_CHARS = /^[A-Za-z0-9._-]$/;
|
|
12
|
+
const UPPERCASE_HEX_BYTE = /^[0-9A-F]{2}$/;
|
|
13
|
+
const segmentTextEncoder = new TextEncoder();
|
|
14
|
+
const segmentTextDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
15
|
+
/** Conservative cross-browser/filesystem ceiling for one already-encoded OPFS entry name. */
|
|
16
|
+
export const MAX_OPFS_ENCODED_SEGMENT_CHARACTERS = 240;
|
|
17
|
+
/** Directory handles have no close operation, so bound retained wrappers under path churn. */
|
|
18
|
+
export const MAX_OPFS_DIRECTORY_HANDLE_CACHE_ENTRIES = 128;
|
|
19
|
+
/** Internal layouts are shallow; reject adversarial/custom paths before recursive resolution. */
|
|
20
|
+
export const MAX_OPFS_PATH_SEGMENTS = 16;
|
|
21
|
+
function assertEncodedSegmentLength(length) {
|
|
22
|
+
if (length > MAX_OPFS_ENCODED_SEGMENT_CHARACTERS) {
|
|
23
|
+
throw new RangeError(`Encoded OPFS path segment exceeds ${String(MAX_OPFS_ENCODED_SEGMENT_CHARACTERS)} characters`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
10
26
|
export function encodeSegment(segment) {
|
|
27
|
+
assertWellFormedString(segment, "OPFS path segment");
|
|
11
28
|
if (segment.length === 0)
|
|
12
29
|
return "%";
|
|
13
30
|
let encoded = "";
|
|
14
31
|
for (const char of segment) {
|
|
15
32
|
if (SAFE_SEGMENT_CHARS.test(char) && !(encoded.length === 0 && char === ".")) {
|
|
16
33
|
encoded += char;
|
|
34
|
+
assertEncodedSegmentLength(encoded.length);
|
|
17
35
|
continue;
|
|
18
36
|
}
|
|
19
|
-
for (const byte of
|
|
37
|
+
for (const byte of segmentTextEncoder.encode(char)) {
|
|
20
38
|
encoded += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
39
|
+
assertEncodedSegmentLength(encoded.length);
|
|
21
40
|
}
|
|
22
41
|
}
|
|
23
42
|
return encoded;
|
|
24
43
|
}
|
|
25
44
|
export function decodeSegment(encoded) {
|
|
45
|
+
assertEncodedSegmentLength(encoded.length);
|
|
26
46
|
if (encoded === "%")
|
|
27
47
|
return "";
|
|
48
|
+
if (encoded.length === 0)
|
|
49
|
+
throw new Error("Invalid OPFS path segment encoding");
|
|
28
50
|
const bytes = [];
|
|
29
51
|
for (let index = 0; index < encoded.length;) {
|
|
30
52
|
const char = encoded[index] ?? "";
|
|
31
53
|
if (char === "%") {
|
|
32
|
-
|
|
54
|
+
const hex = encoded.slice(index + 1, index + 3);
|
|
55
|
+
if (!UPPERCASE_HEX_BYTE.test(hex)) {
|
|
56
|
+
throw new Error("Invalid OPFS path segment encoding");
|
|
57
|
+
}
|
|
58
|
+
bytes.push(Number.parseInt(hex, 16));
|
|
33
59
|
index += 3;
|
|
34
60
|
continue;
|
|
35
61
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
62
|
+
if (!SAFE_SEGMENT_CHARS.test(char)) {
|
|
63
|
+
throw new Error("Invalid OPFS path segment encoding");
|
|
64
|
+
}
|
|
65
|
+
bytes.push(char.charCodeAt(0));
|
|
66
|
+
index += 1;
|
|
67
|
+
}
|
|
68
|
+
let decoded;
|
|
69
|
+
try {
|
|
70
|
+
decoded = segmentTextDecoder.decode(new Uint8Array(bytes));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new Error("Invalid OPFS path segment encoding");
|
|
74
|
+
}
|
|
75
|
+
if (encodeSegment(decoded) !== encoded) {
|
|
76
|
+
throw new Error("Non-canonical OPFS path segment encoding");
|
|
77
|
+
}
|
|
78
|
+
return decoded;
|
|
79
|
+
}
|
|
80
|
+
function validatePath(path) {
|
|
81
|
+
if (path.length > MAX_OPFS_PATH_SEGMENTS) {
|
|
82
|
+
throw new RangeError(`OPFS path exceeds ${String(MAX_OPFS_PATH_SEGMENTS)} segments`);
|
|
83
|
+
}
|
|
84
|
+
for (const segment of path) {
|
|
85
|
+
if (segment.length === 0 || segment.length > MAX_OPFS_ENCODED_SEGMENT_CHARACTERS) {
|
|
86
|
+
throw new RangeError("OPFS path contains an invalid segment");
|
|
87
|
+
}
|
|
39
88
|
}
|
|
40
|
-
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
41
89
|
}
|
|
42
90
|
/**
|
|
43
91
|
* A directory-handle cache plus the file operations the store needs. Handles are cached per
|
|
@@ -50,16 +98,30 @@ export class OpfsTree {
|
|
|
50
98
|
constructor(root) {
|
|
51
99
|
this.#root = root;
|
|
52
100
|
}
|
|
101
|
+
/** @internal Hardening-test visibility for the cache's fixed-memory invariant. */
|
|
102
|
+
get directoryCacheSizeForTests() {
|
|
103
|
+
return this.#directories.size;
|
|
104
|
+
}
|
|
53
105
|
async getDirectory(path, create) {
|
|
106
|
+
validatePath(path);
|
|
54
107
|
if (path.length === 0)
|
|
55
108
|
return this.#root;
|
|
56
109
|
const key = path.join("/");
|
|
57
110
|
const cached = this.#directories.get(key);
|
|
58
|
-
if (cached !== undefined)
|
|
111
|
+
if (cached !== undefined) {
|
|
112
|
+
// Map insertion order is the LRU order. Refresh without retaining a second reference.
|
|
113
|
+
this.#directories.delete(key);
|
|
114
|
+
this.#directories.set(key, cached);
|
|
59
115
|
return cached;
|
|
116
|
+
}
|
|
60
117
|
const parent = await this.getDirectory(path.slice(0, -1), create);
|
|
61
118
|
const directory = await retryTransient(() => parent.getDirectoryHandle(path[path.length - 1] ?? "", { create }));
|
|
62
119
|
this.#directories.set(key, directory);
|
|
120
|
+
if (this.#directories.size > MAX_OPFS_DIRECTORY_HANDLE_CACHE_ENTRIES) {
|
|
121
|
+
const oldest = this.#directories.keys().next().value;
|
|
122
|
+
if (oldest !== undefined)
|
|
123
|
+
this.#directories.delete(oldest);
|
|
124
|
+
}
|
|
63
125
|
return directory;
|
|
64
126
|
}
|
|
65
127
|
/** Drops cached handles at and under a path — call after deleting a directory. */
|
|
@@ -72,11 +134,19 @@ export class OpfsTree {
|
|
|
72
134
|
}
|
|
73
135
|
/** `undefined` when the file (or any parent) does not exist. */
|
|
74
136
|
async readFile(path, options = {}) {
|
|
137
|
+
validatePath(path);
|
|
138
|
+
if (options.maxBytes !== undefined &&
|
|
139
|
+
(!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0)) {
|
|
140
|
+
throw new RangeError("OPFS read byte limit must be a non-negative safe integer");
|
|
141
|
+
}
|
|
75
142
|
try {
|
|
76
143
|
return await retryTransient(async () => {
|
|
77
144
|
const directory = await this.getDirectory(path.slice(0, -1), false);
|
|
78
145
|
const handle = await directory.getFileHandle(path[path.length - 1] ?? "");
|
|
79
146
|
const file = await handle.getFile();
|
|
147
|
+
if (options.maxBytes !== undefined && file.size > options.maxBytes) {
|
|
148
|
+
throw new RangeError(`OPFS file exceeds the ${String(options.maxBytes)}-byte read limit`);
|
|
149
|
+
}
|
|
80
150
|
return new Uint8Array(await file.arrayBuffer());
|
|
81
151
|
});
|
|
82
152
|
}
|
|
@@ -98,17 +168,35 @@ export class OpfsTree {
|
|
|
98
168
|
throw error;
|
|
99
169
|
}
|
|
100
170
|
}
|
|
171
|
+
/** File length without materializing its contents; `undefined` when the path is absent. */
|
|
172
|
+
async fileSize(path) {
|
|
173
|
+
validatePath(path);
|
|
174
|
+
try {
|
|
175
|
+
return await retryTransient(async () => {
|
|
176
|
+
const directory = await this.getDirectory(path.slice(0, -1), false);
|
|
177
|
+
const handle = await directory.getFileHandle(path[path.length - 1] ?? "");
|
|
178
|
+
return (await handle.getFile()).size;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (isDomError(error, "NotFoundError") || isDomError(error, "TypeMismatchError")) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
101
188
|
/**
|
|
102
189
|
* Writes a whole file through an exclusive sync handle: truncate, write, optional flush,
|
|
103
190
|
* close. Quota errors escape unwrapped. The handle is always closed, so a failed write never
|
|
104
191
|
* leaves the file locked — only torn, which readers detect by checksum.
|
|
105
192
|
*/
|
|
106
193
|
async writeFile(path, bytes, options = {}) {
|
|
194
|
+
validatePath(path);
|
|
107
195
|
await retryTransient(async () => {
|
|
108
196
|
const handle = await this.openHandle(path, { create: true });
|
|
109
197
|
try {
|
|
110
198
|
handle.truncate(0);
|
|
111
|
-
handle
|
|
199
|
+
writeFully(handle, bytes, 0, `writing ${path.join("/")}`);
|
|
112
200
|
if (options.flush === true)
|
|
113
201
|
handle.flush();
|
|
114
202
|
}
|
|
@@ -123,6 +211,7 @@ export class OpfsTree {
|
|
|
123
211
|
* platform-specific type lives in the WebWorker lib, which not every consumer loads.
|
|
124
212
|
*/
|
|
125
213
|
async openHandle(path, options) {
|
|
214
|
+
validatePath(path);
|
|
126
215
|
return retryTransient(async () => {
|
|
127
216
|
const directory = await this.getDirectory(path.slice(0, -1), options.create);
|
|
128
217
|
const handle = await directory.getFileHandle(path[path.length - 1] ?? "", {
|
|
@@ -133,6 +222,7 @@ export class OpfsTree {
|
|
|
133
222
|
}
|
|
134
223
|
/** True when the file existed. Absence is not an error — deletes are idempotent. */
|
|
135
224
|
async deleteFile(path) {
|
|
225
|
+
validatePath(path);
|
|
136
226
|
try {
|
|
137
227
|
await retryTransient(async () => {
|
|
138
228
|
const directory = await this.getDirectory(path.slice(0, -1), false);
|
|
@@ -148,6 +238,7 @@ export class OpfsTree {
|
|
|
148
238
|
}
|
|
149
239
|
/** Removes a whole subtree; missing is fine. */
|
|
150
240
|
async deleteTree(path) {
|
|
241
|
+
validatePath(path);
|
|
151
242
|
try {
|
|
152
243
|
await retryTransient(async () => {
|
|
153
244
|
const directory = await this.getDirectory(path.slice(0, -1), false);
|
|
@@ -160,42 +251,40 @@ export class OpfsTree {
|
|
|
160
251
|
}
|
|
161
252
|
this.invalidate(path);
|
|
162
253
|
}
|
|
163
|
-
/** Names
|
|
164
|
-
async
|
|
254
|
+
/** Names directly inside a directory, streamed without retaining the directory cardinality. */
|
|
255
|
+
async *iterateNames(path) {
|
|
256
|
+
validatePath(path);
|
|
257
|
+
let directory;
|
|
165
258
|
try {
|
|
166
|
-
|
|
167
|
-
const directory = await this.getDirectory(path, false);
|
|
168
|
-
const names = [];
|
|
169
|
-
for await (const name of directory.keys())
|
|
170
|
-
names.push(name);
|
|
171
|
-
return names;
|
|
172
|
-
});
|
|
259
|
+
directory = await this.getDirectory(path, false);
|
|
173
260
|
}
|
|
174
261
|
catch (error) {
|
|
175
262
|
if (isDomError(error, "NotFoundError") || isDomError(error, "TypeMismatchError")) {
|
|
176
|
-
return
|
|
263
|
+
return;
|
|
177
264
|
}
|
|
178
265
|
throw error;
|
|
179
266
|
}
|
|
267
|
+
for await (const name of directory.keys())
|
|
268
|
+
yield name;
|
|
180
269
|
}
|
|
181
|
-
/** Every file under a directory, with
|
|
182
|
-
async walkFiles(path) {
|
|
183
|
-
|
|
270
|
+
/** Every file under a directory, streamed with O(path depth) retained memory. */
|
|
271
|
+
async *walkFiles(path) {
|
|
272
|
+
validatePath(path);
|
|
184
273
|
let root;
|
|
185
274
|
try {
|
|
186
275
|
root = await this.getDirectory(path, false);
|
|
187
276
|
}
|
|
188
277
|
catch (error) {
|
|
189
278
|
if (isDomError(error, "NotFoundError"))
|
|
190
|
-
return
|
|
279
|
+
return;
|
|
191
280
|
throw error;
|
|
192
281
|
}
|
|
193
|
-
const walk = async (directory, prefix)
|
|
282
|
+
const walk = async function* (directory, prefix) {
|
|
194
283
|
for await (const [name, entry] of directory) {
|
|
195
284
|
if (entry.kind === "file") {
|
|
196
285
|
try {
|
|
197
286
|
const file = await entry.getFile();
|
|
198
|
-
|
|
287
|
+
yield { path: [...prefix, name], size: file.size };
|
|
199
288
|
}
|
|
200
289
|
catch (error) {
|
|
201
290
|
// A file another instance is writing or deleting this instant is not part of any
|
|
@@ -208,12 +297,11 @@ export class OpfsTree {
|
|
|
208
297
|
}
|
|
209
298
|
}
|
|
210
299
|
else {
|
|
211
|
-
|
|
300
|
+
yield* walk(entry, [...prefix, name]);
|
|
212
301
|
}
|
|
213
302
|
}
|
|
214
303
|
};
|
|
215
|
-
|
|
216
|
-
return results;
|
|
304
|
+
yield* walk(root, []);
|
|
217
305
|
}
|
|
218
306
|
}
|
|
219
307
|
export function isDomError(error, name) {
|
|
@@ -240,4 +328,3 @@ async function retryTransient(operation) {
|
|
|
240
328
|
}
|
|
241
329
|
}
|
|
242
330
|
}
|
|
243
|
-
//# sourceMappingURL=files.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { OpfsBlockStore, deleteOpfsDatabase, type OpfsBlockStoreOptions } from "./store.js";
|
|
2
|
+
export { OpfsUncertainOutcomeError } from "../types.js";
|
|
2
3
|
export { OpfsTree, type ReadFileOptions, type WriteFileOptions } from "./files.js";
|
|
3
|
-
//# sourceMappingURL=index.d.ts.map
|