@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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decisions of a tree copy that touch no filesystem: what it does about an entry already at a
|
|
3
|
+
* name it is about to create, when two names are one, and the refusal two source entries with one
|
|
4
|
+
* destination name get. `copy-tree.ts` is the half that makes the calls.
|
|
5
|
+
*/
|
|
6
|
+
import { FsFaultError } from '../errors/fs-fault.js';
|
|
7
|
+
/**
|
|
8
|
+
* What a tree copy does about an entry ALREADY at a name it is about to create, below its root:
|
|
9
|
+
* - `fresh`: the destination is new (a staged tree): anything there is refused (`EEXIST`), never adopted.
|
|
10
|
+
* - `merge`: the destination is the caller's own output written again (a build directory): a real
|
|
11
|
+
* directory is adopted; a file or a link is REMOVED and made anew — never written through.
|
|
12
|
+
*
|
|
13
|
+
* Under both, an entry the copy itself made is never taken for one that was "already there": two
|
|
14
|
+
* source names that are one name at the destination are refused ({@link aliasFault}).
|
|
15
|
+
*/
|
|
16
|
+
export type CopyOnto = 'fresh' | 'merge';
|
|
17
|
+
/** One entry the walk hands the copy: where it is, and where it is relative to the root (forward slashes). */
|
|
18
|
+
export interface CopiedEntry {
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly relative: string;
|
|
21
|
+
}
|
|
22
|
+
/** The directory, relative to the root, that `relative` is in (`''`: the root). */
|
|
23
|
+
export declare const parentOf: (relative: string) => string;
|
|
24
|
+
/** The entry name of `relative`. */
|
|
25
|
+
export declare const nameOf: (relative: string) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether two entry names are one name on a filesystem that folds letter case or Unicode form
|
|
28
|
+
* (APFS, NTFS, exFAT, SMB). Asked only AFTER the OS answered `EEXIST` for the second of them, to say
|
|
29
|
+
* which earlier entry it collided with: it names the pair, it never decides that there is one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sameNameWhereFolded(a: string, b: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether a sibling this copy made, whose name folds to the name that just answered `EEXIST`, is the
|
|
34
|
+
* entry it collided with — asked only when identity did not find one that is.
|
|
35
|
+
*
|
|
36
|
+
* It is not when the OS proved them TWO entries: both are there, each with an identity, none shared.
|
|
37
|
+
* That is a tree that keeps the two names apart (case-sensitive), and what stands at the colliding
|
|
38
|
+
* name was there before this copy — a `merge`'s previous output, to take over as any other.
|
|
39
|
+
*
|
|
40
|
+
* @param sameness - What identity answered for the sibling's copy against the colliding name
|
|
41
|
+
* @param collidingPresent - Whether the colliding name holds an entry the copy can examine
|
|
42
|
+
*/
|
|
43
|
+
export declare function foldedNameIsTwin(sameness: 'same' | 'different' | 'unknown', collidingPresent: boolean): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* What a copy does at a name that already holds an entry it did not make.
|
|
46
|
+
*
|
|
47
|
+
* @param onto - The copy's mode
|
|
48
|
+
* @param kind - What the copy is about to make there
|
|
49
|
+
* @param thereIsDirectory - Whether the entry there is a real directory (not a link to one); read only under `merge`
|
|
50
|
+
* @returns `refuse` (rethrow the `EEXIST`), `adopt` (keep the directory), or `replace` (remove the file or link, make it anew)
|
|
51
|
+
*/
|
|
52
|
+
export declare function mayTakeOver(onto: CopyOnto, kind: 'directory' | 'leaf', thereIsDirectory: boolean): 'refuse' | 'adopt' | 'replace';
|
|
53
|
+
/**
|
|
54
|
+
* Two source entries that are ONE name where the copy goes: the source's own layout cannot be
|
|
55
|
+
* copied there. A `source` fault (origin `content`) whatever side the tree is read on, naming both.
|
|
56
|
+
*/
|
|
57
|
+
export declare function aliasFault(first: CopiedEntry, second: CopiedEntry, cause: unknown): FsFaultError;
|
|
58
|
+
//# sourceMappingURL=copy-decisions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-decisions.d.ts","sourceRoot":"","sources":["../../src/tree-change/copy-decisions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzC,8GAA8G;AAC9G,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,mFAAmF;AACnF,eAAO,MAAM,QAAQ,GAAI,UAAU,MAAM,KAAG,MAAsF,CAAC;AAEnI,oCAAoC;AACpC,eAAO,MAAM,MAAM,GAAI,UAAU,MAAM,KAAG,MAAuD,CAAC;AAKlG;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAE/G;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAKjI;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAUhG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decisions of a tree copy that touch no filesystem: what it does about an entry already at a
|
|
3
|
+
* name it is about to create, when two names are one, and the refusal two source entries with one
|
|
4
|
+
* destination name get. `copy-tree.ts` is the half that makes the calls.
|
|
5
|
+
*/
|
|
6
|
+
import { FsFaultError } from '../errors/fs-fault.js';
|
|
7
|
+
/** The directory, relative to the root, that `relative` is in (`''`: the root). */
|
|
8
|
+
export const parentOf = (relative) => (relative.includes('/') ? relative.slice(0, relative.lastIndexOf('/')) : '');
|
|
9
|
+
/** The entry name of `relative`. */
|
|
10
|
+
export const nameOf = (relative) => relative.slice(relative.lastIndexOf('/') + 1);
|
|
11
|
+
/** A name as a case- and normalisation-insensitive filesystem compares it, near enough to find a pair to name. */
|
|
12
|
+
const folded = (name) => name.normalize('NFKD').toLowerCase();
|
|
13
|
+
/**
|
|
14
|
+
* Whether two entry names are one name on a filesystem that folds letter case or Unicode form
|
|
15
|
+
* (APFS, NTFS, exFAT, SMB). Asked only AFTER the OS answered `EEXIST` for the second of them, to say
|
|
16
|
+
* which earlier entry it collided with: it names the pair, it never decides that there is one.
|
|
17
|
+
*/
|
|
18
|
+
export function sameNameWhereFolded(a, b) {
|
|
19
|
+
return folded(a) === folded(b);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Whether a sibling this copy made, whose name folds to the name that just answered `EEXIST`, is the
|
|
23
|
+
* entry it collided with — asked only when identity did not find one that is.
|
|
24
|
+
*
|
|
25
|
+
* It is not when the OS proved them TWO entries: both are there, each with an identity, none shared.
|
|
26
|
+
* That is a tree that keeps the two names apart (case-sensitive), and what stands at the colliding
|
|
27
|
+
* name was there before this copy — a `merge`'s previous output, to take over as any other.
|
|
28
|
+
*
|
|
29
|
+
* @param sameness - What identity answered for the sibling's copy against the colliding name
|
|
30
|
+
* @param collidingPresent - Whether the colliding name holds an entry the copy can examine
|
|
31
|
+
*/
|
|
32
|
+
export function foldedNameIsTwin(sameness, collidingPresent) {
|
|
33
|
+
return !(sameness === 'different' && collidingPresent);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What a copy does at a name that already holds an entry it did not make.
|
|
37
|
+
*
|
|
38
|
+
* @param onto - The copy's mode
|
|
39
|
+
* @param kind - What the copy is about to make there
|
|
40
|
+
* @param thereIsDirectory - Whether the entry there is a real directory (not a link to one); read only under `merge`
|
|
41
|
+
* @returns `refuse` (rethrow the `EEXIST`), `adopt` (keep the directory), or `replace` (remove the file or link, make it anew)
|
|
42
|
+
*/
|
|
43
|
+
export function mayTakeOver(onto, kind, thereIsDirectory) {
|
|
44
|
+
if (onto === 'fresh')
|
|
45
|
+
return 'refuse';
|
|
46
|
+
// A merge never swaps one kind for the other: a directory is not deleted for a file, nor a file for a directory.
|
|
47
|
+
if (thereIsDirectory !== (kind === 'directory'))
|
|
48
|
+
return 'refuse';
|
|
49
|
+
return kind === 'directory' ? 'adopt' : 'replace';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Two source entries that are ONE name where the copy goes: the source's own layout cannot be
|
|
53
|
+
* copied there. A `source` fault (origin `content`) whatever side the tree is read on, naming both.
|
|
54
|
+
*/
|
|
55
|
+
export function aliasFault(first, second, cause) {
|
|
56
|
+
return new FsFaultError({
|
|
57
|
+
side: 'source',
|
|
58
|
+
faultClass: 'occupied',
|
|
59
|
+
errno: 'EEXIST',
|
|
60
|
+
path: second.path,
|
|
61
|
+
origin: 'content',
|
|
62
|
+
action: `copy both ${first.path} and ${second.path}: where the copy goes their two names are one name (they differ only in letter case or Unicode form), so one would overwrite the other`,
|
|
63
|
+
cause,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=copy-decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-decisions.js","sourceRoot":"","sources":["../../src/tree-change/copy-decisions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAmBrD,mFAAmF;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAEnI,oCAAoC;AACpC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAElG,kHAAkH;AAClH,MAAM,MAAM,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;AAE9E;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAAS,EAAE,CAAS;IACtD,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAA0C,EAAE,gBAAyB;IACpG,OAAO,CAAC,CAAC,QAAQ,KAAK,WAAW,IAAI,gBAAgB,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAc,EAAE,IAA0B,EAAE,gBAAyB;IAC/F,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,QAAQ,CAAC;IACtC,iHAAiH;IACjH,IAAI,gBAAgB,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjE,OAAO,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAkB,EAAE,MAAmB,EAAE,KAAc;IAChF,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,UAAU;QACtB,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,aAAa,KAAK,CAAC,IAAI,QAAQ,MAAM,CAAC,IAAI,wIAAwI;QAC1L,KAAK;KACN,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `copyTree`: copy a directory tree by the same walk `proveTreeReadable` proves it
|
|
3
|
+
* with, reading each file from the very handle the walk judged a regular file.
|
|
4
|
+
*/
|
|
5
|
+
import { type FsSide } from '../errors/fs-fault.js';
|
|
6
|
+
import { type CopyOnto } from './copy-decisions.js';
|
|
7
|
+
import { type FileUnderOptions } from './files.js';
|
|
8
|
+
import type { ProveTreeReadableOptions } from './readable-tree.js';
|
|
9
|
+
import { type TreeVisitor } from './tree-walk.js';
|
|
10
|
+
/** Where a tree copy goes: `relative` (forward slashes; `''` for the root itself) under `root`. */
|
|
11
|
+
interface CopyDestination {
|
|
12
|
+
/**
|
|
13
|
+
* A directory that is the CALLER's: one it made, or one its user named. Made with its parents
|
|
14
|
+
* when absent — whatever stands above it is the caller's too, a link in the user's own layout
|
|
15
|
+
* (`~/.claude` kept in a dotfiles repository) included — and adopted when a real directory. A link
|
|
16
|
+
* or a file AT its name is never adopted ({@link claimRoot}).
|
|
17
|
+
*/
|
|
18
|
+
readonly root: string;
|
|
19
|
+
/**
|
|
20
|
+
* Where under `root` the copy goes. Everything between the two is inside a tree that may hold
|
|
21
|
+
* what an earlier copy kept — links included — so each component must be a real directory
|
|
22
|
+
* (`makeDirectoryUnder`): a link or a file standing there refuses the copy, naming it.
|
|
23
|
+
*/
|
|
24
|
+
readonly relative: string;
|
|
25
|
+
}
|
|
26
|
+
/** What {@link copyTree} is told: the walk, the side its reads are on, and what it does about what is already there. */
|
|
27
|
+
export interface CopyTreeOptions extends ProveTreeReadableOptions {
|
|
28
|
+
/** Required: only the caller knows whether its destination is new ({@link CopyOnto}). */
|
|
29
|
+
readonly onto: CopyOnto;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The visitor that writes the copy of each entry the walk accepts under `dest`; every read of the
|
|
33
|
+
* copied tree is on `side`.
|
|
34
|
+
*
|
|
35
|
+
* Every create below the root is EXCLUSIVE and never follows a link: a file is opened `wx`, a
|
|
36
|
+
* directory made by a plain `mkdir`, a link made only where nothing is. So a create that finds
|
|
37
|
+
* something answers `EEXIST`, and what happens then is decided in this order:
|
|
38
|
+
*
|
|
39
|
+
* 1. The entry there is one THIS copy made — the same entry by identity, or (where the OS will not
|
|
40
|
+
* say) a sibling whose name is the same once case and Unicode form are folded: two source names,
|
|
41
|
+
* one destination name. Refused as the source's layout (`aliasFault`), under either mode.
|
|
42
|
+
* 2. `fresh`: refused as it is (`EEXIST`), for the caller's boundary to classify.
|
|
43
|
+
* 3. `merge`: a real directory is adopted; a file or a link is removed (the link, never its target)
|
|
44
|
+
* and the entry made anew; anything else — a directory where a file goes, a link where a
|
|
45
|
+
* directory goes — is refused as it is (`mayTakeOver`).
|
|
46
|
+
*
|
|
47
|
+
* Where the copy goes is made by {@link claimDestination}: the root is the caller's (made with its
|
|
48
|
+
* parents when absent, adopted when a real directory, refused `EEXIST` when a link or a file — a link
|
|
49
|
+
* taken for it would put the whole copy where it points), and every directory from the root down to
|
|
50
|
+
* the copy is a real one or is refused as the layout of the tree it is in.
|
|
51
|
+
*
|
|
52
|
+
* Exported for `copyTree` and for the suite that hands it two entries by name — a real alias needs
|
|
53
|
+
* a case-sensitive source and a case-folding destination, which no one temp directory offers.
|
|
54
|
+
*/
|
|
55
|
+
export declare function copyVisitor(where: CopyDestination, copied: string, side: FsSide, onto: CopyOnto): TreeVisitor;
|
|
56
|
+
/**
|
|
57
|
+
* Copy the directory `source` to `relative` under `root` ({@link CopyDestination}).
|
|
58
|
+
*
|
|
59
|
+
* `root` is a directory the caller made or its user named: created, with its parents, when absent;
|
|
60
|
+
* adopted when a real directory; refused, `EEXIST`, when a link or a file stands there. `relative`
|
|
61
|
+
* is `''` for a copy onto `root` itself. A copy that goes INSIDE a tree — a pool skill into a staged
|
|
62
|
+
* plugin that earlier copies filled, links kept — names that tree's root and where under it, and
|
|
63
|
+
* every directory between the two must then be a real one (made when absent): a link or a file
|
|
64
|
+
* there is a `source` fault (origin `content`, class `occupied`) naming it, never followed. So a
|
|
65
|
+
* caller cannot reach through a link by how deep its destination is: the only directory taken on
|
|
66
|
+
* trust is the one it declares as `root`.
|
|
67
|
+
*
|
|
68
|
+
* Entries are walked, and refused, exactly as `proveTreeReadable` proves them (see
|
|
69
|
+
* `tree-walk.ts`): a `follow-contained` link is copied as what it points at, inside
|
|
70
|
+
* the source only; a `preserve` link is copied as a link, its target string
|
|
71
|
+
* verbatim and never followed — its chain is read by text alone to give the copy its `file`/`dir`
|
|
72
|
+
* type, and one through a network or device path is refused (a `refused` fault on `options.side`)
|
|
73
|
+
* before anything is made; an entry `filter` excludes is never touched; a named
|
|
74
|
+
* pipe, socket or device is refused unopened. Each file is copied from the handle
|
|
75
|
+
* `fstat` judged, with its mode. Each directory takes its source's mode with the
|
|
76
|
+
* owner's rwx kept (`| 0o700`): a read-only source must not become a copy nothing
|
|
77
|
+
* can fill or remove.
|
|
78
|
+
*
|
|
79
|
+
* Below the copy's own directory nothing already there is ever adopted or written through, and two source
|
|
80
|
+
* entries that are one name there are refused as the source's layout (a `source` fault
|
|
81
|
+
* naming both): see {@link copyVisitor} and `options.onto`.
|
|
82
|
+
*
|
|
83
|
+
* The copy names no side for what it WRITES: a failure writing the copy is the raw
|
|
84
|
+
* errno, for the caller's boundary to classify — a link this host cannot create, and an
|
|
85
|
+
* `EEXIST` for something already at a name of a `fresh` destination, included. What it READS is on the side the caller names, as the proof's reads
|
|
86
|
+
* are: a refusal is a classified fault on `options.side` (origin `content`) naming
|
|
87
|
+
* the entry. Only the caller knows whether the tree is an input (`source`), VAT's
|
|
88
|
+
* staging (`environment`) or a copy already made into user state (`destination`).
|
|
89
|
+
*
|
|
90
|
+
* @param source - The directory to copy
|
|
91
|
+
* @param root - The directory the caller made or owns, which the copy goes into or under
|
|
92
|
+
* @param relative - Where under `root` the copy goes, forward slashes; `''` for `root` itself
|
|
93
|
+
* @param options - `links`, `filter` and `side`, exactly as `proveTreeReadable` was given them, and `onto`
|
|
94
|
+
*/
|
|
95
|
+
export declare function copyTree(source: string, root: string, relative: string, options: CopyTreeOptions): Promise<void>;
|
|
96
|
+
/** What {@link copyRegularFile} is told: the file it reads, and (as `writeFileUnder` is) the entry already where the copy goes. */
|
|
97
|
+
export interface CopyRegularFileOptions extends FileUnderOptions {
|
|
98
|
+
/** The side of the verb the file is on, as for {@link copyTree}. */
|
|
99
|
+
readonly side: FsSide;
|
|
100
|
+
/** What the file is, for a refused read's message: `read <reading>` (`linked file docs/a.md`). */
|
|
101
|
+
readonly reading: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Copy ONE regular file to `relative` under `root`, as {@link copyTree} copies a file: opened
|
|
105
|
+
* without blocking and judged by `fstat` on that handle (a named pipe, socket or device is refused
|
|
106
|
+
* before a byte is read, never waited on), its bytes copied from that very handle, its mode
|
|
107
|
+
* carried over.
|
|
108
|
+
*
|
|
109
|
+
* The copy is made as `writeFileUnder` makes a file — it is the same primitive: every directory of
|
|
110
|
+
* `relative` must be a real directory (made by a plain `mkdir` when absent), the file is created
|
|
111
|
+
* exclusively, and its bytes and mode go to that handle. So it never writes through a link, at the
|
|
112
|
+
* file's name or on the way to it; `options.existing` decides a regular file already there.
|
|
113
|
+
*
|
|
114
|
+
* Every read is a classified fault on `options.side` (origin `content`), naming what was read. An
|
|
115
|
+
* entry in the copy's way is a `source` fault (origin `content`, class `occupied`) naming it; any
|
|
116
|
+
* other failure writing is the raw errno, for the caller's boundary to classify.
|
|
117
|
+
*
|
|
118
|
+
* @param source - The file to copy
|
|
119
|
+
* @param root - The root of the tree the copy goes into, which the caller made
|
|
120
|
+
* @param relative - Where the copy goes, relative to `root`, forward slashes
|
|
121
|
+
* @param options - The side `source` is on and what it is; `existing` and `writing`, for the copy
|
|
122
|
+
*/
|
|
123
|
+
export declare function copyRegularFile(source: string, root: string, relative: string, options: CopyRegularFileOptions): Promise<void>;
|
|
124
|
+
export {};
|
|
125
|
+
//# sourceMappingURL=copy-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-tree.d.ts","sourceRoot":"","sources":["../../src/tree-change/copy-tree.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAKrE,OAAO,EAAgC,KAAK,QAAQ,EAAwE,MAAM,qBAAqB,CAAC;AACxJ,OAAO,EAAE,KAAK,gBAAgB,EAAqC,MAAM,YAAY,CAAC;AAEtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAA8B,KAAK,WAAW,EAAY,MAAM,gBAAgB,CAAC;AAqDxF,mGAAmG;AACnG,UAAU,eAAe;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA+BD,wHAAwH;AACxH,MAAM,WAAW,eAAgB,SAAQ,wBAAwB;IAC/D,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,WAAW,CAgE7G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtH;AAED,mIAAmI;AACnI,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBpI"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `copyTree`: copy a directory tree by the same walk `proveTreeReadable` proves it
|
|
3
|
+
* with, reading each file from the very handle the walk judged a regular file.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import { isAlreadyExistsError, isPathAbsentError } from '../errors/errno-table.js';
|
|
7
|
+
import { classifyFsFault } from '../errors/fs-fault.js';
|
|
8
|
+
import { linkCopyType } from '../link-following.js';
|
|
9
|
+
import { safePath } from '../path-core.js';
|
|
10
|
+
import { openForReading } from '../text-file.js';
|
|
11
|
+
import { aliasFault, foldedNameIsTwin, mayTakeOver, nameOf, parentOf, sameNameWhereFolded } from './copy-decisions.js';
|
|
12
|
+
import { makeDirectoryUnder, openFileUnder } from './files.js';
|
|
13
|
+
import { sameEntry } from './identity.js';
|
|
14
|
+
import { readingTree, treeReadFault, walkTree } from './tree-walk.js';
|
|
15
|
+
/** The owner's read, write and search bits, kept on every copied directory so its owner can always fill and remove it. */
|
|
16
|
+
const OWNER_RWX = 0o700;
|
|
17
|
+
/** The bytes of an open file being copied; a read failure is `readFault`'s. A write failure never reaches this catch: the writer's loop ends the generator with `return()`, not `throw()`. */
|
|
18
|
+
async function* sourceBytes(source, readFault) {
|
|
19
|
+
try {
|
|
20
|
+
yield* source.createReadStream({ autoClose: false, start: 0 });
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw readFault(error);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Copy the bytes of an open file — one already judged a regular file on that very handle — into
|
|
28
|
+
* `claimed`, a file this copy has just made exclusively (opened `wx`), and carry its permission bits
|
|
29
|
+
* over, so a script stays executable — never its setuid, setgid or sticky bit. The ONE file copy of the tree copy and the
|
|
30
|
+
* single-file copy: bytes and mode both go to that very handle, never to a path, so nothing swapped
|
|
31
|
+
* in after the claim — a link least of all — is written or chmod-ed through. Closes `claimed`.
|
|
32
|
+
*/
|
|
33
|
+
async function copyOpenFileInto(handle, claimed, mode, readFault) {
|
|
34
|
+
try {
|
|
35
|
+
// On the handle, not through a stream of it: a stream owns the handle it writes and closes it
|
|
36
|
+
// with itself, before the mode could be set on it.
|
|
37
|
+
await claimed.writeFile(sourceBytes(handle, readFault));
|
|
38
|
+
// The permission bits only: setuid, setgid and sticky never travel with a copy.
|
|
39
|
+
await claimed.chmod(mode & 0o777);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
// The copy's own failure is the answer: a close refused after it is not a second one.
|
|
43
|
+
await claimed.close().catch(() => undefined);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
await claimed.close();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Make the root of a copy: with its parents when nothing is there, adopted when a real directory
|
|
50
|
+
* is. A link there — to a directory included — or a file is never adopted: the plain `mkdir` is the
|
|
51
|
+
* OS's own `EEXIST` for it, raw, as for any entry of a `fresh` destination.
|
|
52
|
+
*/
|
|
53
|
+
async function claimRoot(into) {
|
|
54
|
+
const there = await fs.lstat(into).catch((error) => {
|
|
55
|
+
if (isPathAbsentError(error))
|
|
56
|
+
return undefined;
|
|
57
|
+
throw error;
|
|
58
|
+
});
|
|
59
|
+
if (there === undefined)
|
|
60
|
+
await fs.mkdir(into, { recursive: true });
|
|
61
|
+
else if (!there.isDirectory())
|
|
62
|
+
await fs.mkdir(into);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Make the directory a copy goes into ({@link CopyDestination}): the root as the caller's, every
|
|
66
|
+
* component under it as a real directory or not at all.
|
|
67
|
+
*
|
|
68
|
+
* @param copied - What is copied, for a refusal's message
|
|
69
|
+
* @returns The directory's path
|
|
70
|
+
*/
|
|
71
|
+
async function claimDestination(into, copied) {
|
|
72
|
+
await claimRoot(into.root);
|
|
73
|
+
return into.relative === '' ? into.root : await makeDirectoryUnder(into.root, into.relative, `the copy of ${copied}`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Make the directory a copy's own entry goes IN, for a source that is not a directory at all — a
|
|
77
|
+
* root that is a link, kept as one (`links: 'preserve'`): the destination root's parents as the
|
|
78
|
+
* caller's, every component under the root as a real directory or not at all.
|
|
79
|
+
*
|
|
80
|
+
* @param copied - What is copied, for a refusal's message
|
|
81
|
+
*/
|
|
82
|
+
async function claimParentOfDestination(into, copied) {
|
|
83
|
+
if (into.relative === '') {
|
|
84
|
+
await fs.mkdir(parentOf(into.root), { recursive: true });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await claimRoot(into.root);
|
|
88
|
+
const parent = parentOf(into.relative);
|
|
89
|
+
if (parent !== '')
|
|
90
|
+
await makeDirectoryUnder(into.root, parent, `the copy of ${copied}`);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The visitor that writes the copy of each entry the walk accepts under `dest`; every read of the
|
|
94
|
+
* copied tree is on `side`.
|
|
95
|
+
*
|
|
96
|
+
* Every create below the root is EXCLUSIVE and never follows a link: a file is opened `wx`, a
|
|
97
|
+
* directory made by a plain `mkdir`, a link made only where nothing is. So a create that finds
|
|
98
|
+
* something answers `EEXIST`, and what happens then is decided in this order:
|
|
99
|
+
*
|
|
100
|
+
* 1. The entry there is one THIS copy made — the same entry by identity, or (where the OS will not
|
|
101
|
+
* say) a sibling whose name is the same once case and Unicode form are folded: two source names,
|
|
102
|
+
* one destination name. Refused as the source's layout (`aliasFault`), under either mode.
|
|
103
|
+
* 2. `fresh`: refused as it is (`EEXIST`), for the caller's boundary to classify.
|
|
104
|
+
* 3. `merge`: a real directory is adopted; a file or a link is removed (the link, never its target)
|
|
105
|
+
* and the entry made anew; anything else — a directory where a file goes, a link where a
|
|
106
|
+
* directory goes — is refused as it is (`mayTakeOver`).
|
|
107
|
+
*
|
|
108
|
+
* Where the copy goes is made by {@link claimDestination}: the root is the caller's (made with its
|
|
109
|
+
* parents when absent, adopted when a real directory, refused `EEXIST` when a link or a file — a link
|
|
110
|
+
* taken for it would put the whole copy where it points), and every directory from the root down to
|
|
111
|
+
* the copy is a real one or is refused as the layout of the tree it is in.
|
|
112
|
+
*
|
|
113
|
+
* Exported for `copyTree` and for the suite that hands it two entries by name — a real alias needs
|
|
114
|
+
* a case-sensitive source and a case-folding destination, which no one temp directory offers.
|
|
115
|
+
*/
|
|
116
|
+
export function copyVisitor(where, copied, side, onto) {
|
|
117
|
+
const dest = where.relative === '' ? where.root : safePath.join(where.root, where.relative);
|
|
118
|
+
const target = (relative) => (relative === '' ? dest : safePath.join(dest, relative));
|
|
119
|
+
/** What this copy made, by the directory (relative to the root) it made it in. */
|
|
120
|
+
const made = new Map();
|
|
121
|
+
const madeIn = (parent) => {
|
|
122
|
+
const list = made.get(parent) ?? [];
|
|
123
|
+
made.set(parent, list);
|
|
124
|
+
return list;
|
|
125
|
+
};
|
|
126
|
+
const twinOf = async (entry, into) => {
|
|
127
|
+
const siblings = madeIn(parentOf(entry.relative));
|
|
128
|
+
const byIdentity = siblings.find((each) => sameEntry(target(each.relative), into) === 'same');
|
|
129
|
+
if (byIdentity !== undefined)
|
|
130
|
+
return byIdentity;
|
|
131
|
+
const byName = siblings.find((each) => sameNameWhereFolded(nameOf(each.relative), nameOf(entry.relative)));
|
|
132
|
+
if (byName === undefined)
|
|
133
|
+
return undefined;
|
|
134
|
+
// Absent or unexaminable alike: then the collision is all there is to go on, and the name decides.
|
|
135
|
+
const present = await fs.lstat(into).then(() => true, () => false);
|
|
136
|
+
return foldedNameIsTwin(sameEntry(target(byName.relative), into), present) ? byName : undefined;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Claim `entry`'s place by `claim` — an exclusive create, and nothing else, so it can be asked
|
|
140
|
+
* twice — and hand back what it made (`undefined`: a directory a merge adopted).
|
|
141
|
+
*/
|
|
142
|
+
const create = async (entry, kind, claim) => {
|
|
143
|
+
const into = target(entry.relative);
|
|
144
|
+
const claimed = await claim(into).catch(async (error) => {
|
|
145
|
+
if (!isAlreadyExistsError(error))
|
|
146
|
+
throw error;
|
|
147
|
+
const twin = await twinOf(entry, into);
|
|
148
|
+
if (twin !== undefined)
|
|
149
|
+
throw aliasFault(twin, entry, error);
|
|
150
|
+
const takeOver = mayTakeOver(onto, kind, onto === 'merge' ? (await fs.lstat(into)).isDirectory() : false);
|
|
151
|
+
if (takeOver === 'refuse')
|
|
152
|
+
throw error;
|
|
153
|
+
if (takeOver === 'adopt')
|
|
154
|
+
return undefined;
|
|
155
|
+
await fs.unlink(into);
|
|
156
|
+
return claim(into);
|
|
157
|
+
});
|
|
158
|
+
madeIn(parentOf(entry.relative)).push(entry);
|
|
159
|
+
return claimed;
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
async directory(entry, stats) {
|
|
163
|
+
const into = target(entry.relative);
|
|
164
|
+
if (entry.relative === '')
|
|
165
|
+
await claimDestination(where, copied);
|
|
166
|
+
else
|
|
167
|
+
await create(entry, 'directory', (path) => fs.mkdir(path));
|
|
168
|
+
await fs.chmod(into, (stats.mode & 0o7777) | OWNER_RWX);
|
|
169
|
+
},
|
|
170
|
+
async file(entry, handle, stats) {
|
|
171
|
+
const claimed = await create(entry, 'leaf', (into) => fs.open(into, 'wx'));
|
|
172
|
+
if (claimed !== undefined)
|
|
173
|
+
await copyOpenFileInto(handle, claimed, stats.mode, (error) => treeReadFault(side, entry.path, error));
|
|
174
|
+
},
|
|
175
|
+
async link(entry) {
|
|
176
|
+
const linkTarget = await readingTree(side, entry.path, () => fs.readlink(entry.path));
|
|
177
|
+
// Typed from the chain read by its text, before anything is made: an untyped `symlink` has
|
|
178
|
+
// Node on Windows ask the kernel to follow the target to pick `file` or `dir`, so a network or
|
|
179
|
+
// device text — at any hop — is refused here, on the copied tree's side, never handed on.
|
|
180
|
+
const type = await readingTree(side, entry.path, () => Promise.resolve().then(() => linkCopyType(entry.path, copied)));
|
|
181
|
+
// The source itself is a link: there is no copied directory to hold it, so its place is made first.
|
|
182
|
+
if (entry.relative === '')
|
|
183
|
+
await claimParentOfDestination(where, copied);
|
|
184
|
+
// Verbatim. A host that cannot make the link (Windows without the privilege: EPERM; a
|
|
185
|
+
// filesystem with none: ENOTSUP) raises the raw errno, a write like any other, for the
|
|
186
|
+
// caller's boundary to classify.
|
|
187
|
+
await create(entry, 'leaf', (into) => fs.symlink(linkTarget, into, type));
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Copy the directory `source` to `relative` under `root` ({@link CopyDestination}).
|
|
193
|
+
*
|
|
194
|
+
* `root` is a directory the caller made or its user named: created, with its parents, when absent;
|
|
195
|
+
* adopted when a real directory; refused, `EEXIST`, when a link or a file stands there. `relative`
|
|
196
|
+
* is `''` for a copy onto `root` itself. A copy that goes INSIDE a tree — a pool skill into a staged
|
|
197
|
+
* plugin that earlier copies filled, links kept — names that tree's root and where under it, and
|
|
198
|
+
* every directory between the two must then be a real one (made when absent): a link or a file
|
|
199
|
+
* there is a `source` fault (origin `content`, class `occupied`) naming it, never followed. So a
|
|
200
|
+
* caller cannot reach through a link by how deep its destination is: the only directory taken on
|
|
201
|
+
* trust is the one it declares as `root`.
|
|
202
|
+
*
|
|
203
|
+
* Entries are walked, and refused, exactly as `proveTreeReadable` proves them (see
|
|
204
|
+
* `tree-walk.ts`): a `follow-contained` link is copied as what it points at, inside
|
|
205
|
+
* the source only; a `preserve` link is copied as a link, its target string
|
|
206
|
+
* verbatim and never followed — its chain is read by text alone to give the copy its `file`/`dir`
|
|
207
|
+
* type, and one through a network or device path is refused (a `refused` fault on `options.side`)
|
|
208
|
+
* before anything is made; an entry `filter` excludes is never touched; a named
|
|
209
|
+
* pipe, socket or device is refused unopened. Each file is copied from the handle
|
|
210
|
+
* `fstat` judged, with its mode. Each directory takes its source's mode with the
|
|
211
|
+
* owner's rwx kept (`| 0o700`): a read-only source must not become a copy nothing
|
|
212
|
+
* can fill or remove.
|
|
213
|
+
*
|
|
214
|
+
* Below the copy's own directory nothing already there is ever adopted or written through, and two source
|
|
215
|
+
* entries that are one name there are refused as the source's layout (a `source` fault
|
|
216
|
+
* naming both): see {@link copyVisitor} and `options.onto`.
|
|
217
|
+
*
|
|
218
|
+
* The copy names no side for what it WRITES: a failure writing the copy is the raw
|
|
219
|
+
* errno, for the caller's boundary to classify — a link this host cannot create, and an
|
|
220
|
+
* `EEXIST` for something already at a name of a `fresh` destination, included. What it READS is on the side the caller names, as the proof's reads
|
|
221
|
+
* are: a refusal is a classified fault on `options.side` (origin `content`) naming
|
|
222
|
+
* the entry. Only the caller knows whether the tree is an input (`source`), VAT's
|
|
223
|
+
* staging (`environment`) or a copy already made into user state (`destination`).
|
|
224
|
+
*
|
|
225
|
+
* @param source - The directory to copy
|
|
226
|
+
* @param root - The directory the caller made or owns, which the copy goes into or under
|
|
227
|
+
* @param relative - Where under `root` the copy goes, forward slashes; `''` for `root` itself
|
|
228
|
+
* @param options - `links`, `filter` and `side`, exactly as `proveTreeReadable` was given them, and `onto`
|
|
229
|
+
*/
|
|
230
|
+
export async function copyTree(source, root, relative, options) {
|
|
231
|
+
await walkTree(source, options, options.side, copyVisitor({ root, relative }, source, options.side, options.onto));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Copy ONE regular file to `relative` under `root`, as {@link copyTree} copies a file: opened
|
|
235
|
+
* without blocking and judged by `fstat` on that handle (a named pipe, socket or device is refused
|
|
236
|
+
* before a byte is read, never waited on), its bytes copied from that very handle, its mode
|
|
237
|
+
* carried over.
|
|
238
|
+
*
|
|
239
|
+
* The copy is made as `writeFileUnder` makes a file — it is the same primitive: every directory of
|
|
240
|
+
* `relative` must be a real directory (made by a plain `mkdir` when absent), the file is created
|
|
241
|
+
* exclusively, and its bytes and mode go to that handle. So it never writes through a link, at the
|
|
242
|
+
* file's name or on the way to it; `options.existing` decides a regular file already there.
|
|
243
|
+
*
|
|
244
|
+
* Every read is a classified fault on `options.side` (origin `content`), naming what was read. An
|
|
245
|
+
* entry in the copy's way is a `source` fault (origin `content`, class `occupied`) naming it; any
|
|
246
|
+
* other failure writing is the raw errno, for the caller's boundary to classify.
|
|
247
|
+
*
|
|
248
|
+
* @param source - The file to copy
|
|
249
|
+
* @param root - The root of the tree the copy goes into, which the caller made
|
|
250
|
+
* @param relative - Where the copy goes, relative to `root`, forward slashes
|
|
251
|
+
* @param options - The side `source` is on and what it is; `existing` and `writing`, for the copy
|
|
252
|
+
*/
|
|
253
|
+
export async function copyRegularFile(source, root, relative, options) {
|
|
254
|
+
// Every read — the open, the `fstat`, the stream, the close — names what was read, on its side.
|
|
255
|
+
const readFault = (error) => classifyFsFault(error, { side: options.side, origin: 'content', action: `read ${options.reading}`, path: source });
|
|
256
|
+
const reading = async (read) => {
|
|
257
|
+
try {
|
|
258
|
+
return await read();
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
throw readFault(error);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const handle = await reading(() => openForReading(source));
|
|
265
|
+
try {
|
|
266
|
+
const stats = await reading(() => handle.stat());
|
|
267
|
+
// Only once the source is open and judged: a `replace` removes nothing for a copy that cannot be read.
|
|
268
|
+
const claimed = await openFileUnder(root, relative, options);
|
|
269
|
+
await copyOpenFileInto(handle, claimed, stats.mode, readFault);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
// The copy's own failure is the answer: a close refused after it is not a second one.
|
|
273
|
+
await handle.close().catch(() => undefined);
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
// The close is a read of the source, too: a refusal of it is on the source's side.
|
|
277
|
+
await reading(() => handle.close());
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=copy-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copy-tree.js","sourceRoot":"","sources":["../../src/tree-change/copy-tree.ts"],"names":[],"mappings":"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"}
|