@vibe-agent-toolkit/utils 0.2.0-rc.10 → 0.2.0-rc.12
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 +112 -10
- package/dist/archive/archive.d.ts +129 -0
- package/dist/archive/archive.d.ts.map +1 -0
- package/dist/archive/archive.js +223 -0
- package/dist/archive/archive.js.map +1 -0
- package/dist/archive/entries.d.ts +132 -0
- package/dist/archive/entries.d.ts.map +1 -0
- package/dist/archive/entries.js +223 -0
- package/dist/archive/entries.js.map +1 -0
- package/dist/archive/limits.d.ts +39 -0
- package/dist/archive/limits.d.ts.map +1 -0
- package/dist/archive/limits.js +59 -0
- package/dist/archive/limits.js.map +1 -0
- package/dist/archive/records.d.ts +14 -0
- package/dist/archive/records.d.ts.map +1 -0
- package/dist/archive/records.js +3 -0
- package/dist/archive/records.js.map +1 -0
- package/dist/archive/tar-reader.d.ts +18 -0
- package/dist/archive/tar-reader.d.ts.map +1 -0
- package/dist/archive/tar-reader.js +173 -0
- package/dist/archive/tar-reader.js.map +1 -0
- package/dist/archive/zip-reader.d.ts +76 -0
- package/dist/archive/zip-reader.d.ts.map +1 -0
- package/dist/archive/zip-reader.js +376 -0
- package/dist/archive/zip-reader.js.map +1 -0
- package/dist/archive.d.ts +14 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +12 -0
- package/dist/archive.js.map +1 -0
- package/dist/asset-reference.d.ts +17 -1
- package/dist/asset-reference.d.ts.map +1 -1
- package/dist/asset-reference.js +31 -7
- package/dist/asset-reference.js.map +1 -1
- package/dist/dirent-kind.d.ts +13 -40
- package/dist/dirent-kind.d.ts.map +1 -1
- package/dist/dirent-kind.js +21 -69
- package/dist/dirent-kind.js.map +1 -1
- package/dist/entrypoint.d.ts +2 -2
- package/dist/entrypoint.d.ts.map +1 -1
- package/dist/entrypoint.js +4 -3
- package/dist/entrypoint.js.map +1 -1
- package/dist/env-flag.d.ts +13 -5
- package/dist/env-flag.d.ts.map +1 -1
- package/dist/env-flag.js +13 -5
- package/dist/env-flag.js.map +1 -1
- package/dist/errors/confirmed-absent.d.ts +41 -0
- package/dist/errors/confirmed-absent.d.ts.map +1 -0
- package/dist/errors/confirmed-absent.js +43 -0
- package/dist/errors/confirmed-absent.js.map +1 -0
- package/dist/errors/errno-table.d.ts +177 -0
- package/dist/errors/errno-table.d.ts.map +1 -0
- package/dist/errors/errno-table.js +343 -0
- package/dist/errors/errno-table.js.map +1 -0
- package/dist/errors/fs-boundary.d.ts +36 -0
- package/dist/errors/fs-boundary.d.ts.map +1 -0
- package/dist/errors/fs-boundary.js +121 -0
- package/dist/errors/fs-boundary.js.map +1 -0
- package/dist/errors/fs-fault.d.ts +176 -0
- package/dist/errors/fs-fault.d.ts.map +1 -0
- package/dist/errors/fs-fault.js +215 -0
- package/dist/errors/fs-fault.js.map +1 -0
- package/dist/errors/git-errno.d.ts +13 -0
- package/dist/errors/git-errno.d.ts.map +1 -0
- package/dist/errors/git-errno.js +45 -0
- package/dist/errors/git-errno.js.map +1 -0
- package/dist/errors/path-present.d.ts +72 -0
- package/dist/errors/path-present.d.ts.map +1 -0
- package/dist/errors/path-present.js +110 -0
- package/dist/errors/path-present.js.map +1 -0
- package/dist/errors/rag-index-empty.d.ts +16 -0
- package/dist/errors/rag-index-empty.d.ts.map +1 -0
- package/dist/errors/rag-index-empty.js +16 -0
- package/dist/errors/rag-index-empty.js.map +1 -0
- package/dist/errors/suppressed-faults.d.ts +34 -0
- package/dist/errors/suppressed-faults.d.ts.map +1 -0
- package/dist/errors/suppressed-faults.js +66 -0
- package/dist/errors/suppressed-faults.js.map +1 -0
- package/dist/file-crawler.d.ts +13 -18
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +140 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs-utils.d.ts +75 -50
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +148 -158
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -4
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +5 -4
- package/dist/fs.js.map +1 -1
- package/dist/git-snapshot.d.ts +25 -17
- package/dist/git-snapshot.d.ts.map +1 -1
- package/dist/git-snapshot.js +65 -11
- package/dist/git-snapshot.js.map +1 -1
- package/dist/git-tracker.d.ts +11 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +29 -6
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +7 -35
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +5 -4
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/in-order.d.ts +93 -0
- package/dist/in-order.d.ts.map +1 -0
- package/dist/in-order.js +165 -0
- package/dist/in-order.js.map +1 -0
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/dist/kill-process-tree.d.ts +43 -0
- package/dist/kill-process-tree.d.ts.map +1 -0
- package/dist/kill-process-tree.js +48 -0
- package/dist/kill-process-tree.js.map +1 -0
- package/dist/link-following.d.ts +115 -0
- package/dist/link-following.d.ts.map +1 -0
- package/dist/link-following.js +283 -0
- package/dist/link-following.js.map +1 -0
- package/dist/listing-refusal.d.ts +44 -0
- package/dist/listing-refusal.d.ts.map +1 -1
- package/dist/listing-refusal.js +79 -2
- package/dist/listing-refusal.js.map +1 -1
- package/dist/name-fold.d.ts +25 -0
- package/dist/name-fold.d.ts.map +1 -0
- package/dist/name-fold.js +40 -0
- package/dist/name-fold.js.map +1 -0
- package/dist/path-containment.d.ts +17 -0
- package/dist/path-containment.d.ts.map +1 -1
- package/dist/path-containment.js +28 -5
- package/dist/path-containment.js.map +1 -1
- package/dist/path-utils.d.ts +14 -17
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +37 -55
- package/dist/path-utils.js.map +1 -1
- package/dist/process.d.ts +2 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +2 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +8 -9
- package/dist/project-utils.js.map +1 -1
- package/dist/safe-exec.d.ts +6 -2
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +1 -1
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts.map +1 -1
- package/dist/skill-targets.js +3 -1
- package/dist/skill-targets.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +0 -15
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +11 -50
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/special-file.d.ts +18 -0
- package/dist/special-file.d.ts.map +1 -0
- package/dist/special-file.js +21 -0
- package/dist/special-file.js.map +1 -0
- package/dist/terminating-signals.d.ts +129 -0
- package/dist/terminating-signals.d.ts.map +1 -0
- package/dist/terminating-signals.js +210 -0
- package/dist/terminating-signals.js.map +1 -0
- package/dist/test-helpers.d.ts +9 -5
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +18 -21
- package/dist/test-helpers.js.map +1 -1
- package/dist/test-scratch-guard.d.ts +34 -0
- package/dist/test-scratch-guard.d.ts.map +1 -0
- package/dist/test-scratch-guard.js +45 -0
- package/dist/test-scratch-guard.js.map +1 -0
- package/dist/testing/archive-fixtures.d.ts +147 -0
- package/dist/testing/archive-fixtures.d.ts.map +1 -0
- package/dist/testing/archive-fixtures.js +449 -0
- package/dist/testing/archive-fixtures.js.map +1 -0
- package/dist/testing/executables.d.ts +6 -0
- package/dist/testing/executables.d.ts.map +1 -1
- package/dist/testing/executables.js +20 -9
- package/dist/testing/executables.js.map +1 -1
- package/dist/testing/fault-fs-preload.d.ts +16 -0
- package/dist/testing/fault-fs-preload.d.ts.map +1 -0
- package/dist/testing/fault-fs-preload.js +22 -0
- package/dist/testing/fault-fs-preload.js.map +1 -0
- package/dist/testing/fault-fs.d.ts +115 -0
- package/dist/testing/fault-fs.d.ts.map +1 -0
- package/dist/testing/fault-fs.js +411 -0
- package/dist/testing/fault-fs.js.map +1 -0
- package/dist/testing/fault-spec.d.ts +83 -0
- package/dist/testing/fault-spec.d.ts.map +1 -0
- package/dist/testing/fault-spec.js +132 -0
- package/dist/testing/fault-spec.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -1
- package/dist/testing/hostile-tree.js +3 -4
- package/dist/testing/hostile-tree.js.map +1 -1
- package/dist/testing/platform-gates.d.ts +8 -0
- package/dist/testing/platform-gates.d.ts.map +1 -1
- package/dist/testing/platform-gates.js +24 -0
- package/dist/testing/platform-gates.js.map +1 -1
- package/dist/testing/temp-dir.d.ts +31 -0
- package/dist/testing/temp-dir.d.ts.map +1 -1
- package/dist/testing/temp-dir.js +63 -0
- package/dist/testing/temp-dir.js.map +1 -1
- package/dist/testing/tier-timeout.d.ts +34 -0
- package/dist/testing/tier-timeout.d.ts.map +1 -0
- package/dist/testing/tier-timeout.js +45 -0
- package/dist/testing/tier-timeout.js.map +1 -0
- package/dist/testing/tree-snapshot.d.ts +71 -0
- package/dist/testing/tree-snapshot.d.ts.map +1 -0
- package/dist/testing/tree-snapshot.js +163 -0
- package/dist/testing/tree-snapshot.js.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +8 -1
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +29 -0
- package/dist/text-content.d.ts.map +1 -1
- package/dist/text-content.js +64 -0
- package/dist/text-content.js.map +1 -1
- package/dist/text-file.d.ts +58 -6
- package/dist/text-file.d.ts.map +1 -1
- package/dist/text-file.js +111 -11
- package/dist/text-file.js.map +1 -1
- package/dist/text.d.ts +1 -1
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +1 -1
- package/dist/text.js.map +1 -1
- package/dist/timing-dump.d.ts.map +1 -1
- package/dist/timing-dump.js +2 -10
- package/dist/timing-dump.js.map +1 -1
- package/dist/tree-change/apply-decisions.d.ts +101 -0
- package/dist/tree-change/apply-decisions.d.ts.map +1 -0
- package/dist/tree-change/apply-decisions.js +139 -0
- package/dist/tree-change/apply-decisions.js.map +1 -0
- package/dist/tree-change/apply.d.ts +98 -0
- package/dist/tree-change/apply.d.ts.map +1 -0
- package/dist/tree-change/apply.js +364 -0
- package/dist/tree-change/apply.js.map +1 -0
- package/dist/tree-change/copy-decisions.d.ts +58 -0
- package/dist/tree-change/copy-decisions.d.ts.map +1 -0
- package/dist/tree-change/copy-decisions.js +66 -0
- package/dist/tree-change/copy-decisions.js.map +1 -0
- package/dist/tree-change/copy-tree.d.ts +125 -0
- package/dist/tree-change/copy-tree.d.ts.map +1 -0
- package/dist/tree-change/copy-tree.js +279 -0
- package/dist/tree-change/copy-tree.js.map +1 -0
- package/dist/tree-change/files.d.ts +167 -0
- package/dist/tree-change/files.d.ts.map +1 -0
- package/dist/tree-change/files.js +423 -0
- package/dist/tree-change/files.js.map +1 -0
- package/dist/tree-change/followed-walk.d.ts +54 -0
- package/dist/tree-change/followed-walk.d.ts.map +1 -0
- package/dist/tree-change/followed-walk.js +81 -0
- package/dist/tree-change/followed-walk.js.map +1 -0
- package/dist/tree-change/host-chain.d.ts +48 -0
- package/dist/tree-change/host-chain.d.ts.map +1 -0
- package/dist/tree-change/host-chain.js +85 -0
- package/dist/tree-change/host-chain.js.map +1 -0
- package/dist/tree-change/identity-compare.d.ts +61 -0
- package/dist/tree-change/identity-compare.d.ts.map +1 -0
- package/dist/tree-change/identity-compare.js +115 -0
- package/dist/tree-change/identity-compare.js.map +1 -0
- package/dist/tree-change/identity.d.ts +62 -0
- package/dist/tree-change/identity.d.ts.map +1 -0
- package/dist/tree-change/identity.js +152 -0
- package/dist/tree-change/identity.js.map +1 -0
- package/dist/tree-change/index.d.ts +24 -0
- package/dist/tree-change/index.d.ts.map +1 -0
- package/dist/tree-change/index.js +14 -0
- package/dist/tree-change/index.js.map +1 -0
- package/dist/tree-change/link-chain.d.ts +180 -0
- package/dist/tree-change/link-chain.d.ts.map +1 -0
- package/dist/tree-change/link-chain.js +377 -0
- package/dist/tree-change/link-chain.js.map +1 -0
- package/dist/tree-change/link-containment.d.ts +105 -0
- package/dist/tree-change/link-containment.d.ts.map +1 -0
- package/dist/tree-change/link-containment.js +196 -0
- package/dist/tree-change/link-containment.js.map +1 -0
- package/dist/tree-change/link-walk.d.ts +49 -0
- package/dist/tree-change/link-walk.d.ts.map +1 -0
- package/dist/tree-change/link-walk.js +69 -0
- package/dist/tree-change/link-walk.js.map +1 -0
- package/dist/tree-change/plan.d.ts +206 -0
- package/dist/tree-change/plan.d.ts.map +1 -0
- package/dist/tree-change/plan.js +320 -0
- package/dist/tree-change/plan.js.map +1 -0
- package/dist/tree-change/readable-tree.d.ts +44 -0
- package/dist/tree-change/readable-tree.d.ts.map +1 -0
- package/dist/tree-change/readable-tree.js +52 -0
- package/dist/tree-change/readable-tree.js.map +1 -0
- package/dist/tree-change/rollback-error.d.ts +29 -0
- package/dist/tree-change/rollback-error.d.ts.map +1 -0
- package/dist/tree-change/rollback-error.js +31 -0
- package/dist/tree-change/rollback-error.js.map +1 -0
- package/dist/tree-change/staging-names.d.ts +27 -0
- package/dist/tree-change/staging-names.d.ts.map +1 -0
- package/dist/tree-change/staging-names.js +40 -0
- package/dist/tree-change/staging-names.js.map +1 -0
- package/dist/tree-change/tree-walk.d.ts +81 -0
- package/dist/tree-change/tree-walk.d.ts.map +1 -0
- package/dist/tree-change/tree-walk.js +212 -0
- package/dist/tree-change/tree-walk.js.map +1 -0
- package/dist/win32-namespace.d.ts +24 -0
- package/dist/win32-namespace.d.ts.map +1 -0
- package/dist/win32-namespace.js +31 -0
- package/dist/win32-namespace.js.map +1 -0
- package/dist/yaml/surgical-yaml.d.ts +12 -3
- package/dist/yaml/surgical-yaml.d.ts.map +1 -1
- package/dist/yaml/surgical-yaml.js +16 -6
- package/dist/yaml/surgical-yaml.js.map +1 -1
- package/eslint/README.md +12 -4
- package/eslint/rules/fs-binding-scan.cjs +278 -0
- package/eslint/rules/no-adhoc-errno.cjs +364 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
- package/eslint/rules/no-blind-catch.cjs +1 -1
- package/eslint/rules/no-destructive-fs.cjs +359 -0
- package/eslint/rules/no-existssync.cjs +70 -0
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
- package/eslint/rules/no-link-following-fs.cjs +68 -0
- package/eslint/rules/no-literal-process-exit.cjs +96 -8
- package/eslint/rules/no-literal-test-timeout.cjs +214 -0
- package/eslint/rules/no-raw-signal-listener.cjs +224 -0
- package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
- package/package.json +10 -2
- package/dist/errors/errno.d.ts +0 -51
- package/dist/errors/errno.d.ts.map +0 -1
- package/dist/errors/errno.js +0 -112
- package/dist/errors/errno.js.map +0 -1
- package/eslint/rules/no-fs-promises-cp.cjs +0 -43
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `snapshotTree` — a comparable, content-addressed picture of a directory tree.
|
|
3
|
+
*
|
|
4
|
+
* The fault matrix asks one question of every injected run: did the tree end
|
|
5
|
+
* byte-equal to BEFORE or to GOLDEN, never a third state? Answering it needs the
|
|
6
|
+
* trees as values: a map from relative path to what is there (kind, mode, content
|
|
7
|
+
* hash, link target), and a diff that names every difference in words.
|
|
8
|
+
*
|
|
9
|
+
* ## What it will not do
|
|
10
|
+
*
|
|
11
|
+
* - It never follows a link: a link is `{ kind: 'link', target }` and nothing under it is
|
|
12
|
+
* walked, so a snapshot cannot wander out of its root or loop.
|
|
13
|
+
* - It never opens a special file (FIFO, socket, device): `lstat` decides the kind first and
|
|
14
|
+
* only a regular file is opened, `O_NONBLOCK`, so a FIFO that slipped in between cannot hang it.
|
|
15
|
+
* - It never swallows an unreadable entry. A directory the OS refuses is a loud error naming
|
|
16
|
+
* it, because a gap in the picture would read as "unchanged".
|
|
17
|
+
*
|
|
18
|
+
* ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so the `./testing`
|
|
19
|
+
* subpath keeps the empty third-party set its purity pin asserts.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readdirSync, readFileSync, readlinkSync } from 'node:fs';
|
|
23
|
+
import { isPathAbsentError } from '../errors/errno-table.js';
|
|
24
|
+
import { safePath } from '../path-core.js';
|
|
25
|
+
import { decodeTextContent } from '../text-content.js';
|
|
26
|
+
const NON_BLOCKING = constants.O_NONBLOCK ?? 0;
|
|
27
|
+
const PERMISSION_BITS = 0o7777;
|
|
28
|
+
const ROOT_KEY = '.';
|
|
29
|
+
function unreadable(path, error) {
|
|
30
|
+
return new Error(`snapshotTree: cannot read ${path}: ${error.message}`, { cause: error });
|
|
31
|
+
}
|
|
32
|
+
/** The file's content as it is hashed: byte rewrites first, then (decoded once) text rewrites. */
|
|
33
|
+
function rewritten(content, key, rewrites) {
|
|
34
|
+
const applicable = rewrites.filter((rule) => rule.applies(key));
|
|
35
|
+
let bytes = content;
|
|
36
|
+
for (const rule of applicable)
|
|
37
|
+
if ('rewriteBytes' in rule)
|
|
38
|
+
bytes = rule.rewriteBytes(bytes);
|
|
39
|
+
const textRules = applicable.filter((rule) => 'rewrite' in rule);
|
|
40
|
+
return textRules.length === 0 ? bytes : textRules.reduce((text, rule) => rule.rewrite(text), decodeTextContent(bytes).text);
|
|
41
|
+
}
|
|
42
|
+
function hashFile(path, key, rewrites) {
|
|
43
|
+
let fd;
|
|
44
|
+
try {
|
|
45
|
+
fd = openSync(path, constants.O_RDONLY | NON_BLOCKING);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (isPathAbsentError(error))
|
|
49
|
+
return undefined;
|
|
50
|
+
throw unreadable(path, error);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const stats = fstatSync(fd);
|
|
54
|
+
if (!stats.isFile())
|
|
55
|
+
return undefined;
|
|
56
|
+
return { mode: stats.mode & PERMISSION_BITS, sha256: createHash('sha256').update(rewritten(readFileSync(fd), key, rewrites)).digest('hex') };
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw unreadable(path, error);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
closeSync(fd);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function linkTarget(path) {
|
|
66
|
+
try {
|
|
67
|
+
return readlinkSync(path);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw unreadable(path, error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The entry at `path`, or undefined when it vanished between listing and looking. */
|
|
74
|
+
function entryAt(path, key, rewrites) {
|
|
75
|
+
let stats;
|
|
76
|
+
try {
|
|
77
|
+
stats = lstatSync(path);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (isPathAbsentError(error))
|
|
81
|
+
return undefined;
|
|
82
|
+
throw unreadable(path, error);
|
|
83
|
+
}
|
|
84
|
+
if (stats.isSymbolicLink())
|
|
85
|
+
return { kind: 'link', target: linkTarget(path) };
|
|
86
|
+
if (stats.isDirectory())
|
|
87
|
+
return { kind: 'dir', mode: stats.mode & PERMISSION_BITS };
|
|
88
|
+
if (!stats.isFile())
|
|
89
|
+
return { kind: 'special' };
|
|
90
|
+
const hashed = hashFile(path, key, rewrites);
|
|
91
|
+
return hashed === undefined ? { kind: 'special' } : { kind: 'file', ...hashed };
|
|
92
|
+
}
|
|
93
|
+
function walk(path, key, rewrites, out) {
|
|
94
|
+
const entry = entryAt(path, key, rewrites);
|
|
95
|
+
if (entry === undefined)
|
|
96
|
+
return;
|
|
97
|
+
out.set(key, entry);
|
|
98
|
+
if (entry.kind !== 'dir')
|
|
99
|
+
return;
|
|
100
|
+
let names;
|
|
101
|
+
try {
|
|
102
|
+
names = readdirSync(path);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw unreadable(path, error);
|
|
106
|
+
}
|
|
107
|
+
for (const name of names) {
|
|
108
|
+
walk(safePath.join(path, name), key === ROOT_KEY ? name : `${key}/${name}`, rewrites, out);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Snapshot the tree at `root`.
|
|
113
|
+
*
|
|
114
|
+
* @param root - A directory, a file or a link; absent yields the empty map
|
|
115
|
+
* @param options - Content rewrites applied before hashing; a key prefix for merging roots
|
|
116
|
+
* @returns Entries in walk order; compare with {@link diffSnapshots}
|
|
117
|
+
*/
|
|
118
|
+
export function snapshotTree(root, options = {}) {
|
|
119
|
+
const out = new Map();
|
|
120
|
+
walk(root, options.keyPrefix ?? ROOT_KEY, options.rewrites ?? [], out);
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
function describeEntry(entry) {
|
|
124
|
+
switch (entry.kind) {
|
|
125
|
+
case 'file': return `file mode ${entry.mode.toString(8)} sha256 ${entry.sha256.slice(0, 12)}`;
|
|
126
|
+
case 'dir': return `dir mode ${entry.mode.toString(8)}`;
|
|
127
|
+
case 'link': return `link -> ${entry.target}`;
|
|
128
|
+
case 'special': return 'special file';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Everything an entry is compared by: what a diff line prints shows only the head of a file's hash. */
|
|
132
|
+
function stateOf(entry) {
|
|
133
|
+
return entry.kind === 'file' ? `${describeEntry(entry)} ${entry.sha256}` : describeEntry(entry);
|
|
134
|
+
}
|
|
135
|
+
function compareKeys(x, y) {
|
|
136
|
+
if (x === y)
|
|
137
|
+
return 0;
|
|
138
|
+
return x < y ? -1 : 1;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Every difference between two snapshots, in words: `+ key (…)` added, `- key (…)`
|
|
142
|
+
* removed, `~ key: … => …` changed. Sorted by key; empty means identical.
|
|
143
|
+
*/
|
|
144
|
+
export function diffSnapshots(a, b) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
for (const [key, before] of a) {
|
|
147
|
+
const after = b.get(key);
|
|
148
|
+
if (after === undefined)
|
|
149
|
+
lines.push({ key, line: `- ${key} (${describeEntry(before)})` });
|
|
150
|
+
else if (stateOf(before) !== stateOf(after))
|
|
151
|
+
lines.push({ key, line: `~ ${key}: ${describeEntry(before)} => ${describeEntry(after)}` });
|
|
152
|
+
}
|
|
153
|
+
for (const [key, after] of b) {
|
|
154
|
+
if (!a.has(key))
|
|
155
|
+
lines.push({ key, line: `+ ${key} (${describeEntry(after)})` });
|
|
156
|
+
}
|
|
157
|
+
return lines.toSorted((x, y) => compareKeys(x.key, y.key)).map((item) => item.line);
|
|
158
|
+
}
|
|
159
|
+
/** The entries at `prefix` and under it (keys unchanged); a sibling sharing the spelling is not under it. */
|
|
160
|
+
export function subtree(s, prefix) {
|
|
161
|
+
return new Map([...s].filter(([key]) => key === prefix || key.startsWith(`${prefix}/`)));
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=tree-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-snapshot.js","sourceRoot":"","sources":["../../src/testing/tree-snapshot.ts"],"names":[],"mappings":"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"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -21,10 +21,16 @@
|
|
|
21
21
|
import type { UnreadablePolicy } from './listing-refusal.js';
|
|
22
22
|
export * from './test-helpers.js';
|
|
23
23
|
export { __readCrawlTimingSnapshot, __setCrawlTimingForTest, __writeCrawlTimingDumpForTest, } from './crawl-timing.js';
|
|
24
|
+
export * from './testing/archive-fixtures.js';
|
|
24
25
|
export * from './testing/executables.js';
|
|
26
|
+
export * from './testing/fault-fs.js';
|
|
27
|
+
export { faultFsSpecOf, faultRuleOf, INJECTED_ERRNOS, injectedErrnoError } from './testing/fault-spec.js';
|
|
28
|
+
export type { FaultFsSpec, FaultRule, FaultSpec, FsApi, FsOpFamily, InjectedErrno } from './testing/fault-spec.js';
|
|
25
29
|
export * from './testing/hostile-tree.js';
|
|
26
30
|
export * from './testing/platform-gates.js';
|
|
27
31
|
export * from './testing/temp-dir.js';
|
|
32
|
+
export * from './testing/tier-timeout.js';
|
|
33
|
+
export * from './testing/tree-snapshot.js';
|
|
28
34
|
/** A planted fixture tree and the means to remove it. */
|
|
29
35
|
export interface TempCorpus {
|
|
30
36
|
/** Absolute path to the tree's root. */
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAO7D,cAAc,mBAAmB,CAAC;AAIlC,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAG3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAO7D,cAAc,mBAAmB,CAAC;AAIlC,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAG3B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC1G,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACnH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAE3C,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACvC,UAAU,CAMZ;AAED,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,wDAAwD;IACxD,IAAI,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACvC,iBAAiB,CAyBnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAEtE"}
|
package/dist/testing.js
CHANGED
|
@@ -31,10 +31,17 @@ export * from './test-helpers.js';
|
|
|
31
31
|
export { __readCrawlTimingSnapshot, __setCrawlTimingForTest, __writeCrawlTimingDumpForTest, } from './crawl-timing.js';
|
|
32
32
|
// The three fixture modules the sinks' suites share: the ONE temp-dir family,
|
|
33
33
|
// the host gates, and the hostile tree every sink is tested against.
|
|
34
|
+
export * from './testing/archive-fixtures.js';
|
|
34
35
|
export * from './testing/executables.js';
|
|
36
|
+
export * from './testing/fault-fs.js';
|
|
37
|
+
// By name: the harness's vocabulary, not its op table (`OPS`, `OpSpec`, `PathShape` are `fault-fs.ts`'s
|
|
38
|
+
// own wiring, and an `export *` here made them this subpath's API). Pinned by `module-subpaths.test.ts`.
|
|
39
|
+
export { faultFsSpecOf, faultRuleOf, INJECTED_ERRNOS, injectedErrnoError } from './testing/fault-spec.js';
|
|
35
40
|
export * from './testing/hostile-tree.js';
|
|
36
41
|
export * from './testing/platform-gates.js';
|
|
37
42
|
export * from './testing/temp-dir.js';
|
|
43
|
+
export * from './testing/tier-timeout.js';
|
|
44
|
+
export * from './testing/tree-snapshot.js';
|
|
38
45
|
/**
|
|
39
46
|
* Write a literal corpus into a fresh temp directory.
|
|
40
47
|
*
|
|
@@ -119,6 +126,6 @@ export function replantableCorpus(prefix, corpus) {
|
|
|
119
126
|
* @returns A `refuse` policy for `crawlDirectory` and the git listings
|
|
120
127
|
*/
|
|
121
128
|
export function refuseUnreadableFixture(root) {
|
|
122
|
-
return { refuse: { root, remedy: 'A test fixture must be listable in full; fix the fixture, not the walk.' } };
|
|
129
|
+
return { refuse: { root, remedy: 'A test fixture must be listable in full; fix the fixture, not the walk.', side: 'source' } };
|
|
123
130
|
}
|
|
124
131
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAC/E,sEAAsE;AACtE,cAAc,mBAAmB,CAAC;AAClC,2EAA2E;AAC3E,8EAA8E;AAC9E,gEAAgE;AAChE,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,8EAA8E;AAC9E,qEAAqE;AACrE,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAC/E,sEAAsE;AACtE,cAAc,mBAAmB,CAAC;AAClC,2EAA2E;AAC3E,8EAA8E;AAC9E,gEAAgE;AAChE,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,8EAA8E;AAC9E,qEAAqE;AACrE,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,wGAAwG;AACxG,yGAAyG;AACzG,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE1G,cAAc,2BAA2B,CAAC;AAC1C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAU3C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAc,EACd,MAAwC;IAExC,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACpE,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACjF,CAAC;AAYD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,MAAwC;IAExC,IAAI,OAA+B,CAAC;IACpC,OAAO;QACL,KAAK,EAAE,GAAG,EAAE;YACV,0EAA0E;YAC1E,uEAAuE;YACvE,wEAAwE;YACxE,wEAAwE;YACxE,iEAAiE;YACjE,OAAO,EAAE,OAAO,EAAE,CAAC;YACnB,OAAO,GAAG,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;QACD,KAAK,EAAE,GAAG,EAAE;YACV,OAAO,EAAE,OAAO,EAAE,CAAC;YACnB,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QACD,IAAI,EAAE,GAAG,EAAE;YACT,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,sBAAsB,MAAM,iEAAiE,CAC9F,CAAC;YACJ,CAAC;YACD,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,yEAAyE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC;AACjI,CAAC"}
|
package/dist/text-content.d.ts
CHANGED
|
@@ -130,6 +130,34 @@
|
|
|
130
130
|
* NOT shift is the content key, which is computed over the raw bytes; see
|
|
131
131
|
* `resources`' `content-key.ts`.
|
|
132
132
|
*/
|
|
133
|
+
import { VatError } from './errors/vat-error.js';
|
|
134
|
+
/**
|
|
135
|
+
* Thrown when bytes are too many to become one JS string — the engine's refusal,
|
|
136
|
+
* made a coded error so a caller can treat it as a fact about a file.
|
|
137
|
+
*
|
|
138
|
+
* The limit is the engine's, not VAT's: V8 caps a string at
|
|
139
|
+
* `buffer.constants.MAX_STRING_LENGTH` (0x1fffffe8) UTF-16 code units, and
|
|
140
|
+
* decoding past it throws. One near-gigabyte CSV in a corpus used to abort a
|
|
141
|
+
* whole projection population on that throw.
|
|
142
|
+
*
|
|
143
|
+
* Two places raise it, and they agree by construction rather than by a shared
|
|
144
|
+
* literal. The `./fs` read path (`readDecodableBytes` in `text-file.ts`) refuses
|
|
145
|
+
* by `stat` BEFORE reading, against `MAX_DECODABLE_BYTES`, so half a gigabyte is
|
|
146
|
+
* never pulled into memory only to be refused. {@link decodeTextContent} — pure,
|
|
147
|
+
* so it cannot ask `node:buffer` for the limit (`subpath-purity.test.ts`) —
|
|
148
|
+
* translates the engine's own refusal for bytes that arrive by any other route.
|
|
149
|
+
* Dispatch with `isVatError(error, TextTooLargeError.code)`, never on the message.
|
|
150
|
+
*/
|
|
151
|
+
export declare class TextTooLargeError extends VatError {
|
|
152
|
+
readonly byteLength: number;
|
|
153
|
+
/** The code every instance carries, for `isVatError(error, TextTooLargeError.code)`. */
|
|
154
|
+
static readonly code = "TEXT_TOO_LARGE";
|
|
155
|
+
/**
|
|
156
|
+
* @param byteLength - How many bytes the refused content holds
|
|
157
|
+
* @param options - `cause`, when an engine refusal is being translated
|
|
158
|
+
*/
|
|
159
|
+
constructor(byteLength: number, options?: ErrorOptions);
|
|
160
|
+
}
|
|
133
161
|
/** An encoding this module can decode. */
|
|
134
162
|
export type TextEncoding = 'utf-8' | 'utf-16le' | 'utf-16be' | 'utf-32le' | 'utf-32be';
|
|
135
163
|
/**
|
|
@@ -184,6 +212,7 @@ export interface DecodedText extends TextProvenance {
|
|
|
184
212
|
* @param bytes - The exact bytes read from disk
|
|
185
213
|
* @returns The decoded text, the encoding used, whether that was a fact, and how
|
|
186
214
|
* many characters the decode had to replace
|
|
215
|
+
* @throws {@link TextTooLargeError} when the engine refuses a string that long
|
|
187
216
|
*
|
|
188
217
|
* @example
|
|
189
218
|
* ```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-content.d.ts","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmIG;AAEH,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAEvF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACxC;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAkLD
|
|
1
|
+
{"version":3,"file":"text-content.d.ts","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;IAS3C,QAAQ,CAAC,UAAU,EAAE,MAAM;IAR7B,wFAAwF;IACxF,MAAM,CAAC,QAAQ,CAAC,IAAI,oBAAoB;IAExC;;;OAGG;gBAEQ,UAAU,EAAE,MAAM,EAC3B,OAAO,CAAC,EAAE,YAAY;CAQzB;AAkBD,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAEvF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB;IACzB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;CACxC;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAY,SAAQ,cAAc;IACjD,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAkLD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAOhE"}
|
package/dist/text-content.js
CHANGED
|
@@ -130,6 +130,51 @@
|
|
|
130
130
|
* NOT shift is the content key, which is computed over the raw bytes; see
|
|
131
131
|
* `resources`' `content-key.ts`.
|
|
132
132
|
*/
|
|
133
|
+
import { VatError } from './errors/vat-error.js';
|
|
134
|
+
/**
|
|
135
|
+
* Thrown when bytes are too many to become one JS string — the engine's refusal,
|
|
136
|
+
* made a coded error so a caller can treat it as a fact about a file.
|
|
137
|
+
*
|
|
138
|
+
* The limit is the engine's, not VAT's: V8 caps a string at
|
|
139
|
+
* `buffer.constants.MAX_STRING_LENGTH` (0x1fffffe8) UTF-16 code units, and
|
|
140
|
+
* decoding past it throws. One near-gigabyte CSV in a corpus used to abort a
|
|
141
|
+
* whole projection population on that throw.
|
|
142
|
+
*
|
|
143
|
+
* Two places raise it, and they agree by construction rather than by a shared
|
|
144
|
+
* literal. The `./fs` read path (`readDecodableBytes` in `text-file.ts`) refuses
|
|
145
|
+
* by `stat` BEFORE reading, against `MAX_DECODABLE_BYTES`, so half a gigabyte is
|
|
146
|
+
* never pulled into memory only to be refused. {@link decodeTextContent} — pure,
|
|
147
|
+
* so it cannot ask `node:buffer` for the limit (`subpath-purity.test.ts`) —
|
|
148
|
+
* translates the engine's own refusal for bytes that arrive by any other route.
|
|
149
|
+
* Dispatch with `isVatError(error, TextTooLargeError.code)`, never on the message.
|
|
150
|
+
*/
|
|
151
|
+
export class TextTooLargeError extends VatError {
|
|
152
|
+
byteLength;
|
|
153
|
+
/** The code every instance carries, for `isVatError(error, TextTooLargeError.code)`. */
|
|
154
|
+
static code = 'TEXT_TOO_LARGE';
|
|
155
|
+
/**
|
|
156
|
+
* @param byteLength - How many bytes the refused content holds
|
|
157
|
+
* @param options - `cause`, when an engine refusal is being translated
|
|
158
|
+
*/
|
|
159
|
+
constructor(byteLength, options) {
|
|
160
|
+
super(TextTooLargeError.code, `${byteLength} bytes is too large to decode into a JavaScript string`, options);
|
|
161
|
+
this.byteLength = byteLength;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The code Node's `TextDecoder` attaches when the decoded string would pass the
|
|
166
|
+
* engine's string-length limit. Matched as a `code`, never as its message.
|
|
167
|
+
*/
|
|
168
|
+
const ENGINE_STRING_TOO_LONG = 'ERR_STRING_TOO_LONG';
|
|
169
|
+
/**
|
|
170
|
+
* Whether `error` is the engine refusing to build a string that long.
|
|
171
|
+
*
|
|
172
|
+
* @param error - What a decode threw
|
|
173
|
+
* @returns True for Node's `ERR_STRING_TOO_LONG`
|
|
174
|
+
*/
|
|
175
|
+
function isEngineStringTooLong(error) {
|
|
176
|
+
return error instanceof Error && error.code === ENGINE_STRING_TOO_LONG;
|
|
177
|
+
}
|
|
133
178
|
/**
|
|
134
179
|
* Byte-order marks, **longest first**.
|
|
135
180
|
*
|
|
@@ -308,6 +353,7 @@ function countReplacementCharacters(text) {
|
|
|
308
353
|
* @param bytes - The exact bytes read from disk
|
|
309
354
|
* @returns The decoded text, the encoding used, whether that was a fact, and how
|
|
310
355
|
* many characters the decode had to replace
|
|
356
|
+
* @throws {@link TextTooLargeError} when the engine refuses a string that long
|
|
311
357
|
*
|
|
312
358
|
* @example
|
|
313
359
|
* ```typescript
|
|
@@ -318,6 +364,24 @@ function countReplacementCharacters(text) {
|
|
|
318
364
|
* ```
|
|
319
365
|
*/
|
|
320
366
|
export function decodeTextContent(bytes) {
|
|
367
|
+
try {
|
|
368
|
+
return decodeWithinEngineLimits(bytes);
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
if (isEngineStringTooLong(error))
|
|
372
|
+
throw new TextTooLargeError(bytes.byteLength, { cause: error });
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* {@link decodeTextContent} without the translation of the engine's
|
|
378
|
+
* string-length refusal — split out so the translation wraps BOTH decodes, the
|
|
379
|
+
* fatal first pass and the lenient retry, either of which can reach the limit.
|
|
380
|
+
*
|
|
381
|
+
* @param bytes - The exact bytes read from disk
|
|
382
|
+
* @returns The decoded text and its provenance
|
|
383
|
+
*/
|
|
384
|
+
function decodeWithinEngineLimits(bytes) {
|
|
321
385
|
const bom = bomAt(bytes);
|
|
322
386
|
const encoding = bom?.encoding ?? 'utf-8';
|
|
323
387
|
const body = bom === null ? bytes : bytes.subarray(bom.length);
|
package/dist/text-content.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-content.js","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmIG;
|
|
1
|
+
{"version":3,"file":"text-content.js","sourceRoot":"","sources":["../src/text-content.ts"],"names":[],"mappings":"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"}
|
package/dist/text-file.d.ts
CHANGED
|
@@ -14,18 +14,20 @@
|
|
|
14
14
|
* it worse rather than more consistent.
|
|
15
15
|
*
|
|
16
16
|
* A caller that needs the RAW bytes as well as the text — to hash them, key
|
|
17
|
-
* them, or report `stat().size` — must not use these: read the bytes once
|
|
18
|
-
* and call `decodeTextContent` on them, so the digest
|
|
19
|
-
* from the same read. `readContentWithKey` in
|
|
20
|
-
* that caller and is shaped exactly that way.
|
|
17
|
+
* them, or report `stat().size` — must not use these: read the bytes once with
|
|
18
|
+
* {@link readDecodableBytes} and call `decodeTextContent` on them, so the digest
|
|
19
|
+
* and the characters come from the same read. `readContentWithKey` in
|
|
20
|
+
* `@vibe-agent-toolkit/resources` is that caller and is shaped exactly that way.
|
|
21
21
|
*/
|
|
22
|
+
import { type FileHandle } from 'node:fs/promises';
|
|
22
23
|
import { type DecodedText } from './text-content.js';
|
|
23
24
|
/**
|
|
24
25
|
* Read a file and decode it through {@link decodeTextContent}.
|
|
25
26
|
*
|
|
26
27
|
* @param filePath - Path to read
|
|
27
28
|
* @returns The decoded text, the encoding used, whether that was a fact, and the replacement-character count
|
|
28
|
-
* @throws Whatever `readFile` throws — callers decide whether that is fatal
|
|
29
|
+
* @throws Whatever `readFile` throws — callers decide whether that is fatal — and
|
|
30
|
+
* {@link TextTooLargeError} for a file too large to decode, before reading it
|
|
29
31
|
*
|
|
30
32
|
* @example
|
|
31
33
|
* ```typescript
|
|
@@ -39,7 +41,57 @@ export declare function readTextContent(filePath: string): Promise<DecodedText>;
|
|
|
39
41
|
*
|
|
40
42
|
* @param filePath - Path to read
|
|
41
43
|
* @returns The decoded text, the encoding used, whether that was a fact, and the replacement-character count
|
|
42
|
-
* @throws Whatever `readFileSync` throws
|
|
44
|
+
* @throws Whatever `readFileSync` throws, and {@link TextTooLargeError} as {@link readDecodableBytes} does
|
|
43
45
|
*/
|
|
44
46
|
export declare function readTextContentSync(filePath: string): DecodedText;
|
|
47
|
+
/**
|
|
48
|
+
* The most bytes that can be decoded into one JS string: the engine's own
|
|
49
|
+
* string-length limit, read from `node:buffer` rather than written down.
|
|
50
|
+
*
|
|
51
|
+
* A bound on BYTES stated in UTF-16 code units, which is exact-or-safe for UTF-8
|
|
52
|
+
* (a UTF-8 byte never yields more than one code unit) and conservative for
|
|
53
|
+
* UTF-16/UTF-32, whose bytes yield half or a quarter as many.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MAX_DECODABLE_BYTES: number;
|
|
56
|
+
/**
|
|
57
|
+
* Whether content of this many bytes is past what can be decoded to a string.
|
|
58
|
+
*
|
|
59
|
+
* @param byteLength - The content's size in bytes, as `stat` reports it
|
|
60
|
+
* @returns True when decoding it would exceed {@link MAX_DECODABLE_BYTES}
|
|
61
|
+
*/
|
|
62
|
+
export declare function exceedsDecodableLength(byteLength: number): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Open `filePath` for reading without ever blocking on it: a named pipe, socket
|
|
65
|
+
* or device is refused (`EFTYPE`) and its handle closed, so only a regular file
|
|
66
|
+
* (or a directory, which the read refuses itself) comes back. The open a caller
|
|
67
|
+
* uses to PROVE a source readable before copying it — a bare `open(path, 'r')`
|
|
68
|
+
* of a pipe waits for a writer forever.
|
|
69
|
+
*
|
|
70
|
+
* @param filePath - Path to open
|
|
71
|
+
* @returns The open handle; the caller closes it
|
|
72
|
+
* @throws an `EFTYPE` errno for a special file, and whatever `open` throws otherwise
|
|
73
|
+
*/
|
|
74
|
+
export declare function openForReading(filePath: string): Promise<FileHandle>;
|
|
75
|
+
/**
|
|
76
|
+
* Read a file's raw bytes for decoding — refusing, by `stat` and before reading
|
|
77
|
+
* a byte, one too large to decode at all.
|
|
78
|
+
*
|
|
79
|
+
* The one read path for "bytes that are about to go through
|
|
80
|
+
* {@link decodeTextContent}": {@link readTextContent} uses it, and so does
|
|
81
|
+
* `readContentWithKey` in `@vibe-agent-toolkit/resources`, which needs the raw
|
|
82
|
+
* bytes as well to key them. Without the stat, a 966 MB file is read whole and
|
|
83
|
+
* only then refused by the decoder, and past 2 GiB `readFile` fails first with
|
|
84
|
+
* its own `ERR_FS_FILE_TOO_LARGE` — two different errors for one fact.
|
|
85
|
+
*
|
|
86
|
+
* The size comes from `fstat` on the handle that is then read, not from a
|
|
87
|
+
* separate `stat` of the path, so a rename between the two cannot substitute
|
|
88
|
+
* another file. A file that GROWS past the limit after the `fstat` still reaches
|
|
89
|
+
* the decoder, which raises the same {@link TextTooLargeError}.
|
|
90
|
+
*
|
|
91
|
+
* @param filePath - Path to read
|
|
92
|
+
* @returns The file's bytes
|
|
93
|
+
* @throws {@link TextTooLargeError} when the file is past {@link MAX_DECODABLE_BYTES},
|
|
94
|
+
* an `EFTYPE` errno when it is not a regular file, and whatever `open`/`readFile` throw otherwise
|
|
95
|
+
*/
|
|
96
|
+
export declare function readDecodableBytes(filePath: string): Promise<Buffer>;
|
|
45
97
|
//# sourceMappingURL=text-file.d.ts.map
|
package/dist/text-file.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-file.d.ts","sourceRoot":"","sources":["../src/text-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"text-file.d.ts","sourceRoot":"","sources":["../src/text-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGzD,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE3F;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAE5E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAUjE;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAoC,CAAC;AAEvE;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAElE;AAoBD;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAS1E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ1E"}
|
package/dist/text-file.js
CHANGED
|
@@ -14,20 +14,23 @@
|
|
|
14
14
|
* it worse rather than more consistent.
|
|
15
15
|
*
|
|
16
16
|
* A caller that needs the RAW bytes as well as the text — to hash them, key
|
|
17
|
-
* them, or report `stat().size` — must not use these: read the bytes once
|
|
18
|
-
* and call `decodeTextContent` on them, so the digest
|
|
19
|
-
* from the same read. `readContentWithKey` in
|
|
20
|
-
* that caller and is shaped exactly that way.
|
|
17
|
+
* them, or report `stat().size` — must not use these: read the bytes once with
|
|
18
|
+
* {@link readDecodableBytes} and call `decodeTextContent` on them, so the digest
|
|
19
|
+
* and the characters come from the same read. `readContentWithKey` in
|
|
20
|
+
* `@vibe-agent-toolkit/resources` is that caller and is shaped exactly that way.
|
|
21
21
|
*/
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
22
|
+
import { constants } from 'node:buffer';
|
|
23
|
+
import { closeSync, constants as fsConstants, fstatSync, openSync, readFileSync } from 'node:fs';
|
|
24
|
+
import { open } from 'node:fs/promises';
|
|
25
|
+
import { refuseSpecialFile } from './special-file.js';
|
|
26
|
+
import { decodeTextContent, TextTooLargeError } from './text-content.js';
|
|
25
27
|
/**
|
|
26
28
|
* Read a file and decode it through {@link decodeTextContent}.
|
|
27
29
|
*
|
|
28
30
|
* @param filePath - Path to read
|
|
29
31
|
* @returns The decoded text, the encoding used, whether that was a fact, and the replacement-character count
|
|
30
|
-
* @throws Whatever `readFile` throws — callers decide whether that is fatal
|
|
32
|
+
* @throws Whatever `readFile` throws — callers decide whether that is fatal — and
|
|
33
|
+
* {@link TextTooLargeError} for a file too large to decode, before reading it
|
|
31
34
|
*
|
|
32
35
|
* @example
|
|
33
36
|
* ```typescript
|
|
@@ -36,16 +39,113 @@ import { decodeTextContent } from './text-content.js';
|
|
|
36
39
|
* ```
|
|
37
40
|
*/
|
|
38
41
|
export async function readTextContent(filePath) {
|
|
39
|
-
return decodeTextContent(await
|
|
42
|
+
return decodeTextContent(await readDecodableBytes(filePath));
|
|
40
43
|
}
|
|
41
44
|
/**
|
|
42
45
|
* {@link readTextContent}, synchronously.
|
|
43
46
|
*
|
|
44
47
|
* @param filePath - Path to read
|
|
45
48
|
* @returns The decoded text, the encoding used, whether that was a fact, and the replacement-character count
|
|
46
|
-
* @throws Whatever `readFileSync` throws
|
|
49
|
+
* @throws Whatever `readFileSync` throws, and {@link TextTooLargeError} as {@link readDecodableBytes} does
|
|
47
50
|
*/
|
|
48
51
|
export function readTextContentSync(filePath) {
|
|
49
|
-
|
|
52
|
+
const handle = openSync(filePath, readWithoutBlocking());
|
|
53
|
+
try {
|
|
54
|
+
const stats = fstatSync(handle);
|
|
55
|
+
refuseSpecialFile(stats, filePath);
|
|
56
|
+
refuseUndecodableSize(stats.size);
|
|
57
|
+
return decodeTextContent(readFileSync(handle));
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
closeSync(handle);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The most bytes that can be decoded into one JS string: the engine's own
|
|
65
|
+
* string-length limit, read from `node:buffer` rather than written down.
|
|
66
|
+
*
|
|
67
|
+
* A bound on BYTES stated in UTF-16 code units, which is exact-or-safe for UTF-8
|
|
68
|
+
* (a UTF-8 byte never yields more than one code unit) and conservative for
|
|
69
|
+
* UTF-16/UTF-32, whose bytes yield half or a quarter as many.
|
|
70
|
+
*/
|
|
71
|
+
export const MAX_DECODABLE_BYTES = constants.MAX_STRING_LENGTH;
|
|
72
|
+
/**
|
|
73
|
+
* Whether content of this many bytes is past what can be decoded to a string.
|
|
74
|
+
*
|
|
75
|
+
* @param byteLength - The content's size in bytes, as `stat` reports it
|
|
76
|
+
* @returns True when decoding it would exceed {@link MAX_DECODABLE_BYTES}
|
|
77
|
+
*/
|
|
78
|
+
export function exceedsDecodableLength(byteLength) {
|
|
79
|
+
return byteLength > MAX_DECODABLE_BYTES;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The flags to open for reading without waiting on a writer. Opening a named
|
|
83
|
+
* pipe blocks until one appears — a FIFO reached by a parse lane hung the whole
|
|
84
|
+
* run — and `O_NONBLOCK` returns at once so the handle's `fstat` can refuse it.
|
|
85
|
+
* It changes nothing for a regular file. Windows defines no `O_NONBLOCK` (and no
|
|
86
|
+
* FIFOs). Read at the call, not at import, so a test's partial `node:fs` mock
|
|
87
|
+
* that never reads a file does not have to supply `constants`.
|
|
88
|
+
*/
|
|
89
|
+
function readWithoutBlocking() {
|
|
90
|
+
const nonBlocking = fsConstants.O_NONBLOCK;
|
|
91
|
+
return fsConstants.O_RDONLY | (nonBlocking ?? 0);
|
|
92
|
+
}
|
|
93
|
+
/** Throw {@link TextTooLargeError} for a size no decode can hold. */
|
|
94
|
+
function refuseUndecodableSize(byteLength) {
|
|
95
|
+
if (exceedsDecodableLength(byteLength))
|
|
96
|
+
throw new TextTooLargeError(byteLength);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Open `filePath` for reading without ever blocking on it: a named pipe, socket
|
|
100
|
+
* or device is refused (`EFTYPE`) and its handle closed, so only a regular file
|
|
101
|
+
* (or a directory, which the read refuses itself) comes back. The open a caller
|
|
102
|
+
* uses to PROVE a source readable before copying it — a bare `open(path, 'r')`
|
|
103
|
+
* of a pipe waits for a writer forever.
|
|
104
|
+
*
|
|
105
|
+
* @param filePath - Path to open
|
|
106
|
+
* @returns The open handle; the caller closes it
|
|
107
|
+
* @throws an `EFTYPE` errno for a special file, and whatever `open` throws otherwise
|
|
108
|
+
*/
|
|
109
|
+
export async function openForReading(filePath) {
|
|
110
|
+
const handle = await open(filePath, readWithoutBlocking());
|
|
111
|
+
try {
|
|
112
|
+
refuseSpecialFile(await handle.stat(), filePath);
|
|
113
|
+
return handle;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
await handle.close();
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Read a file's raw bytes for decoding — refusing, by `stat` and before reading
|
|
122
|
+
* a byte, one too large to decode at all.
|
|
123
|
+
*
|
|
124
|
+
* The one read path for "bytes that are about to go through
|
|
125
|
+
* {@link decodeTextContent}": {@link readTextContent} uses it, and so does
|
|
126
|
+
* `readContentWithKey` in `@vibe-agent-toolkit/resources`, which needs the raw
|
|
127
|
+
* bytes as well to key them. Without the stat, a 966 MB file is read whole and
|
|
128
|
+
* only then refused by the decoder, and past 2 GiB `readFile` fails first with
|
|
129
|
+
* its own `ERR_FS_FILE_TOO_LARGE` — two different errors for one fact.
|
|
130
|
+
*
|
|
131
|
+
* The size comes from `fstat` on the handle that is then read, not from a
|
|
132
|
+
* separate `stat` of the path, so a rename between the two cannot substitute
|
|
133
|
+
* another file. A file that GROWS past the limit after the `fstat` still reaches
|
|
134
|
+
* the decoder, which raises the same {@link TextTooLargeError}.
|
|
135
|
+
*
|
|
136
|
+
* @param filePath - Path to read
|
|
137
|
+
* @returns The file's bytes
|
|
138
|
+
* @throws {@link TextTooLargeError} when the file is past {@link MAX_DECODABLE_BYTES},
|
|
139
|
+
* an `EFTYPE` errno when it is not a regular file, and whatever `open`/`readFile` throw otherwise
|
|
140
|
+
*/
|
|
141
|
+
export async function readDecodableBytes(filePath) {
|
|
142
|
+
const handle = await openForReading(filePath);
|
|
143
|
+
try {
|
|
144
|
+
refuseUndecodableSize((await handle.stat()).size);
|
|
145
|
+
return await handle.readFile();
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
await handle.close();
|
|
149
|
+
}
|
|
50
150
|
}
|
|
51
151
|
//# sourceMappingURL=text-file.js.map
|