@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,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plan half of the tree-change primitive: every destructive verb describes
|
|
3
|
+
* what it wants as {@link TreeChange}s, and {@link planTreeChanges} decides, with
|
|
4
|
+
* no side effect, what will actually happen to each — before anything is written.
|
|
5
|
+
*
|
|
6
|
+
* The decisions, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. **What is there** at each destination (`lstat`; a destination the OS refuses
|
|
9
|
+
* to examine throws, classified `destination`; "absent" is believed only when
|
|
10
|
+
* the parent's listing agrees).
|
|
11
|
+
* 2. **Keep.** A `remove` with nothing there, or whose `keepIfSameAs` names an
|
|
12
|
+
* entry that is `same` **or `unknown`** to it, is kept, with the reason. Only
|
|
13
|
+
* the target's own unexaminability throws, never a sibling's. A `remove` that
|
|
14
|
+
* holds a kept entry (or cannot be proven not to) is kept too: a kept entry is
|
|
15
|
+
* kept whole, never taken away with a directory above it.
|
|
16
|
+
* 3. **Aliasing and containment, by identity.** A `remove` that is the same entry
|
|
17
|
+
* as a `replace` (`plugins/Old` and `plugins/old` on APFS or NTFS, a link and its
|
|
18
|
+
* target), or an earlier `remove`, or lies inside any other change's
|
|
19
|
+
* destination, is `subsumed`: that change parks it. `unknown` is never a merge
|
|
20
|
+
* and never a refusal — the two changes run as two, and a wrong guess fails the
|
|
21
|
+
* apply, which rolls back; refusing would refuse every change on a filesystem
|
|
22
|
+
* that reports `ino` 0. Any other two changes still active whose destinations
|
|
23
|
+
* are one entry or nest are refused ({@link TREE_DESTS_OVERLAP_CODE}): the
|
|
24
|
+
* calling verb's defect, never the user's input.
|
|
25
|
+
* 4. **Ownership** of whatever is there: `must-be-free` refuses anything but an
|
|
26
|
+
* empty directory ({@link TREE_DEST_OCCUPIED_CODE}); `vat-made` refuses what its
|
|
27
|
+
* `recognise` disowns ({@link TREE_DEST_NOT_OWNED_CODE}); `force` and
|
|
28
|
+
* `vat-state` take anything — except that a `replace-file` never takes a directory
|
|
29
|
+
* (or a link to one), under any ownership ({@link TREE_DEST_OCCUPIED_CODE}).
|
|
30
|
+
* 5. **Holding.** A `copy` source, or a `write` fill's declared `reads`, that is the
|
|
31
|
+
* destination or inside it ({@link TREE_DEST_HOLDS_SOURCE_CODE}: the replace
|
|
32
|
+
* would delete it — never asked of a destination that is a link, which holds
|
|
33
|
+
* nothing: only the link is parked), or that holds the destination
|
|
34
|
+
* ({@link TREE_SOURCE_HOLDS_DEST_CODE}: a copy into itself), is refused.
|
|
35
|
+
* 6. **Readable source.** Every `copy` source is proven readable (the one
|
|
36
|
+
* special-file and link policy, `proveTreeReadable`, on the fill's declared `side`).
|
|
37
|
+
*
|
|
38
|
+
* Utils names no refusal code: these are library codes, and the CLI's refusal map
|
|
39
|
+
* decides which refusal each is.
|
|
40
|
+
*/
|
|
41
|
+
import { type FsSide } from '../errors/fs-fault.js';
|
|
42
|
+
import type { EntryContainment, EntrySameness } from './identity-compare.js';
|
|
43
|
+
import type { LinkPolicy } from './tree-walk.js';
|
|
44
|
+
/** A destination that must be absent or an empty directory already holds something. */
|
|
45
|
+
export declare const TREE_DEST_OCCUPIED_CODE = "TREE_DEST_OCCUPIED";
|
|
46
|
+
/** A destination VAT may replace only when it made it holds something its recogniser disowns. */
|
|
47
|
+
export declare const TREE_DEST_NOT_OWNED_CODE = "TREE_DEST_NOT_OWNED";
|
|
48
|
+
/**
|
|
49
|
+
* Two changes of one plan whose destinations are one entry or nest (other than a remove the other
|
|
50
|
+
* change takes with it): they cannot be one transaction — the outer park would carry the inner
|
|
51
|
+
* change's staging away. The calling verb built a plan it must not, so it is VAT's defect.
|
|
52
|
+
*/
|
|
53
|
+
export declare const TREE_DESTS_OVERLAP_CODE = "TREE_DESTS_OVERLAP";
|
|
54
|
+
/** A copy's destination lies inside its source: the copy would copy into itself. */
|
|
55
|
+
export declare const TREE_SOURCE_HOLDS_DEST_CODE = "TREE_SOURCE_HOLDS_DEST";
|
|
56
|
+
/** A copy's source is its destination or lies inside it: the replace would delete what it copies. */
|
|
57
|
+
export declare const TREE_DEST_HOLDS_SOURCE_CODE = "TREE_DEST_HOLDS_SOURCE";
|
|
58
|
+
/** What a recogniser says about a destination that is there. */
|
|
59
|
+
export type OwnershipVerdict = {
|
|
60
|
+
readonly owned: true;
|
|
61
|
+
} | {
|
|
62
|
+
readonly owned: false;
|
|
63
|
+
readonly reason: string;
|
|
64
|
+
};
|
|
65
|
+
/** Who may lose whatever is at a destination. */
|
|
66
|
+
export type Ownership =
|
|
67
|
+
/** Only an absent destination or an empty directory; anything else is refused. */
|
|
68
|
+
{
|
|
69
|
+
readonly kind: 'must-be-free';
|
|
70
|
+
}
|
|
71
|
+
/** The user said `--force`: whatever is there may go. */
|
|
72
|
+
| {
|
|
73
|
+
readonly kind: 'force';
|
|
74
|
+
}
|
|
75
|
+
/** Something VAT made before, as `recognise` judges it; anything else is refused. */
|
|
76
|
+
| {
|
|
77
|
+
readonly kind: 'vat-made';
|
|
78
|
+
readonly recognise: (dest: string) => OwnershipVerdict;
|
|
79
|
+
}
|
|
80
|
+
/** State VAT owns outright (a cache, a marketplace under `~/.claude`, `dist/`). */
|
|
81
|
+
| {
|
|
82
|
+
readonly kind: 'vat-state';
|
|
83
|
+
};
|
|
84
|
+
/** How a replacement tree is made, in a staged place beside its destination. */
|
|
85
|
+
export type TreeFill =
|
|
86
|
+
/**
|
|
87
|
+
* `copy` copies `source` by the readable-tree walk. `side` is the side of the verb `source` is on — an input
|
|
88
|
+
* (`source`), VAT's staging (`environment`), or a copy already made into user state (`destination`) — and
|
|
89
|
+
* every read of it, by the proof and by the copy, is classified on it. Required: only the caller knows.
|
|
90
|
+
*/
|
|
91
|
+
{
|
|
92
|
+
readonly from: 'copy';
|
|
93
|
+
readonly source: string;
|
|
94
|
+
readonly side: FsSide;
|
|
95
|
+
readonly links: LinkPolicy;
|
|
96
|
+
readonly filter?: (relative: string) => boolean;
|
|
97
|
+
}
|
|
98
|
+
/** `write` fills the empty staged directory; `reads` are the inputs it reads, held to the same holding check as a copy source. */
|
|
99
|
+
| {
|
|
100
|
+
readonly from: 'write';
|
|
101
|
+
readonly write: (staged: string) => Promise<void>;
|
|
102
|
+
readonly reads?: readonly string[];
|
|
103
|
+
}
|
|
104
|
+
/** The staged entry is a link to `target` (a `--dev` lane). */
|
|
105
|
+
| {
|
|
106
|
+
readonly from: 'link';
|
|
107
|
+
readonly target: string;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* A `replace-file`'s bytes: given, or made when the file is staged. Staging runs in the plan's order, so a
|
|
111
|
+
* function is called only after every earlier change of the plan has staged — a file made FROM a tree the
|
|
112
|
+
* same plan stages (an archive of a staged bundle) is one change of that plan, not a second transaction.
|
|
113
|
+
*/
|
|
114
|
+
export type FileContents = string | Uint8Array | (() => string | Uint8Array);
|
|
115
|
+
/** One change a verb wants. `label` names it in the dry run and in every message. */
|
|
116
|
+
export type TreeChange = {
|
|
117
|
+
readonly op: 'replace';
|
|
118
|
+
readonly dest: string;
|
|
119
|
+
readonly ownership: Ownership;
|
|
120
|
+
readonly fill: TreeFill;
|
|
121
|
+
readonly label: string;
|
|
122
|
+
} | {
|
|
123
|
+
readonly op: 'replace-file';
|
|
124
|
+
readonly dest: string;
|
|
125
|
+
readonly ownership: Ownership;
|
|
126
|
+
readonly contents: FileContents;
|
|
127
|
+
readonly label: string;
|
|
128
|
+
} | {
|
|
129
|
+
readonly op: 'remove';
|
|
130
|
+
readonly dest: string;
|
|
131
|
+
readonly ownership: Ownership;
|
|
132
|
+
readonly keepIfSameAs?: () => readonly string[];
|
|
133
|
+
readonly label: string;
|
|
134
|
+
};
|
|
135
|
+
/** What is at a destination. */
|
|
136
|
+
export type EntryKind = 'absent' | 'directory' | 'file' | 'link' | 'special';
|
|
137
|
+
/** What will happen to a destination. */
|
|
138
|
+
export type PlannedAction = 'create' | 'replace' | 'remove' | 'keep' | 'subsumed';
|
|
139
|
+
/** One change, decided. */
|
|
140
|
+
export interface PlannedChange {
|
|
141
|
+
readonly change: TreeChange;
|
|
142
|
+
readonly existing: EntryKind;
|
|
143
|
+
readonly action: PlannedAction;
|
|
144
|
+
/** Why it is kept or subsumed. */
|
|
145
|
+
readonly reason?: string;
|
|
146
|
+
/**
|
|
147
|
+
* Set on a remove kept because the OS refused to examine an entry it may be (its `keepIfSameAs`):
|
|
148
|
+
* that entry. A caller that drops its own record of the kept entry names it, so the user can tell
|
|
149
|
+
* what stayed and why (ruling R7 d-I-1: never delete what may BE a kept sibling).
|
|
150
|
+
*/
|
|
151
|
+
readonly unexaminedSibling?: string;
|
|
152
|
+
}
|
|
153
|
+
/** Every change, decided; `describe()` is the dry run, one line per change. */
|
|
154
|
+
export interface TreePlan {
|
|
155
|
+
readonly changes: readonly PlannedChange[];
|
|
156
|
+
describe(): readonly string[];
|
|
157
|
+
}
|
|
158
|
+
/** The filesystem facts the planner decides by: live in {@link planTreeChanges}, faked in its unit test. */
|
|
159
|
+
export interface PlanFacts {
|
|
160
|
+
/** What is at `dest`; throws when the OS refuses to examine it. */
|
|
161
|
+
readonly existing: (dest: string) => EntryKind;
|
|
162
|
+
readonly isEmptyDirectory: (dest: string) => boolean;
|
|
163
|
+
/** Whether the link at `dest` leads to a directory (false for a dangling link, or one to a file). */
|
|
164
|
+
readonly linksToDirectory: (dest: string) => boolean;
|
|
165
|
+
readonly sameEntry: (a: string, b: string) => EntrySameness;
|
|
166
|
+
readonly isInside: (child: string, ancestor: string) => EntryContainment;
|
|
167
|
+
/** Throws when the OS refuses to examine `dest`'s identity: a remove's own unexaminability is never a keep. */
|
|
168
|
+
readonly requireExaminable: (dest: string) => void;
|
|
169
|
+
/** Whether the OS refuses to examine `entry`'s identity (an `unknown` that is a refusal, not a fold that cannot tell). */
|
|
170
|
+
readonly refusesToExamine: (entry: string) => boolean;
|
|
171
|
+
}
|
|
172
|
+
/** Whether a planned change touches its destination (`keep` and `subsumed` do not). */
|
|
173
|
+
export declare const isActive: ({ action }: PlannedChange) => boolean;
|
|
174
|
+
/**
|
|
175
|
+
* The pure planner: every decision of {@link planTreeChanges} but the source
|
|
176
|
+
* proof, made from `facts`.
|
|
177
|
+
*
|
|
178
|
+
* @throws VatError coded `TREE_DESTS_OVERLAP` (a defect in the caller's plan), `TREE_DEST_OCCUPIED`,
|
|
179
|
+
* `TREE_DEST_NOT_OWNED`, `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; whatever `facts` throws
|
|
180
|
+
*/
|
|
181
|
+
export declare function planFromFacts(changes: readonly TreeChange[], facts: PlanFacts): TreePlan;
|
|
182
|
+
/**
|
|
183
|
+
* What is at `dest` NOW, for the apply to hold against what the plan decided
|
|
184
|
+
* (`changedSincePlan`). The emptiness of a directory is read only where the plan took it as free.
|
|
185
|
+
*
|
|
186
|
+
* @throws FsFaultError side `destination` when the OS refuses to examine or list it
|
|
187
|
+
*/
|
|
188
|
+
export declare function destinationNow(dest: string, planned: PlannedChange): {
|
|
189
|
+
kind: EntryKind;
|
|
190
|
+
emptyDirectory: boolean;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Decide every change against the filesystem as it is, with no side effect. Each
|
|
194
|
+
* destination is taken in its canonical spelling (`safePath.resolve`: absolute,
|
|
195
|
+
* `.`/`..` resolved, forward slashes); `describe()` prints that spelling.
|
|
196
|
+
* Hand the result to `applyTreePlan`; print `describe()` for a dry run — the two
|
|
197
|
+
* cannot disagree, because the apply does exactly what the plan says.
|
|
198
|
+
*
|
|
199
|
+
* @param changes - What the verb wants, in the order it wants it
|
|
200
|
+
* @throws VatError coded `TREE_DEST_OCCUPIED`, `TREE_DEST_NOT_OWNED`,
|
|
201
|
+
* `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; `FsFaultError` side
|
|
202
|
+
* `destination` for a destination the OS refuses to examine, on the fill's
|
|
203
|
+
* declared side for a copy source it refuses to read
|
|
204
|
+
*/
|
|
205
|
+
export declare function planTreeChanges(changes: readonly TreeChange[]): Promise<TreePlan>;
|
|
206
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/tree-change/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plan half of the tree-change primitive: every destructive verb describes
|
|
3
|
+
* what it wants as {@link TreeChange}s, and {@link planTreeChanges} decides, with
|
|
4
|
+
* no side effect, what will actually happen to each — before anything is written.
|
|
5
|
+
*
|
|
6
|
+
* The decisions, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. **What is there** at each destination (`lstat`; a destination the OS refuses
|
|
9
|
+
* to examine throws, classified `destination`; "absent" is believed only when
|
|
10
|
+
* the parent's listing agrees).
|
|
11
|
+
* 2. **Keep.** A `remove` with nothing there, or whose `keepIfSameAs` names an
|
|
12
|
+
* entry that is `same` **or `unknown`** to it, is kept, with the reason. Only
|
|
13
|
+
* the target's own unexaminability throws, never a sibling's. A `remove` that
|
|
14
|
+
* holds a kept entry (or cannot be proven not to) is kept too: a kept entry is
|
|
15
|
+
* kept whole, never taken away with a directory above it.
|
|
16
|
+
* 3. **Aliasing and containment, by identity.** A `remove` that is the same entry
|
|
17
|
+
* as a `replace` (`plugins/Old` and `plugins/old` on APFS or NTFS, a link and its
|
|
18
|
+
* target), or an earlier `remove`, or lies inside any other change's
|
|
19
|
+
* destination, is `subsumed`: that change parks it. `unknown` is never a merge
|
|
20
|
+
* and never a refusal — the two changes run as two, and a wrong guess fails the
|
|
21
|
+
* apply, which rolls back; refusing would refuse every change on a filesystem
|
|
22
|
+
* that reports `ino` 0. Any other two changes still active whose destinations
|
|
23
|
+
* are one entry or nest are refused ({@link TREE_DESTS_OVERLAP_CODE}): the
|
|
24
|
+
* calling verb's defect, never the user's input.
|
|
25
|
+
* 4. **Ownership** of whatever is there: `must-be-free` refuses anything but an
|
|
26
|
+
* empty directory ({@link TREE_DEST_OCCUPIED_CODE}); `vat-made` refuses what its
|
|
27
|
+
* `recognise` disowns ({@link TREE_DEST_NOT_OWNED_CODE}); `force` and
|
|
28
|
+
* `vat-state` take anything — except that a `replace-file` never takes a directory
|
|
29
|
+
* (or a link to one), under any ownership ({@link TREE_DEST_OCCUPIED_CODE}).
|
|
30
|
+
* 5. **Holding.** A `copy` source, or a `write` fill's declared `reads`, that is the
|
|
31
|
+
* destination or inside it ({@link TREE_DEST_HOLDS_SOURCE_CODE}: the replace
|
|
32
|
+
* would delete it — never asked of a destination that is a link, which holds
|
|
33
|
+
* nothing: only the link is parked), or that holds the destination
|
|
34
|
+
* ({@link TREE_SOURCE_HOLDS_DEST_CODE}: a copy into itself), is refused.
|
|
35
|
+
* 6. **Readable source.** Every `copy` source is proven readable (the one
|
|
36
|
+
* special-file and link policy, `proveTreeReadable`, on the fill's declared `side`).
|
|
37
|
+
*
|
|
38
|
+
* Utils names no refusal code: these are library codes, and the CLI's refusal map
|
|
39
|
+
* decides which refusal each is.
|
|
40
|
+
*/
|
|
41
|
+
import { lstatSync, readdirSync } from 'node:fs';
|
|
42
|
+
import { requireConfirmedAbsent } from '../errors/confirmed-absent.js';
|
|
43
|
+
import { isPathAbsentError } from '../errors/errno-table.js';
|
|
44
|
+
import { classifyFsFault, everywhere, withFsFaultSync } from '../errors/fs-fault.js';
|
|
45
|
+
import { VatError } from '../errors/vat-error.js';
|
|
46
|
+
import { forEachInOrder } from '../in-order.js';
|
|
47
|
+
import { followedStatSync } from '../link-following.js';
|
|
48
|
+
import { relativeEscapesRoot, safePath } from '../path-core.js';
|
|
49
|
+
import { identityOracle } from './identity.js';
|
|
50
|
+
import { proveTreeReadable } from './readable-tree.js';
|
|
51
|
+
/** A destination that must be absent or an empty directory already holds something. */
|
|
52
|
+
export const TREE_DEST_OCCUPIED_CODE = 'TREE_DEST_OCCUPIED';
|
|
53
|
+
/** A destination VAT may replace only when it made it holds something its recogniser disowns. */
|
|
54
|
+
export const TREE_DEST_NOT_OWNED_CODE = 'TREE_DEST_NOT_OWNED';
|
|
55
|
+
/**
|
|
56
|
+
* Two changes of one plan whose destinations are one entry or nest (other than a remove the other
|
|
57
|
+
* change takes with it): they cannot be one transaction — the outer park would carry the inner
|
|
58
|
+
* change's staging away. The calling verb built a plan it must not, so it is VAT's defect.
|
|
59
|
+
*/
|
|
60
|
+
export const TREE_DESTS_OVERLAP_CODE = 'TREE_DESTS_OVERLAP';
|
|
61
|
+
/** A copy's destination lies inside its source: the copy would copy into itself. */
|
|
62
|
+
export const TREE_SOURCE_HOLDS_DEST_CODE = 'TREE_SOURCE_HOLDS_DEST';
|
|
63
|
+
/** A copy's source is its destination or lies inside it: the replace would delete what it copies. */
|
|
64
|
+
export const TREE_DEST_HOLDS_SOURCE_CODE = 'TREE_DEST_HOLDS_SOURCE';
|
|
65
|
+
function refusal(code, message) {
|
|
66
|
+
return new VatError(code, message);
|
|
67
|
+
}
|
|
68
|
+
/** Why a remove is kept — and the sibling the OS refused to examine, when that is why — or `undefined` when it goes ahead. */
|
|
69
|
+
function keepReason(change, existing, facts) {
|
|
70
|
+
if (change.op !== 'remove')
|
|
71
|
+
return undefined;
|
|
72
|
+
if (existing === 'absent')
|
|
73
|
+
return { reason: 'nothing there' };
|
|
74
|
+
const others = change.keepIfSameAs?.() ?? [];
|
|
75
|
+
// The target's own refusal throws; only an OTHER entry's refusal is `unknown`, a keep.
|
|
76
|
+
if (others.length > 0)
|
|
77
|
+
facts.requireExaminable(change.dest);
|
|
78
|
+
for (const other of others) {
|
|
79
|
+
const verdict = facts.sameEntry(change.dest, other);
|
|
80
|
+
if (verdict === 'same')
|
|
81
|
+
return { reason: `${other} is the same entry` };
|
|
82
|
+
if (verdict === 'unknown') {
|
|
83
|
+
const reason = `could not tell whether ${other} is the same entry`;
|
|
84
|
+
return facts.refusesToExamine(other) ? { reason, unexaminedSibling: other } : { reason };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
/** The kept entry `planned` (a remove) holds, or may hold: removing it would take that entry with it. */
|
|
90
|
+
function heldKeep(planned, all, facts) {
|
|
91
|
+
if (planned.action !== 'remove')
|
|
92
|
+
return undefined;
|
|
93
|
+
return all.find((other) => other !== planned && other.action === 'keep' && other.existing !== 'absent' && facts.isInside(other.change.dest, planned.change.dest) !== 'outside');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Keep every remove that holds — or cannot be proven not to hold — a kept entry: a
|
|
97
|
+
* kept entry is kept whole, never taken away with a directory above it. Repeated until
|
|
98
|
+
* nothing changes, so a remove holding one that was just kept is kept as well.
|
|
99
|
+
*/
|
|
100
|
+
function keepingHolders(planned, facts) {
|
|
101
|
+
const next = planned.map((each) => {
|
|
102
|
+
const held = heldKeep(each, planned, facts);
|
|
103
|
+
return held === undefined ? each : { ...each, action: 'keep', reason: `holds ${held.change.dest}, which is kept` };
|
|
104
|
+
});
|
|
105
|
+
return next.some((each, index) => each !== planned[index]) ? keepingHolders(next, facts) : planned;
|
|
106
|
+
}
|
|
107
|
+
function initialAction(change, existing) {
|
|
108
|
+
if (change.op === 'remove')
|
|
109
|
+
return 'remove';
|
|
110
|
+
return existing === 'absent' ? 'create' : 'replace';
|
|
111
|
+
}
|
|
112
|
+
/** Whether `other` takes `change`'s destination with it: the same entry as a replace or an earlier remove, or an ancestor of it. */
|
|
113
|
+
function subsumes(change, index, other, otherIndex, facts) {
|
|
114
|
+
if (otherIndex === index || other.action === 'keep' || other.existing === 'absent')
|
|
115
|
+
return false;
|
|
116
|
+
const sameAllowed = other.change.op !== 'remove' || otherIndex < index;
|
|
117
|
+
if (sameAllowed && facts.sameEntry(change.change.dest, other.change.dest) === 'same')
|
|
118
|
+
return true;
|
|
119
|
+
return facts.isInside(change.change.dest, other.change.dest) === 'inside';
|
|
120
|
+
}
|
|
121
|
+
function withSubsumption(planned, index, all, facts) {
|
|
122
|
+
if (planned.action !== 'remove')
|
|
123
|
+
return planned;
|
|
124
|
+
const by = all.find((other, otherIndex) => subsumes(planned, index, other, otherIndex, facts));
|
|
125
|
+
return by === undefined ? planned : { ...planned, action: 'subsumed', reason: `parked by ${by.change.op} ${by.change.label}` };
|
|
126
|
+
}
|
|
127
|
+
/** Whether a planned change touches its destination (`keep` and `subsumed` do not). */
|
|
128
|
+
export const isActive = ({ action }) => action === 'create' || action === 'replace' || action === 'remove';
|
|
129
|
+
/**
|
|
130
|
+
* Whether `inner`'s destination is `outer`'s or lies under it. Judged by identity where `outer`
|
|
131
|
+
* exists; an absent destination has no identity, so there it is judged by the spelling.
|
|
132
|
+
*/
|
|
133
|
+
function holds(outer, inner, facts) {
|
|
134
|
+
if (outer.existing === 'absent')
|
|
135
|
+
return !relativeEscapesRoot(safePath.relative(outer.change.dest, inner.change.dest));
|
|
136
|
+
return facts.sameEntry(inner.change.dest, outer.change.dest) === 'same' || facts.isInside(inner.change.dest, outer.change.dest) === 'inside';
|
|
137
|
+
}
|
|
138
|
+
/** Refuse two active changes over one tree: the plan is the calling verb's defect. `unknown` is never proof. */
|
|
139
|
+
function checkOverlap(planned, facts) {
|
|
140
|
+
const active = planned.filter((each) => isActive(each));
|
|
141
|
+
for (const [index, first] of active.entries()) {
|
|
142
|
+
const second = active.slice(index + 1).find((other) => holds(first, other, facts) || holds(other, first, facts));
|
|
143
|
+
if (second !== undefined) {
|
|
144
|
+
throw refusal(TREE_DESTS_OVERLAP_CODE, `${first.change.label} (${first.change.dest}) and ${second.change.label} (${second.change.dest}) change one tree; a plan may not overlap its own destinations`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Refuse a FILE change whose destination is a directory, or a link to one — under every ownership,
|
|
150
|
+
* `force` included: `--force` means "overwrite the file", and a file landing on a directory's name
|
|
151
|
+
* would park the whole tree and delete it.
|
|
152
|
+
*/
|
|
153
|
+
function checkFileOverDirectory({ change, existing }, facts) {
|
|
154
|
+
if (change.op !== 'replace-file')
|
|
155
|
+
return;
|
|
156
|
+
const { dest, label } = change;
|
|
157
|
+
if (existing === 'directory') {
|
|
158
|
+
throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} is a directory; a file is never written over a directory`);
|
|
159
|
+
}
|
|
160
|
+
if (existing === 'link' && facts.linksToDirectory(dest)) {
|
|
161
|
+
throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} is a link to a directory; a file is never written over a directory`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Refuse a destination its ownership does not let this change take. */
|
|
165
|
+
function checkOwnership(planned, facts) {
|
|
166
|
+
const { change, existing, action } = planned;
|
|
167
|
+
if (existing === 'absent' || action === 'keep' || action === 'subsumed')
|
|
168
|
+
return;
|
|
169
|
+
checkFileOverDirectory(planned, facts);
|
|
170
|
+
const { ownership, dest, label } = change;
|
|
171
|
+
// An empty directory is free for a tree that lands in its place, never for a FILE: a file
|
|
172
|
+
// replacing a directory the user made (a folder they named as an output) is a surprise, not a fill.
|
|
173
|
+
const freeDirectory = change.op !== 'replace-file' && existing === 'directory' && facts.isEmptyDirectory(dest);
|
|
174
|
+
// The refusal says what this change would have done to the destination.
|
|
175
|
+
const verb = change.op === 'remove' ? 'remove' : 'replace';
|
|
176
|
+
if (ownership.kind === 'must-be-free' && !freeDirectory) {
|
|
177
|
+
throw refusal(TREE_DEST_OCCUPIED_CODE, `${label}: ${dest} already holds something (a ${existing}); refusing to ${verb} it`);
|
|
178
|
+
}
|
|
179
|
+
if (ownership.kind === 'vat-made') {
|
|
180
|
+
const verdict = ownership.recognise(dest);
|
|
181
|
+
if (!verdict.owned)
|
|
182
|
+
throw refusal(TREE_DEST_NOT_OWNED_CODE, `${label}: refusing to ${verb} ${dest}: ${verdict.reason}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** The inputs a replace reads, which its destination must neither be, hold, nor lie inside. */
|
|
186
|
+
function readsOf(change) {
|
|
187
|
+
if (change.op !== 'replace')
|
|
188
|
+
return [];
|
|
189
|
+
if (change.fill.from === 'copy')
|
|
190
|
+
return [change.fill.source];
|
|
191
|
+
return change.fill.from === 'write' ? change.fill.reads ?? [] : [];
|
|
192
|
+
}
|
|
193
|
+
function checkHolding(planned, facts) {
|
|
194
|
+
if (planned.action !== 'create' && planned.action !== 'replace')
|
|
195
|
+
return;
|
|
196
|
+
const { dest, label } = planned.change;
|
|
197
|
+
// A destination that is a LINK holds nothing: the replace parks the link itself, never what it points
|
|
198
|
+
// at — so a source the link leads to (a `--dev` link to the build being installed) is not lost with it.
|
|
199
|
+
const destHoldsEntries = planned.existing !== 'link';
|
|
200
|
+
for (const source of readsOf(planned.change)) {
|
|
201
|
+
if (destHoldsEntries && (facts.sameEntry(source, dest) === 'same' || facts.isInside(source, dest) === 'inside')) {
|
|
202
|
+
throw refusal(TREE_DEST_HOLDS_SOURCE_CODE, `${label}: ${source} is ${dest} or lies inside it, so replacing ${dest} would delete it`);
|
|
203
|
+
}
|
|
204
|
+
if (facts.isInside(dest, source) === 'inside') {
|
|
205
|
+
throw refusal(TREE_SOURCE_HOLDS_DEST_CODE, `${label}: ${dest} lies inside ${source}, which it is made from`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function describeChange({ change, action, reason }) {
|
|
210
|
+
const line = `${action} ${change.label} ${change.dest}`;
|
|
211
|
+
return reason === undefined ? line : `${line} (${reason})`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The pure planner: every decision of {@link planTreeChanges} but the source
|
|
215
|
+
* proof, made from `facts`.
|
|
216
|
+
*
|
|
217
|
+
* @throws VatError coded `TREE_DESTS_OVERLAP` (a defect in the caller's plan), `TREE_DEST_OCCUPIED`,
|
|
218
|
+
* `TREE_DEST_NOT_OWNED`, `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; whatever `facts` throws
|
|
219
|
+
*/
|
|
220
|
+
export function planFromFacts(changes, facts) {
|
|
221
|
+
const drafted = changes.map((change) => {
|
|
222
|
+
const existing = facts.existing(change.dest);
|
|
223
|
+
const kept = keepReason(change, existing, facts);
|
|
224
|
+
return kept === undefined ? { change, existing, action: initialAction(change, existing) } : { change, existing, action: 'keep', ...kept };
|
|
225
|
+
});
|
|
226
|
+
const kept = keepingHolders(drafted, facts);
|
|
227
|
+
const planned = kept.map((draft, index) => withSubsumption(draft, index, kept, facts));
|
|
228
|
+
checkOverlap(planned, facts);
|
|
229
|
+
for (const each of planned)
|
|
230
|
+
checkOwnership(each, facts);
|
|
231
|
+
for (const each of planned)
|
|
232
|
+
checkHolding(each, facts);
|
|
233
|
+
return { changes: planned, describe: () => planned.map((each) => describeChange(each)) };
|
|
234
|
+
}
|
|
235
|
+
const EXAMINE_DEST = { side: 'destination', action: 'examine the destination' };
|
|
236
|
+
function kindOf(dest) {
|
|
237
|
+
let stats;
|
|
238
|
+
try {
|
|
239
|
+
stats = lstatSync(dest);
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
if (!isPathAbsentError(error))
|
|
243
|
+
throw classifyFsFault(error, { ...EXAMINE_DEST, path: dest });
|
|
244
|
+
// Absent only when the parent's listing agrees: a refused probe of an entry that is there is a fault.
|
|
245
|
+
requireConfirmedAbsent(dest, error, { action: EXAMINE_DEST.action, path: dest }, { follows: false, sideOf: everywhere(EXAMINE_DEST.side) });
|
|
246
|
+
return 'absent';
|
|
247
|
+
}
|
|
248
|
+
if (stats.isSymbolicLink())
|
|
249
|
+
return 'link';
|
|
250
|
+
if (stats.isDirectory())
|
|
251
|
+
return 'directory';
|
|
252
|
+
return stats.isFile() ? 'file' : 'special';
|
|
253
|
+
}
|
|
254
|
+
/** Whether the link at `dest` leads to a directory: a dangling link leads nowhere; a target the OS refuses to examine is a fault. */
|
|
255
|
+
function linksToDirectory(dest) {
|
|
256
|
+
try {
|
|
257
|
+
// `dest` is the destination the verb names: the operator's own path, its own link the edge.
|
|
258
|
+
return followedStatSync(dest, dest)?.isDirectory() ?? false;
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (isPathAbsentError(error))
|
|
262
|
+
return false;
|
|
263
|
+
throw classifyFsFault(error, { ...EXAMINE_DEST, path: dest });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* What is at `dest` NOW, for the apply to hold against what the plan decided
|
|
268
|
+
* (`changedSincePlan`). The emptiness of a directory is read only where the plan took it as free.
|
|
269
|
+
*
|
|
270
|
+
* @throws FsFaultError side `destination` when the OS refuses to examine or list it
|
|
271
|
+
*/
|
|
272
|
+
export function destinationNow(dest, planned) {
|
|
273
|
+
const kind = kindOf(dest);
|
|
274
|
+
const askEmpty = kind === 'directory' && planned.existing === 'directory' && planned.change.ownership.kind === 'must-be-free';
|
|
275
|
+
return { kind, emptyDirectory: askEmpty && withFsFaultSync({ side: 'destination', action: 'list the destination', path: dest }, () => readdirSync(dest).length === 0) };
|
|
276
|
+
}
|
|
277
|
+
/** The live facts of one plan: every identity question asks each entry once, so no two decisions see the filesystem differently. */
|
|
278
|
+
function liveFacts() {
|
|
279
|
+
const oracle = identityOracle();
|
|
280
|
+
return {
|
|
281
|
+
existing: kindOf,
|
|
282
|
+
isEmptyDirectory: (dest) => withFsFaultSync({ side: 'destination', action: 'list the destination', path: dest }, () => readdirSync(dest).length === 0),
|
|
283
|
+
linksToDirectory,
|
|
284
|
+
sameEntry: oracle.sameEntry,
|
|
285
|
+
isInside: oracle.isInside,
|
|
286
|
+
requireExaminable: (dest) => {
|
|
287
|
+
oracle.identities(dest);
|
|
288
|
+
},
|
|
289
|
+
refusesToExamine: (entry) => oracle.refuses(entry),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/** Prove a copy source readable, as the copy will read it. */
|
|
293
|
+
function proveSource({ change, action }) {
|
|
294
|
+
if (change.op !== 'replace' || change.fill.from !== 'copy' || (action !== 'create' && action !== 'replace'))
|
|
295
|
+
return Promise.resolve();
|
|
296
|
+
const { source, links, side, filter } = change.fill;
|
|
297
|
+
return proveTreeReadable(source, { links, side, ...(filter === undefined ? {} : { filter }) });
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Decide every change against the filesystem as it is, with no side effect. Each
|
|
301
|
+
* destination is taken in its canonical spelling (`safePath.resolve`: absolute,
|
|
302
|
+
* `.`/`..` resolved, forward slashes); `describe()` prints that spelling.
|
|
303
|
+
* Hand the result to `applyTreePlan`; print `describe()` for a dry run — the two
|
|
304
|
+
* cannot disagree, because the apply does exactly what the plan says.
|
|
305
|
+
*
|
|
306
|
+
* @param changes - What the verb wants, in the order it wants it
|
|
307
|
+
* @throws VatError coded `TREE_DEST_OCCUPIED`, `TREE_DEST_NOT_OWNED`,
|
|
308
|
+
* `TREE_DEST_HOLDS_SOURCE` or `TREE_SOURCE_HOLDS_DEST`; `FsFaultError` side
|
|
309
|
+
* `destination` for a destination the OS refuses to examine, on the fill's
|
|
310
|
+
* declared side for a copy source it refuses to read
|
|
311
|
+
*/
|
|
312
|
+
export async function planTreeChanges(changes) {
|
|
313
|
+
// One canonical spelling of every destination (absolute, normalized, forward slashes), which the
|
|
314
|
+
// plan, the dry run and the apply — down to the parents a create makes and removes — all use.
|
|
315
|
+
const canonical = changes.map((change) => ({ ...change, dest: safePath.resolve(change.dest) }));
|
|
316
|
+
const plan = planFromFacts(canonical, liveFacts());
|
|
317
|
+
await forEachInOrder(plan.changes, proveSource);
|
|
318
|
+
return plan;
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/tree-change/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The readable-tree proof: before a verb writes anything for a tree it is about to
|
|
3
|
+
* read whole, it proves every entry readable, so an unreadable input is refused as
|
|
4
|
+
* the input it is rather than surfacing half-way through as an output failure.
|
|
5
|
+
*/
|
|
6
|
+
import type { FsSide } from '../errors/fs-fault.js';
|
|
7
|
+
import { type TreeWalkOptions } from './tree-walk.js';
|
|
8
|
+
/** What {@link proveTreeReadable} is told: the walk's link policy and filter, and the side its faults are on. */
|
|
9
|
+
export interface ProveTreeReadableOptions extends TreeWalkOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The side of the verb the tree is on: an input (`source`), VAT's staging
|
|
12
|
+
* (`environment`), or a copy already made into user state (`destination`).
|
|
13
|
+
*/
|
|
14
|
+
readonly side: FsSide;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Prove the directory `root` readable: every directory listed, every regular file
|
|
18
|
+
* opened without blocking and judged by `fstat` on that handle, each entry held to
|
|
19
|
+
* the one special-file and link policy `copyTree` copies by (see `tree-walk.ts`).
|
|
20
|
+
* A root that is not a directory is refused by its listing (`ENOTDIR`), as
|
|
21
|
+
* `copyTree` refuses it. Each file is opened, never `access`ed: on Windows `access`
|
|
22
|
+
* does not consult ACLs.
|
|
23
|
+
*
|
|
24
|
+
* @param root - The directory the caller is about to read
|
|
25
|
+
* @param options - `links` and `filter` as the read will use them, and the `side` the tree is on
|
|
26
|
+
* @throws FsFaultError on `options.side` (origin `content`) naming the first entry
|
|
27
|
+
* the OS refused, in listing order — a named pipe, socket or device as class
|
|
28
|
+
* `wrong-type`; `CopyLinkEscapesSourceError` / `DirectoryWalkRevisitedError` for
|
|
29
|
+
* a link out of, or back into, a `follow-contained` tree
|
|
30
|
+
*/
|
|
31
|
+
export declare function proveTreeReadable(root: string, options: ProveTreeReadableOptions): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Read a regular file whole, through a handle opened without blocking and judged
|
|
34
|
+
* by `fstat` on that handle: a named pipe, socket or device is refused before a
|
|
35
|
+
* byte is read, never waited on.
|
|
36
|
+
*
|
|
37
|
+
* @param path - The file to read
|
|
38
|
+
* @returns Its bytes
|
|
39
|
+
* @throws an `EFTYPE` errno for a special file, `EISDIR` for a directory, and
|
|
40
|
+
* whatever `open` / `read` throw otherwise — raw, for the caller to classify on
|
|
41
|
+
* the side it knows the file is on
|
|
42
|
+
*/
|
|
43
|
+
export declare function readRegularFile(path: string): Promise<Buffer>;
|
|
44
|
+
//# sourceMappingURL=readable-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readable-tree.d.ts","sourceRoot":"","sources":["../../src/tree-change/readable-tree.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EAAoB,KAAK,eAAe,EAAY,MAAM,gBAAgB,CAAC;AAElF,iHAAiH;AACjH,MAAM,WAAW,wBAAyB,SAAQ,eAAe;IAC/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtG;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnE"}
|