@vibe-agent-toolkit/utils 0.2.0-rc.11 → 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 +111 -9
- 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 +2 -0
- package/dist/env-flag.d.ts.map +1 -1
- package/dist/env-flag.js +2 -0
- 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 +4 -4
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +4 -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 +3 -1
- package/dist/path-containment.d.ts.map +1 -1
- package/dist/path-containment.js +13 -7
- 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-file.d.ts +14 -1
- package/dist/text-file.d.ts.map +1 -1
- package/dist/text-file.js +41 -8
- package/dist/text-file.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 +3 -16
- 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,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry identity: are two names ONE entry on disk?
|
|
3
|
+
*
|
|
4
|
+
* Two changes to a tree can alias without sharing a spelling — `plugins/Old` and
|
|
5
|
+
* `plugins/old` on APFS or NTFS, a link and its target, two links to one target.
|
|
6
|
+
* Comparing names cannot see it, and acting on a wrong "different" deletes what the
|
|
7
|
+
* user just installed. So identity is asked of the filesystem:
|
|
8
|
+
*
|
|
9
|
+
* - `dev` and `ino` both nonzero → the id `${dev}:${ino}`, from a `bigint` `lstat` of
|
|
10
|
+
* the entry, plus a `stat` of a link's target. A link answers to BOTH: removing it
|
|
11
|
+
* removes only the link, but it aliases its target.
|
|
12
|
+
* - Otherwise (a filesystem that reports `ino` 0) → a folded real path: the real
|
|
13
|
+
* parent, a `/`, the base name, NFC then lower-cased. The fold can only err toward
|
|
14
|
+
* "same", which keeps what it is asked to remove.
|
|
15
|
+
*
|
|
16
|
+
* An entry the OS refuses to examine is `unknown`, never `different`: the caller
|
|
17
|
+
* keeps. {@link entryIdentities} throws the refusal as an `FsFaultError` on the side
|
|
18
|
+
* the CALLER names, so whatever surfaces it reports a classified fault, not a raw errno.
|
|
19
|
+
*/
|
|
20
|
+
import { lstatSync } from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { requireConfirmedAbsent } from '../errors/confirmed-absent.js';
|
|
23
|
+
import { isPathAbsentError } from '../errors/errno-table.js';
|
|
24
|
+
import { classifyFsFault, everywhere, isFsFaultError } from '../errors/fs-fault.js';
|
|
25
|
+
import { edgeBigintStatSync } from '../link-following.js';
|
|
26
|
+
import { canonicalPath } from '../path-containment.js';
|
|
27
|
+
import { safePath, toForwardSlash, toNfc } from '../path-core.js';
|
|
28
|
+
import { normalizePath } from '../path-utils.js';
|
|
29
|
+
import { identityOf, identityOracleOver, insideBy, sameBy } from './identity-compare.js';
|
|
30
|
+
/** Run `work` for `entry`; a filesystem refusal is rethrown classified on `side`, anything else as it was. */
|
|
31
|
+
function guarded(entry, side, work) {
|
|
32
|
+
try {
|
|
33
|
+
return work();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw classifyFsFault(error, { side, action: 'examine the entry', path: entry });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** The fold of `real` (an absolute path): its real parent, a `/`, its base name, NFC then lower-cased. */
|
|
40
|
+
function foldedRealPath(entry, side, real) {
|
|
41
|
+
return guarded(entry, side, () => ({
|
|
42
|
+
foldedRealPath: toNfc(`${toForwardSlash(normalizePath(path.dirname(real), path.dirname(real)))}/${path.basename(real)}`).toLowerCase(),
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `stat(entry)` for an entry that may be absent: `undefined` when nothing is there — believed only when
|
|
47
|
+
* the parent's listing agrees — and a classified fault when the OS refuses.
|
|
48
|
+
*/
|
|
49
|
+
function examine(entry, side, follows) {
|
|
50
|
+
const ctx = { side, action: 'examine the entry', path: entry };
|
|
51
|
+
try {
|
|
52
|
+
return follows ? edgeBigintStatSync(entry) : lstatSync(entry, { bigint: true });
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!isPathAbsentError(error))
|
|
56
|
+
throw classifyFsFault(error, ctx);
|
|
57
|
+
// A `stat` follows a link (a dangling one is absent); an `lstat` sees the link itself.
|
|
58
|
+
requireConfirmedAbsent(entry, error, { action: ctx.action, path: entry }, { follows, sideOf: everywhere(side) });
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Every identity `entry` answers to: `[]` when nothing is there; its own (`lstat`) for
|
|
64
|
+
* anything; plus its target's (`stat`) when it is a link that resolves.
|
|
65
|
+
*
|
|
66
|
+
* @param entry - Absolute path to identify
|
|
67
|
+
* @param side - The side of the verb that named `entry`; every fault (the `lstat`, the `stat`, the real path of a fold) is raised on it
|
|
68
|
+
* @throws FsFaultError on `side` when the OS refuses to examine `entry`
|
|
69
|
+
*/
|
|
70
|
+
export function entryIdentities(entry, side) {
|
|
71
|
+
const own = examine(entry, side, false);
|
|
72
|
+
if (own === undefined)
|
|
73
|
+
return [];
|
|
74
|
+
const identities = [identityOf(own, () => foldedRealPath(entry, side, entry))];
|
|
75
|
+
if (!own.isSymbolicLink())
|
|
76
|
+
return identities;
|
|
77
|
+
// Every entry identified here is one the verb itself names (a destination it writes or removes):
|
|
78
|
+
// the operator's own path, its own link the edge the kernel answers.
|
|
79
|
+
const target = examine(entry, side, true);
|
|
80
|
+
if (target !== undefined) {
|
|
81
|
+
identities.push(identityOf(target, () => foldedRealPath(entry, side, guarded(entry, side, () => normalizePath(entry, entry)))));
|
|
82
|
+
}
|
|
83
|
+
return identities;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The identities of `entry`, or `undefined` when the OS refuses to examine it. The
|
|
87
|
+
* side is irrelevant here: a refusal becomes `unknown` and never surfaces.
|
|
88
|
+
*/
|
|
89
|
+
function identitiesOrUndecided(entry) {
|
|
90
|
+
try {
|
|
91
|
+
return entryIdentities(entry, 'destination');
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (isFsFaultError(error))
|
|
95
|
+
return undefined;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Where `entry` physically is — its own place (its real parent directory, then its name) and, for a
|
|
101
|
+
* link, its target's — or `undefined` when the OS refuses to resolve it. `entry` need not exist: it
|
|
102
|
+
* is resolved from its deepest existing ancestor.
|
|
103
|
+
*/
|
|
104
|
+
function physicalSpellings(entry) {
|
|
105
|
+
try {
|
|
106
|
+
return [safePath.join(canonicalPath(path.dirname(entry), path.dirname(entry)), path.basename(entry)), canonicalPath(entry, entry)];
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// A refusal is `unknown` to the caller, exactly as an entry it cannot examine is; a defect stays loud.
|
|
110
|
+
if (isFsFaultError(classifyFsFault(error, { side: 'destination', action: 'resolve the entry', path: entry })))
|
|
111
|
+
return undefined;
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Whether `a` and `b` are one entry. Any shared identity is `same`; identities on
|
|
117
|
+
* both sides with a filesystem id each and none shared is `different`; a folded path
|
|
118
|
+
* on either side that does not match is `unknown`; an entry the OS refuses to
|
|
119
|
+
* examine is `unknown`; an absent entry is `different` (nothing there can alias).
|
|
120
|
+
*
|
|
121
|
+
* @param a - Absolute path
|
|
122
|
+
* @param b - Absolute path
|
|
123
|
+
*/
|
|
124
|
+
export function sameEntry(a, b) {
|
|
125
|
+
return sameBy(identitiesOrUndecided, a, b);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Whether `child` lies strictly under `ancestor`, judged by identity: some directory
|
|
129
|
+
* on the way up from `child` — along its spelling as given, and along where it
|
|
130
|
+
* physically is — is the same entry as `ancestor`. So a linked or case-aliased
|
|
131
|
+
* spelling of the ancestor is found, and so is a directory of `child`'s spelling that
|
|
132
|
+
* is a link to somewhere BELOW the ancestor. `child` need not exist.
|
|
133
|
+
* `unknown` when no ancestor proved `same` and one could not be examined.
|
|
134
|
+
*
|
|
135
|
+
* ⚠️ On a filesystem that reports `ino` 0 the answer for an existing `ancestor` is
|
|
136
|
+
* `unknown`, never `outside`: a fold cannot prove two names apart. A caller that
|
|
137
|
+
* refuses on `inside` must not refuse on `unknown`, or every copy there is refused.
|
|
138
|
+
*
|
|
139
|
+
* @param child - Absolute path
|
|
140
|
+
* @param ancestor - Absolute path
|
|
141
|
+
*/
|
|
142
|
+
export function isInsideByIdentity(child, ancestor) {
|
|
143
|
+
return insideBy(identitiesOrUndecided, child, ancestor, physicalSpellings);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* {@link sameEntry} and {@link isInsideByIdentity} over one memo: each entry is examined once
|
|
147
|
+
* (`entryIdentities(entry, 'destination')`), so every answer of a plan is built from one observation.
|
|
148
|
+
*/
|
|
149
|
+
export function identityOracle() {
|
|
150
|
+
return identityOracleOver((entry) => entryIdentities(entry, 'destination'), physicalSpellings);
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/tree-change/identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The plan/apply tree-change primitive: entry identity, the readable-tree proof and the copy, the planner, the applier and the file helpers. */
|
|
2
|
+
export { entryIdentities, isInsideByIdentity, sameEntry } from './identity.js';
|
|
3
|
+
export type { EntryContainment, EntrySameness, Identity } from './identity-compare.js';
|
|
4
|
+
export { COPY_LINK_ESCAPES_SOURCE_CODE, CopyLinkEscapesSourceError, DIRECTORY_WALK_REVISITED_CODE, DirectoryWalkRevisitedError, FollowedWalk, } from './followed-walk.js';
|
|
5
|
+
export { proveTreeReadable, readRegularFile } from './readable-tree.js';
|
|
6
|
+
export type { ProveTreeReadableOptions } from './readable-tree.js';
|
|
7
|
+
export { copyRegularFile, copyTree } from './copy-tree.js';
|
|
8
|
+
export type { CopyRegularFileOptions, CopyTreeOptions } from './copy-tree.js';
|
|
9
|
+
export type { CopyOnto } from './copy-decisions.js';
|
|
10
|
+
export type { LinkPolicy, TreeWalkOptions } from './tree-walk.js';
|
|
11
|
+
export { refuseEscapingLinks, treeChain, treeContainment } from './link-containment.js';
|
|
12
|
+
export type { LinkContainmentCheck } from './link-containment.js';
|
|
13
|
+
export { isLinkChainRefusedError } from './host-chain.js';
|
|
14
|
+
export { HOST_PATH_FLAVOR, isWithinUnder, untrustedLinkRefusal, untrustedTextRefusal, win32FoldedName } from './link-chain.js';
|
|
15
|
+
export { planTreeChanges, TREE_DEST_HOLDS_SOURCE_CODE, TREE_DEST_NOT_OWNED_CODE, TREE_DEST_OCCUPIED_CODE, TREE_DESTS_OVERLAP_CODE, TREE_SOURCE_HOLDS_DEST_CODE, } from './plan.js';
|
|
16
|
+
export type { EntryKind, FileContents, Ownership, OwnershipVerdict, PlannedAction, PlannedChange, TreeChange, TreeFill, TreePlan } from './plan.js';
|
|
17
|
+
export { applyTreePlan, applyTreePlanOrLeftover, TREE_CLEANUP_INCOMPLETE_CODE } from './apply.js';
|
|
18
|
+
export { TREE_ROLLBACK_INCOMPLETE_CODE, TreeRollbackIncompleteError } from './rollback-error.js';
|
|
19
|
+
export type { TreeRollbackStranded } from './rollback-error.js';
|
|
20
|
+
export type { ApplyOptions, ApplyOutcome, ApplyResult, TreeChangeWarning } from './apply.js';
|
|
21
|
+
export type { TempDirOutcome } from './files.js';
|
|
22
|
+
export { disposeTempDir, disposeTempDirAfterFailure, disposeTempDirSync, makeDirectoryUnder, renameFileAtomic, replaceFile, TEMP_DIR_OUTSIDE_TMPDIR_CODE, withTempDir, writeFileUnder } from './files.js';
|
|
23
|
+
export { isParkedTreeEntry, isTreeChangeResidue } from './staging-names.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tree-change/index.ts"],"names":[],"mappings":"AAAA,iJAAiJ;AACjJ,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9E,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxF,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/H,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpJ,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7F,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,4BAA4B,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1M,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The plan/apply tree-change primitive: entry identity, the readable-tree proof and the copy, the planner, the applier and the file helpers. */
|
|
2
|
+
export { entryIdentities, isInsideByIdentity, sameEntry } from './identity.js';
|
|
3
|
+
export { COPY_LINK_ESCAPES_SOURCE_CODE, CopyLinkEscapesSourceError, DIRECTORY_WALK_REVISITED_CODE, DirectoryWalkRevisitedError, FollowedWalk, } from './followed-walk.js';
|
|
4
|
+
export { proveTreeReadable, readRegularFile } from './readable-tree.js';
|
|
5
|
+
export { copyRegularFile, copyTree } from './copy-tree.js';
|
|
6
|
+
export { refuseEscapingLinks, treeChain, treeContainment } from './link-containment.js';
|
|
7
|
+
export { isLinkChainRefusedError } from './host-chain.js';
|
|
8
|
+
export { HOST_PATH_FLAVOR, isWithinUnder, untrustedLinkRefusal, untrustedTextRefusal, win32FoldedName } from './link-chain.js';
|
|
9
|
+
export { planTreeChanges, TREE_DEST_HOLDS_SOURCE_CODE, TREE_DEST_NOT_OWNED_CODE, TREE_DEST_OCCUPIED_CODE, TREE_DESTS_OVERLAP_CODE, TREE_SOURCE_HOLDS_DEST_CODE, } from './plan.js';
|
|
10
|
+
export { applyTreePlan, applyTreePlanOrLeftover, TREE_CLEANUP_INCOMPLETE_CODE } from './apply.js';
|
|
11
|
+
export { TREE_ROLLBACK_INCOMPLETE_CODE, TreeRollbackIncompleteError } from './rollback-error.js';
|
|
12
|
+
export { disposeTempDir, disposeTempDirAfterFailure, disposeTempDirSync, makeDirectoryUnder, renameFileAtomic, replaceFile, TEMP_DIR_OUTSIDE_TMPDIR_CODE, withTempDir, writeFileUnder } from './files.js';
|
|
13
|
+
export { isParkedTreeEntry, isTreeChangeResidue } from './staging-names.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tree-change/index.ts"],"names":[],"mappings":"AAAA,iJAAiJ;AACjJ,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,EACL,6BAA6B,EAC7B,0BAA0B,EAC1B,6BAA6B,EAC7B,2BAA2B,EAC3B,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI3D,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExF,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/H,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,6BAA6B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAIjG,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,4BAA4B,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1M,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE resolver of a symbolic-link chain: where a path leads, walked hop by hop from what
|
|
3
|
+
* `lstat` and `readlink` say alone, so nothing is handed to the kernel until the whole chain has
|
|
4
|
+
* been read as text and shown to be local.
|
|
5
|
+
*
|
|
6
|
+
* That order is the point. A Windows kernel asked to resolve a path through a link to
|
|
7
|
+
* `\\host\share` opens an SMB session and offers the operator's NTLM credentials to that host
|
|
8
|
+
* before any answer comes back — so a `realpath` or a `stat` that follows links is a SECOND lock,
|
|
9
|
+
* asked only once this walk has proved the chain local, never the first.
|
|
10
|
+
*
|
|
11
|
+
* Pure: the caller supplies the path {@link PathFlavor} and the probe, so a test runs Windows
|
|
12
|
+
* semantics on any host. The flavor decides two things the kernel decides:
|
|
13
|
+
*
|
|
14
|
+
* - what separates a link text's components, and what makes it absolute or a network path
|
|
15
|
+
* (`\\host\share`, `\\?\UNC\…`, `\\.\…`, `//host/share` under win32; on POSIX a backslash is a
|
|
16
|
+
* character of a name and `//x` is the local root);
|
|
17
|
+
* - what `..` means. POSIX resolves a link before the `..` after it, physically (the parent of
|
|
18
|
+
* where the link LED). Windows joins a relative link text onto the link's directory and collapses
|
|
19
|
+
* every `..` lexically before reparsing — `x\..\y` is `y` whether `x` exists, is a file or a link —
|
|
20
|
+
* so under win32 each splice is collapsed by string first, and every component it leaves is examined.
|
|
21
|
+
*
|
|
22
|
+
* Running out of hops ({@link MAX_LINK_HOPS}) is a refusal, never an answer: a chain the walk did
|
|
23
|
+
* not finish reading is a chain the kernel would finish for it. A CYCLE is different, and is told
|
|
24
|
+
* apart exactly: the walk meets the same link with the same text left to read, so every text in
|
|
25
|
+
* the cycle has been read and is local, and the kernel can only answer `ELOOP` — it resolves to
|
|
26
|
+
* nothing, as a dangling link does, in every lane.
|
|
27
|
+
*
|
|
28
|
+
* An untrusted tree ({@link ChainRule.outside} `refuse`) may be read on either host — a clone made
|
|
29
|
+
* here is installed there — so its every link text is also held to the OTHER flavor's rules
|
|
30
|
+
* ({@link untrustedTextRefusal}): a text that is absolute, drive-relative or a network path on
|
|
31
|
+
* either kernel is refused on both.
|
|
32
|
+
*/
|
|
33
|
+
/** What stands at a place, a link there NOT followed. */
|
|
34
|
+
export type PlaceKind = 'directory' | 'file' | 'link' | 'absent';
|
|
35
|
+
/** Whose path rules a link's text is read by. Bound from `process.platform` at the edge ({@link HOST_PATH_FLAVOR}). */
|
|
36
|
+
export type PathFlavor = 'posix' | 'win32';
|
|
37
|
+
/** The flavor of the host VAT runs on — the edge where a pure caller's flavor is bound. */
|
|
38
|
+
export declare const HOST_PATH_FLAVOR: PathFlavor;
|
|
39
|
+
/**
|
|
40
|
+
* How many links one resolution follows before it refuses. Windows' limit — 63 reparse points in
|
|
41
|
+
* one path — is the larger of the two kernels' (Linux's `MAXSYMLINKS` is 40), so no chain a kernel
|
|
42
|
+
* would still follow is ever cut short and answered.
|
|
43
|
+
*/
|
|
44
|
+
export declare const MAX_LINK_HOPS = 63;
|
|
45
|
+
/** Why a chain is refused before the kernel is asked anything, with the link whose text decided it. */
|
|
46
|
+
export type ChainRefusal =
|
|
47
|
+
/** A network or device path, at any hop: never handed to the kernel, in any lane. */
|
|
48
|
+
{
|
|
49
|
+
readonly why: 'network';
|
|
50
|
+
readonly link: string;
|
|
51
|
+
readonly text: string;
|
|
52
|
+
}
|
|
53
|
+
/** An absolute local text, in a tree whose links may not leave it ({@link ChainRule.outside} `refuse`). */
|
|
54
|
+
| {
|
|
55
|
+
readonly why: 'absolute';
|
|
56
|
+
readonly link: string;
|
|
57
|
+
readonly text: string;
|
|
58
|
+
}
|
|
59
|
+
/** A drive-relative text (`C:x`): it names a place relative to a per-drive working directory no walk can know. */
|
|
60
|
+
| {
|
|
61
|
+
readonly why: 'drive-relative';
|
|
62
|
+
readonly link: string;
|
|
63
|
+
readonly text: string;
|
|
64
|
+
}
|
|
65
|
+
/** A text holding a NUL, or a component ending in a dot or a space, in a tree whose links may not leave it: not the text a kernel resolves. */
|
|
66
|
+
| {
|
|
67
|
+
readonly why: 'unportable';
|
|
68
|
+
readonly link: string;
|
|
69
|
+
readonly text: string;
|
|
70
|
+
}
|
|
71
|
+
/** A step above the tree's root, in a tree whose links may not leave it. */
|
|
72
|
+
| {
|
|
73
|
+
readonly why: 'climbs-out';
|
|
74
|
+
readonly link: string;
|
|
75
|
+
readonly text: string;
|
|
76
|
+
}
|
|
77
|
+
/** More than {@link MAX_LINK_HOPS} links that are not a cycle: a chain longer than either kernel follows. */
|
|
78
|
+
| {
|
|
79
|
+
readonly why: 'hop-limit';
|
|
80
|
+
readonly link: string;
|
|
81
|
+
};
|
|
82
|
+
/** Where a chain leads, as far as text can tell. */
|
|
83
|
+
export type LinkChain =
|
|
84
|
+
/** A local path, every link on the way read: the kernel may now be asked about it. */
|
|
85
|
+
{
|
|
86
|
+
readonly kind: 'resolved';
|
|
87
|
+
readonly path: string;
|
|
88
|
+
readonly final: 'directory' | 'file';
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* It leads to nothing: a component is absent or a file stands where a directory must
|
|
92
|
+
* (`absent`), or the chain comes back to a link it already read with the same text left
|
|
93
|
+
* (`cycle` — what the kernel answers `ELOOP` for).
|
|
94
|
+
*/
|
|
95
|
+
| {
|
|
96
|
+
readonly kind: 'nothing';
|
|
97
|
+
readonly at: string;
|
|
98
|
+
readonly cause: 'absent' | 'cycle';
|
|
99
|
+
}
|
|
100
|
+
/** It may not be resolved at all. */
|
|
101
|
+
| {
|
|
102
|
+
readonly kind: 'refused';
|
|
103
|
+
readonly refusal: ChainRefusal;
|
|
104
|
+
};
|
|
105
|
+
/** The tree a chain is resolved in, and what it may do outside it. */
|
|
106
|
+
export interface ChainRule {
|
|
107
|
+
readonly flavor: PathFlavor;
|
|
108
|
+
/** The tree's root, canonical (its own components are real directories). */
|
|
109
|
+
readonly root: string;
|
|
110
|
+
/**
|
|
111
|
+
* - `refuse`: an untrusted tree (a clone, an archive, a tree an install copies) — an absolute or
|
|
112
|
+
* drive-relative text, or any step above the root, is refused, and nothing outside is probed.
|
|
113
|
+
* - `follow`: the user's own tree — an absolute local text is followed and walked by the same
|
|
114
|
+
* rules, so a later hop's network text is still caught; where the chain ends is the caller's call.
|
|
115
|
+
*/
|
|
116
|
+
readonly outside: 'refuse' | 'follow';
|
|
117
|
+
}
|
|
118
|
+
/** What the walk may ask the filesystem: never anything that follows a link. */
|
|
119
|
+
export interface ChainProbe {
|
|
120
|
+
/** What stands at `path`, a link there NOT followed (`lstat`). */
|
|
121
|
+
readonly kindAt: (path: string) => PlaceKind;
|
|
122
|
+
/** The text of the link at `path` (`readlink`). */
|
|
123
|
+
readonly textAt: (path: string) => string;
|
|
124
|
+
}
|
|
125
|
+
declare const CLIMBS_OUT = "climbs-out";
|
|
126
|
+
/**
|
|
127
|
+
* The root a NAMED path hangs from — `/`, a drive's `C:/`, a share's `//host/share/` — for a walk
|
|
128
|
+
* of the user's own path, which may lead anywhere local.
|
|
129
|
+
*
|
|
130
|
+
* @param path - Absolute, forward- or back-slashed
|
|
131
|
+
* @param flavor - Whose rules
|
|
132
|
+
* @throws TypeError for a path that is not absolute under `flavor`
|
|
133
|
+
*/
|
|
134
|
+
export declare function namedPathRoot(path: string, flavor: PathFlavor): string;
|
|
135
|
+
/**
|
|
136
|
+
* Why a link text in an UNTRUSTED tree is refused by its text alone, under EITHER flavor — the rule
|
|
137
|
+
* a chain in a `refuse` tree is held to at every hop, and the one a caller that has only the text
|
|
138
|
+
* (a git clone reading link blobs before checkout) asks directly.
|
|
139
|
+
*
|
|
140
|
+
* A text is also `unportable` when it holds a NUL (the OS cuts the text short there, so the walk
|
|
141
|
+
* would judge bytes no kernel sees) or a component ending in a dot or a space (Win32 strips those,
|
|
142
|
+
* so `.. ` would climb where the walk read a name) — the win32 rule held on every host.
|
|
143
|
+
*
|
|
144
|
+
* @param text - A link's text, verbatim
|
|
145
|
+
* @returns why either kernel would not read it as a relative text inside the tree; `undefined` for one it would
|
|
146
|
+
*/
|
|
147
|
+
export declare function untrustedTextRefusal(text: string): UntrustedTextRefusal | undefined;
|
|
148
|
+
/** Why an untrusted link's text alone is refused ({@link untrustedTextRefusal}). */
|
|
149
|
+
type UntrustedTextRefusal = 'network' | 'absolute' | 'drive-relative' | 'unportable';
|
|
150
|
+
/**
|
|
151
|
+
* Why a committed link — `linkPath` (its `/`-separated place in the tree) whose text is `text` — is
|
|
152
|
+
* refused by its text alone, before it exists on disk: {@link untrustedTextRefusal}, or `climbs-out`
|
|
153
|
+
* when, joined onto the link's directory and split by EITHER flavor's separators, its `..`s rise above
|
|
154
|
+
* the tree's root — the same collapse the walk applies under win32.
|
|
155
|
+
*
|
|
156
|
+
* @returns The refusal, or `undefined` for a text that stays inside by its text alone
|
|
157
|
+
*/
|
|
158
|
+
export declare function untrustedLinkRefusal(linkPath: string, text: string): UntrustedTextRefusal | typeof CLIMBS_OUT | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* A name folded as Windows compares names: each character upcased on its own (an ordinal, simple
|
|
161
|
+
* case fold — never `toLowerCase`, never a mapping that changes a character's length, like `ß` → `SS`).
|
|
162
|
+
*/
|
|
163
|
+
export declare function win32FoldedName(name: string): string;
|
|
164
|
+
/**
|
|
165
|
+
* Whether `path` is `root` or below it, both forward-slashed — case-blind under win32 by the
|
|
166
|
+
* ordinal fold Windows compares names with ({@link win32FoldedName}).
|
|
167
|
+
*/
|
|
168
|
+
export declare function isWithinUnder(path: string, root: string, flavor: PathFlavor): boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Where `start` leads, every link on the way read by its text before anything follows it.
|
|
171
|
+
*
|
|
172
|
+
* @param start - An absolute path, in `rule.flavor`'s spelling (forward or back slashes)
|
|
173
|
+
* @param rule - The flavor, the tree, and what the chain may do outside it
|
|
174
|
+
* @param probe - `lstat` and `readlink`, and nothing that follows a link
|
|
175
|
+
* @returns The local path the kernel may now be asked about, nothing, or the refusal
|
|
176
|
+
* @throws whatever `probe` throws: a probe the OS refused is the caller's to classify
|
|
177
|
+
*/
|
|
178
|
+
export declare function walkLinkChain(start: string, rule: ChainRule, probe: ChainProbe): LinkChain;
|
|
179
|
+
export {};
|
|
180
|
+
//# sourceMappingURL=link-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-chain.d.ts","sourceRoot":"","sources":["../../src/tree-change/link-chain.ts"],"names":[],"mappings":"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"}
|