@vibe-agent-toolkit/utils 0.2.0-rc.10 → 0.2.0-rc.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -10
- package/dist/archive/archive.d.ts +129 -0
- package/dist/archive/archive.d.ts.map +1 -0
- package/dist/archive/archive.js +223 -0
- package/dist/archive/archive.js.map +1 -0
- package/dist/archive/entries.d.ts +132 -0
- package/dist/archive/entries.d.ts.map +1 -0
- package/dist/archive/entries.js +223 -0
- package/dist/archive/entries.js.map +1 -0
- package/dist/archive/limits.d.ts +39 -0
- package/dist/archive/limits.d.ts.map +1 -0
- package/dist/archive/limits.js +59 -0
- package/dist/archive/limits.js.map +1 -0
- package/dist/archive/records.d.ts +14 -0
- package/dist/archive/records.d.ts.map +1 -0
- package/dist/archive/records.js +3 -0
- package/dist/archive/records.js.map +1 -0
- package/dist/archive/tar-reader.d.ts +18 -0
- package/dist/archive/tar-reader.d.ts.map +1 -0
- package/dist/archive/tar-reader.js +173 -0
- package/dist/archive/tar-reader.js.map +1 -0
- package/dist/archive/zip-reader.d.ts +76 -0
- package/dist/archive/zip-reader.d.ts.map +1 -0
- package/dist/archive/zip-reader.js +376 -0
- package/dist/archive/zip-reader.js.map +1 -0
- package/dist/archive.d.ts +14 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +12 -0
- package/dist/archive.js.map +1 -0
- package/dist/asset-reference.d.ts +17 -1
- package/dist/asset-reference.d.ts.map +1 -1
- package/dist/asset-reference.js +31 -7
- package/dist/asset-reference.js.map +1 -1
- package/dist/dirent-kind.d.ts +13 -40
- package/dist/dirent-kind.d.ts.map +1 -1
- package/dist/dirent-kind.js +21 -69
- package/dist/dirent-kind.js.map +1 -1
- package/dist/entrypoint.d.ts +2 -2
- package/dist/entrypoint.d.ts.map +1 -1
- package/dist/entrypoint.js +4 -3
- package/dist/entrypoint.js.map +1 -1
- package/dist/env-flag.d.ts +13 -5
- package/dist/env-flag.d.ts.map +1 -1
- package/dist/env-flag.js +13 -5
- package/dist/env-flag.js.map +1 -1
- package/dist/errors/confirmed-absent.d.ts +41 -0
- package/dist/errors/confirmed-absent.d.ts.map +1 -0
- package/dist/errors/confirmed-absent.js +43 -0
- package/dist/errors/confirmed-absent.js.map +1 -0
- package/dist/errors/errno-table.d.ts +177 -0
- package/dist/errors/errno-table.d.ts.map +1 -0
- package/dist/errors/errno-table.js +343 -0
- package/dist/errors/errno-table.js.map +1 -0
- package/dist/errors/fs-boundary.d.ts +36 -0
- package/dist/errors/fs-boundary.d.ts.map +1 -0
- package/dist/errors/fs-boundary.js +121 -0
- package/dist/errors/fs-boundary.js.map +1 -0
- package/dist/errors/fs-fault.d.ts +176 -0
- package/dist/errors/fs-fault.d.ts.map +1 -0
- package/dist/errors/fs-fault.js +215 -0
- package/dist/errors/fs-fault.js.map +1 -0
- package/dist/errors/git-errno.d.ts +13 -0
- package/dist/errors/git-errno.d.ts.map +1 -0
- package/dist/errors/git-errno.js +45 -0
- package/dist/errors/git-errno.js.map +1 -0
- package/dist/errors/path-present.d.ts +72 -0
- package/dist/errors/path-present.d.ts.map +1 -0
- package/dist/errors/path-present.js +110 -0
- package/dist/errors/path-present.js.map +1 -0
- package/dist/errors/rag-index-empty.d.ts +16 -0
- package/dist/errors/rag-index-empty.d.ts.map +1 -0
- package/dist/errors/rag-index-empty.js +16 -0
- package/dist/errors/rag-index-empty.js.map +1 -0
- package/dist/errors/suppressed-faults.d.ts +34 -0
- package/dist/errors/suppressed-faults.d.ts.map +1 -0
- package/dist/errors/suppressed-faults.js +66 -0
- package/dist/errors/suppressed-faults.js.map +1 -0
- package/dist/file-crawler.d.ts +13 -18
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +140 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs-utils.d.ts +75 -50
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +148 -158
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -4
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +5 -4
- package/dist/fs.js.map +1 -1
- package/dist/git-snapshot.d.ts +25 -17
- package/dist/git-snapshot.d.ts.map +1 -1
- package/dist/git-snapshot.js +65 -11
- package/dist/git-snapshot.js.map +1 -1
- package/dist/git-tracker.d.ts +11 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +29 -6
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +7 -35
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +5 -4
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/in-order.d.ts +93 -0
- package/dist/in-order.d.ts.map +1 -0
- package/dist/in-order.js +165 -0
- package/dist/in-order.js.map +1 -0
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -4
- package/dist/index.js.map +1 -1
- package/dist/kill-process-tree.d.ts +43 -0
- package/dist/kill-process-tree.d.ts.map +1 -0
- package/dist/kill-process-tree.js +48 -0
- package/dist/kill-process-tree.js.map +1 -0
- package/dist/link-following.d.ts +115 -0
- package/dist/link-following.d.ts.map +1 -0
- package/dist/link-following.js +283 -0
- package/dist/link-following.js.map +1 -0
- package/dist/listing-refusal.d.ts +44 -0
- package/dist/listing-refusal.d.ts.map +1 -1
- package/dist/listing-refusal.js +79 -2
- package/dist/listing-refusal.js.map +1 -1
- package/dist/name-fold.d.ts +25 -0
- package/dist/name-fold.d.ts.map +1 -0
- package/dist/name-fold.js +40 -0
- package/dist/name-fold.js.map +1 -0
- package/dist/path-containment.d.ts +17 -0
- package/dist/path-containment.d.ts.map +1 -1
- package/dist/path-containment.js +28 -5
- package/dist/path-containment.js.map +1 -1
- package/dist/path-utils.d.ts +14 -17
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +37 -55
- package/dist/path-utils.js.map +1 -1
- package/dist/process.d.ts +2 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +2 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +8 -9
- package/dist/project-utils.js.map +1 -1
- package/dist/safe-exec.d.ts +6 -2
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +1 -1
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts.map +1 -1
- package/dist/skill-targets.js +3 -1
- package/dist/skill-targets.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +0 -15
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +11 -50
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/special-file.d.ts +18 -0
- package/dist/special-file.d.ts.map +1 -0
- package/dist/special-file.js +21 -0
- package/dist/special-file.js.map +1 -0
- package/dist/terminating-signals.d.ts +129 -0
- package/dist/terminating-signals.d.ts.map +1 -0
- package/dist/terminating-signals.js +210 -0
- package/dist/terminating-signals.js.map +1 -0
- package/dist/test-helpers.d.ts +9 -5
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +18 -21
- package/dist/test-helpers.js.map +1 -1
- package/dist/test-scratch-guard.d.ts +34 -0
- package/dist/test-scratch-guard.d.ts.map +1 -0
- package/dist/test-scratch-guard.js +45 -0
- package/dist/test-scratch-guard.js.map +1 -0
- package/dist/testing/archive-fixtures.d.ts +147 -0
- package/dist/testing/archive-fixtures.d.ts.map +1 -0
- package/dist/testing/archive-fixtures.js +449 -0
- package/dist/testing/archive-fixtures.js.map +1 -0
- package/dist/testing/executables.d.ts +6 -0
- package/dist/testing/executables.d.ts.map +1 -1
- package/dist/testing/executables.js +20 -9
- package/dist/testing/executables.js.map +1 -1
- package/dist/testing/fault-fs-preload.d.ts +16 -0
- package/dist/testing/fault-fs-preload.d.ts.map +1 -0
- package/dist/testing/fault-fs-preload.js +22 -0
- package/dist/testing/fault-fs-preload.js.map +1 -0
- package/dist/testing/fault-fs.d.ts +115 -0
- package/dist/testing/fault-fs.d.ts.map +1 -0
- package/dist/testing/fault-fs.js +411 -0
- package/dist/testing/fault-fs.js.map +1 -0
- package/dist/testing/fault-spec.d.ts +83 -0
- package/dist/testing/fault-spec.d.ts.map +1 -0
- package/dist/testing/fault-spec.js +132 -0
- package/dist/testing/fault-spec.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -1
- package/dist/testing/hostile-tree.js +3 -4
- package/dist/testing/hostile-tree.js.map +1 -1
- package/dist/testing/platform-gates.d.ts +8 -0
- package/dist/testing/platform-gates.d.ts.map +1 -1
- package/dist/testing/platform-gates.js +24 -0
- package/dist/testing/platform-gates.js.map +1 -1
- package/dist/testing/temp-dir.d.ts +31 -0
- package/dist/testing/temp-dir.d.ts.map +1 -1
- package/dist/testing/temp-dir.js +63 -0
- package/dist/testing/temp-dir.js.map +1 -1
- package/dist/testing/tier-timeout.d.ts +34 -0
- package/dist/testing/tier-timeout.d.ts.map +1 -0
- package/dist/testing/tier-timeout.js +45 -0
- package/dist/testing/tier-timeout.js.map +1 -0
- package/dist/testing/tree-snapshot.d.ts +71 -0
- package/dist/testing/tree-snapshot.d.ts.map +1 -0
- package/dist/testing/tree-snapshot.js +163 -0
- package/dist/testing/tree-snapshot.js.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +8 -1
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +29 -0
- package/dist/text-content.d.ts.map +1 -1
- package/dist/text-content.js +64 -0
- package/dist/text-content.js.map +1 -1
- package/dist/text-file.d.ts +58 -6
- package/dist/text-file.d.ts.map +1 -1
- package/dist/text-file.js +111 -11
- package/dist/text-file.js.map +1 -1
- package/dist/text.d.ts +1 -1
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +1 -1
- package/dist/text.js.map +1 -1
- package/dist/timing-dump.d.ts.map +1 -1
- package/dist/timing-dump.js +2 -10
- package/dist/timing-dump.js.map +1 -1
- package/dist/tree-change/apply-decisions.d.ts +101 -0
- package/dist/tree-change/apply-decisions.d.ts.map +1 -0
- package/dist/tree-change/apply-decisions.js +139 -0
- package/dist/tree-change/apply-decisions.js.map +1 -0
- package/dist/tree-change/apply.d.ts +98 -0
- package/dist/tree-change/apply.d.ts.map +1 -0
- package/dist/tree-change/apply.js +364 -0
- package/dist/tree-change/apply.js.map +1 -0
- package/dist/tree-change/copy-decisions.d.ts +58 -0
- package/dist/tree-change/copy-decisions.d.ts.map +1 -0
- package/dist/tree-change/copy-decisions.js +66 -0
- package/dist/tree-change/copy-decisions.js.map +1 -0
- package/dist/tree-change/copy-tree.d.ts +125 -0
- package/dist/tree-change/copy-tree.d.ts.map +1 -0
- package/dist/tree-change/copy-tree.js +279 -0
- package/dist/tree-change/copy-tree.js.map +1 -0
- package/dist/tree-change/files.d.ts +167 -0
- package/dist/tree-change/files.d.ts.map +1 -0
- package/dist/tree-change/files.js +423 -0
- package/dist/tree-change/files.js.map +1 -0
- package/dist/tree-change/followed-walk.d.ts +54 -0
- package/dist/tree-change/followed-walk.d.ts.map +1 -0
- package/dist/tree-change/followed-walk.js +81 -0
- package/dist/tree-change/followed-walk.js.map +1 -0
- package/dist/tree-change/host-chain.d.ts +48 -0
- package/dist/tree-change/host-chain.d.ts.map +1 -0
- package/dist/tree-change/host-chain.js +85 -0
- package/dist/tree-change/host-chain.js.map +1 -0
- package/dist/tree-change/identity-compare.d.ts +61 -0
- package/dist/tree-change/identity-compare.d.ts.map +1 -0
- package/dist/tree-change/identity-compare.js +115 -0
- package/dist/tree-change/identity-compare.js.map +1 -0
- package/dist/tree-change/identity.d.ts +62 -0
- package/dist/tree-change/identity.d.ts.map +1 -0
- package/dist/tree-change/identity.js +152 -0
- package/dist/tree-change/identity.js.map +1 -0
- package/dist/tree-change/index.d.ts +24 -0
- package/dist/tree-change/index.d.ts.map +1 -0
- package/dist/tree-change/index.js +14 -0
- package/dist/tree-change/index.js.map +1 -0
- package/dist/tree-change/link-chain.d.ts +180 -0
- package/dist/tree-change/link-chain.d.ts.map +1 -0
- package/dist/tree-change/link-chain.js +377 -0
- package/dist/tree-change/link-chain.js.map +1 -0
- package/dist/tree-change/link-containment.d.ts +105 -0
- package/dist/tree-change/link-containment.d.ts.map +1 -0
- package/dist/tree-change/link-containment.js +196 -0
- package/dist/tree-change/link-containment.js.map +1 -0
- package/dist/tree-change/link-walk.d.ts +49 -0
- package/dist/tree-change/link-walk.d.ts.map +1 -0
- package/dist/tree-change/link-walk.js +69 -0
- package/dist/tree-change/link-walk.js.map +1 -0
- package/dist/tree-change/plan.d.ts +206 -0
- package/dist/tree-change/plan.d.ts.map +1 -0
- package/dist/tree-change/plan.js +320 -0
- package/dist/tree-change/plan.js.map +1 -0
- package/dist/tree-change/readable-tree.d.ts +44 -0
- package/dist/tree-change/readable-tree.d.ts.map +1 -0
- package/dist/tree-change/readable-tree.js +52 -0
- package/dist/tree-change/readable-tree.js.map +1 -0
- package/dist/tree-change/rollback-error.d.ts +29 -0
- package/dist/tree-change/rollback-error.d.ts.map +1 -0
- package/dist/tree-change/rollback-error.js +31 -0
- package/dist/tree-change/rollback-error.js.map +1 -0
- package/dist/tree-change/staging-names.d.ts +27 -0
- package/dist/tree-change/staging-names.d.ts.map +1 -0
- package/dist/tree-change/staging-names.js +40 -0
- package/dist/tree-change/staging-names.js.map +1 -0
- package/dist/tree-change/tree-walk.d.ts +81 -0
- package/dist/tree-change/tree-walk.d.ts.map +1 -0
- package/dist/tree-change/tree-walk.js +212 -0
- package/dist/tree-change/tree-walk.js.map +1 -0
- package/dist/win32-namespace.d.ts +24 -0
- package/dist/win32-namespace.d.ts.map +1 -0
- package/dist/win32-namespace.js +31 -0
- package/dist/win32-namespace.js.map +1 -0
- package/dist/yaml/surgical-yaml.d.ts +12 -3
- package/dist/yaml/surgical-yaml.d.ts.map +1 -1
- package/dist/yaml/surgical-yaml.js +16 -6
- package/dist/yaml/surgical-yaml.js.map +1 -1
- package/eslint/README.md +12 -4
- package/eslint/rules/fs-binding-scan.cjs +278 -0
- package/eslint/rules/no-adhoc-errno.cjs +364 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
- package/eslint/rules/no-blind-catch.cjs +1 -1
- package/eslint/rules/no-destructive-fs.cjs +359 -0
- package/eslint/rules/no-existssync.cjs +70 -0
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
- package/eslint/rules/no-link-following-fs.cjs +68 -0
- package/eslint/rules/no-literal-process-exit.cjs +96 -8
- package/eslint/rules/no-literal-test-timeout.cjs +214 -0
- package/eslint/rules/no-raw-signal-listener.cjs +224 -0
- package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
- package/package.json +10 -2
- package/dist/errors/errno.d.ts +0 -51
- package/dist/errors/errno.d.ts.map +0 -1
- package/dist/errors/errno.js +0 -112
- package/dist/errors/errno.js.map +0 -1
- package/eslint/rules/no-fs-promises-cp.cjs +0 -43
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The readable-tree proof: before a verb writes anything for a tree it is about to
|
|
3
|
+
* read whole, it proves every entry readable, so an unreadable input is refused as
|
|
4
|
+
* the input it is rather than surfacing half-way through as an output failure.
|
|
5
|
+
*/
|
|
6
|
+
import { openForReading } from '../text-file.js';
|
|
7
|
+
import { walkTree } from './tree-walk.js';
|
|
8
|
+
/** Proving reads nothing past the open: the open, the `fstat` and the listing are the proof. */
|
|
9
|
+
const PROVE = {
|
|
10
|
+
directory: () => Promise.resolve(),
|
|
11
|
+
file: () => Promise.resolve(),
|
|
12
|
+
link: () => Promise.resolve(),
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Prove the directory `root` readable: every directory listed, every regular file
|
|
16
|
+
* opened without blocking and judged by `fstat` on that handle, each entry held to
|
|
17
|
+
* the one special-file and link policy `copyTree` copies by (see `tree-walk.ts`).
|
|
18
|
+
* A root that is not a directory is refused by its listing (`ENOTDIR`), as
|
|
19
|
+
* `copyTree` refuses it. Each file is opened, never `access`ed: on Windows `access`
|
|
20
|
+
* does not consult ACLs.
|
|
21
|
+
*
|
|
22
|
+
* @param root - The directory the caller is about to read
|
|
23
|
+
* @param options - `links` and `filter` as the read will use them, and the `side` the tree is on
|
|
24
|
+
* @throws FsFaultError on `options.side` (origin `content`) naming the first entry
|
|
25
|
+
* the OS refused, in listing order — a named pipe, socket or device as class
|
|
26
|
+
* `wrong-type`; `CopyLinkEscapesSourceError` / `DirectoryWalkRevisitedError` for
|
|
27
|
+
* a link out of, or back into, a `follow-contained` tree
|
|
28
|
+
*/
|
|
29
|
+
export async function proveTreeReadable(root, options) {
|
|
30
|
+
await walkTree(root, options, options.side, PROVE);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Read a regular file whole, through a handle opened without blocking and judged
|
|
34
|
+
* by `fstat` on that handle: a named pipe, socket or device is refused before a
|
|
35
|
+
* byte is read, never waited on.
|
|
36
|
+
*
|
|
37
|
+
* @param path - The file to read
|
|
38
|
+
* @returns Its bytes
|
|
39
|
+
* @throws an `EFTYPE` errno for a special file, `EISDIR` for a directory, and
|
|
40
|
+
* whatever `open` / `read` throw otherwise — raw, for the caller to classify on
|
|
41
|
+
* the side it knows the file is on
|
|
42
|
+
*/
|
|
43
|
+
export async function readRegularFile(path) {
|
|
44
|
+
const handle = await openForReading(path);
|
|
45
|
+
try {
|
|
46
|
+
return await handle.readFile();
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await handle.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=readable-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readable-tree.js","sourceRoot":"","sources":["../../src/tree-change/readable-tree.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAA0C,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAWlF,gGAAgG;AAChG,MAAM,KAAK,GAAgB;IACzB,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAClC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC7B,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAiC;IACrF,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one shape of a change that failed and could not be fully undone. Pure: it only words
|
|
3
|
+
* what the rollback found.
|
|
4
|
+
*/
|
|
5
|
+
import { VatError } from '../errors/vat-error.js';
|
|
6
|
+
/** A failed change that could not be fully undone: a previous entry not back at its destination, or a new one not off it. */
|
|
7
|
+
export declare const TREE_ROLLBACK_INCOMPLETE_CODE = "TREE_ROLLBACK_INCOMPLETE";
|
|
8
|
+
/** One destination a rollback could not restore: where its previous entry is, if it had one, and why. */
|
|
9
|
+
export interface TreeRollbackStranded {
|
|
10
|
+
readonly dest: string;
|
|
11
|
+
readonly parked: string | undefined;
|
|
12
|
+
readonly why: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A change failed, and the rollback could not undo all of it: each previous entry
|
|
16
|
+
* not back at its destination is under its parked name, never deleted; a create's
|
|
17
|
+
* new entry — or a rewritten file's new content — may still be at its destination.
|
|
18
|
+
* `cause` is the failure that started the rollback. The one shape of
|
|
19
|
+
* {@link TREE_ROLLBACK_INCOMPLETE_CODE}: a caller that undoes writes of its own (a
|
|
20
|
+
* registry edit run as `afterSwap`) throws it too, its entries with no `parked`.
|
|
21
|
+
*/
|
|
22
|
+
export declare class TreeRollbackIncompleteError extends VatError {
|
|
23
|
+
/** Where each previous entry that is not at its destination now is. */
|
|
24
|
+
readonly parked: readonly string[];
|
|
25
|
+
/** Every destination the rollback could not restore. */
|
|
26
|
+
readonly stranded: readonly TreeRollbackStranded[];
|
|
27
|
+
constructor(original: unknown, stranded: readonly TreeRollbackStranded[]);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=rollback-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollback-error.d.ts","sourceRoot":"","sources":["../../src/tree-change/rollback-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,6HAA6H;AAC7H,eAAO,MAAM,6BAA6B,6BAA6B,CAAC;AAExE,yGAAyG;AACzG,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;GAOG;AACH,qBAAa,2BAA4B,SAAQ,QAAQ;IACvD,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,CAAC;gBAEvC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,oBAAoB,EAAE;CAMzE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one shape of a change that failed and could not be fully undone. Pure: it only words
|
|
3
|
+
* what the rollback found.
|
|
4
|
+
*/
|
|
5
|
+
import { VatError } from '../errors/vat-error.js';
|
|
6
|
+
/** A failed change that could not be fully undone: a previous entry not back at its destination, or a new one not off it. */
|
|
7
|
+
export const TREE_ROLLBACK_INCOMPLETE_CODE = 'TREE_ROLLBACK_INCOMPLETE';
|
|
8
|
+
function strandedSentence({ dest, parked, why }) {
|
|
9
|
+
return parked === undefined ? `the new content at ${dest} could not be undone (${why})` : `the previous content of ${dest} is at ${parked} (${why})`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A change failed, and the rollback could not undo all of it: each previous entry
|
|
13
|
+
* not back at its destination is under its parked name, never deleted; a create's
|
|
14
|
+
* new entry — or a rewritten file's new content — may still be at its destination.
|
|
15
|
+
* `cause` is the failure that started the rollback. The one shape of
|
|
16
|
+
* {@link TREE_ROLLBACK_INCOMPLETE_CODE}: a caller that undoes writes of its own (a
|
|
17
|
+
* registry edit run as `afterSwap`) throws it too, its entries with no `parked`.
|
|
18
|
+
*/
|
|
19
|
+
export class TreeRollbackIncompleteError extends VatError {
|
|
20
|
+
/** Where each previous entry that is not at its destination now is. */
|
|
21
|
+
parked;
|
|
22
|
+
/** Every destination the rollback could not restore. */
|
|
23
|
+
stranded;
|
|
24
|
+
constructor(original, stranded) {
|
|
25
|
+
const where = stranded.map((s) => strandedSentence(s)).join('; ');
|
|
26
|
+
super(TREE_ROLLBACK_INCOMPLETE_CODE, `${original instanceof Error ? original.message : String(original)}; and the change could not be undone: ${where}`, { cause: original });
|
|
27
|
+
this.parked = stranded.flatMap((s) => (s.parked === undefined ? [] : [s.parked]));
|
|
28
|
+
this.stranded = stranded;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rollback-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollback-error.js","sourceRoot":"","sources":["../../src/tree-change/rollback-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,6HAA6H;AAC7H,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AASxE,SAAS,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAwB;IACnE,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,sBAAsB,IAAI,yBAAyB,GAAG,GAAG,CAAC,CAAC,CAAC,2BAA2B,IAAI,UAAU,MAAM,KAAK,GAAG,GAAG,CAAC;AACvJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,2BAA4B,SAAQ,QAAQ;IACvD,uEAAuE;IAC9D,MAAM,CAAoB;IACnC,wDAAwD;IAC/C,QAAQ,CAAkC;IAEnD,YAAY,QAAiB,EAAE,QAAyC;QACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,KAAK,CAAC,6BAA6B,EAAE,GAAG,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,yCAAyC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9K,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAClF,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names the tree-change primitive gives what it stages, parks and discards beside a
|
|
3
|
+
* destination, and the two questions a listing asks of a name. Pure: no filesystem call.
|
|
4
|
+
*/
|
|
5
|
+
/** What a previous entry is renamed to while its replacement goes in: `<staged>.previous`. */
|
|
6
|
+
export declare const PARKED_SUFFIX = ".previous";
|
|
7
|
+
/**
|
|
8
|
+
* Whether a directory entry is the primitive's own: a staged tree, a parked
|
|
9
|
+
* previous one, or a discarded one, left beside a destination by a crash or by a
|
|
10
|
+
* removal the OS refused (`.<base>.vat-staged-<random>[.previous]`). A listing of
|
|
11
|
+
* the directory a change sits in must never read one as a sibling it holds.
|
|
12
|
+
*
|
|
13
|
+
* @param name - A directory entry's name
|
|
14
|
+
*/
|
|
15
|
+
export declare function isTreeChangeResidue(name: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Whether a directory entry is a PARKED previous entry: the one residue that is safe to sweep, since a
|
|
18
|
+
* staged entry may be another process's change still in flight.
|
|
19
|
+
*
|
|
20
|
+
* @param name - A directory entry's name
|
|
21
|
+
*/
|
|
22
|
+
export declare function isParkedTreeEntry(name: string): boolean;
|
|
23
|
+
/** The `mkdtemp` prefix of a staged tree beside `dest`. */
|
|
24
|
+
export declare function stagingPrefix(dest: string): string;
|
|
25
|
+
/** A fresh staging name beside `dest`, for an entry `mkdtemp` does not make: a file, a link, or a `write` fill's directory (made with the caller's mode). */
|
|
26
|
+
export declare function stagingName(dest: string): string;
|
|
27
|
+
//# sourceMappingURL=staging-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging-names.d.ts","sourceRoot":"","sources":["../../src/tree-change/staging-names.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,8FAA8F;AAC9F,eAAO,MAAM,aAAa,cAAc,CAAC;AAEzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,6JAA6J;AAC7J,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names the tree-change primitive gives what it stages, parks and discards beside a
|
|
3
|
+
* destination, and the two questions a listing asks of a name. Pure: no filesystem call.
|
|
4
|
+
*/
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { safePath } from '../path-core.js';
|
|
8
|
+
/** Every staged, parked or discarded entry is named `.<base>.vat-staged-<random>[suffix]` beside the destination. */
|
|
9
|
+
const STAGED_INFIX = '.vat-staged-';
|
|
10
|
+
/** What a previous entry is renamed to while its replacement goes in: `<staged>.previous`. */
|
|
11
|
+
export const PARKED_SUFFIX = '.previous';
|
|
12
|
+
/**
|
|
13
|
+
* Whether a directory entry is the primitive's own: a staged tree, a parked
|
|
14
|
+
* previous one, or a discarded one, left beside a destination by a crash or by a
|
|
15
|
+
* removal the OS refused (`.<base>.vat-staged-<random>[.previous]`). A listing of
|
|
16
|
+
* the directory a change sits in must never read one as a sibling it holds.
|
|
17
|
+
*
|
|
18
|
+
* @param name - A directory entry's name
|
|
19
|
+
*/
|
|
20
|
+
export function isTreeChangeResidue(name) {
|
|
21
|
+
return name.startsWith('.') && name.includes(STAGED_INFIX);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether a directory entry is a PARKED previous entry: the one residue that is safe to sweep, since a
|
|
25
|
+
* staged entry may be another process's change still in flight.
|
|
26
|
+
*
|
|
27
|
+
* @param name - A directory entry's name
|
|
28
|
+
*/
|
|
29
|
+
export function isParkedTreeEntry(name) {
|
|
30
|
+
return isTreeChangeResidue(name) && name.endsWith(PARKED_SUFFIX);
|
|
31
|
+
}
|
|
32
|
+
/** The `mkdtemp` prefix of a staged tree beside `dest`. */
|
|
33
|
+
export function stagingPrefix(dest) {
|
|
34
|
+
return safePath.join(path.dirname(dest), `.${path.basename(dest)}${STAGED_INFIX}`);
|
|
35
|
+
}
|
|
36
|
+
/** A fresh staging name beside `dest`, for an entry `mkdtemp` does not make: a file, a link, or a `write` fill's directory (made with the caller's mode). */
|
|
37
|
+
export function stagingName(dest) {
|
|
38
|
+
return `${stagingPrefix(dest)}${randomBytes(4).toString('hex')}`;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=staging-names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staging-names.js","sourceRoot":"","sources":["../../src/tree-change/staging-names.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,qHAAqH;AACrH,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;AACnE,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,6JAA6J;AAC7J,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one walk over a tree that is about to be read whole: `proveTreeReadable`
|
|
3
|
+
* runs it to prove a source before anything is written, and `copyTree` runs it to
|
|
4
|
+
* copy that source. One walk, so the proof and the copy can never disagree about
|
|
5
|
+
* which entries exist, which links are followed, or which entries are refused.
|
|
6
|
+
*
|
|
7
|
+
* Every entry is judged by one special-file policy, in this order:
|
|
8
|
+
*
|
|
9
|
+
* - A name the caller's `filter` excludes is skipped before anything touches it:
|
|
10
|
+
* no `stat`, no `open`, and a directory's subtree is never listed.
|
|
11
|
+
* - A link under `links: 'preserve'` is handed over as a link; its target is never
|
|
12
|
+
* examined.
|
|
13
|
+
* - A link under `links: 'refuse'` refuses the walk, naming it ({@link linkRefused}): the policy
|
|
14
|
+
* of a tree that holds none by construction, so one found there is never followed or copied.
|
|
15
|
+
* - A link under `links: 'follow-contained'` is answered by its target (`stat`, so
|
|
16
|
+
* a dangling link is an absent source). A directory target is entered first, so a
|
|
17
|
+
* link back into the walk is named as the loop it is
|
|
18
|
+
* ({@link DirectoryWalkRevisitedError}); then a target outside the root is refused
|
|
19
|
+
* ({@link CopyLinkEscapesSourceError}).
|
|
20
|
+
* - A named pipe, socket or device (or a link to one) is refused unopened, as the
|
|
21
|
+
* `EFTYPE` errno `refuseSpecialFile` raises.
|
|
22
|
+
* - A regular file is opened without blocking and judged again by `fstat` on that
|
|
23
|
+
* handle, which is the one the visitor reads: a pipe swapped in after the listing
|
|
24
|
+
* is refused there, never waited on.
|
|
25
|
+
*
|
|
26
|
+
* The ROOT is an entry like any other: it is `lstat`-ed, and a root that is a link is handed over
|
|
27
|
+
* (`preserve`) or refused (`refuse`) exactly as a link under it would be. Only `follow-contained`
|
|
28
|
+
* takes a root link for the directory it points at — the root is what containment is judged
|
|
29
|
+
* against, so it cannot be outside itself.
|
|
30
|
+
*
|
|
31
|
+
* Every READ the walk makes (list, `stat`, `open`, `fstat`, `realpath`) that the OS
|
|
32
|
+
* refuses is a classified fault on the caller's `side`, origin `content`, naming the
|
|
33
|
+
* entry. What a visitor WRITES is the visitor's: the walk never classifies it.
|
|
34
|
+
*/
|
|
35
|
+
import type { Stats } from 'node:fs';
|
|
36
|
+
import { type FileHandle } from 'node:fs/promises';
|
|
37
|
+
import { type FsSide } from '../errors/fs-fault.js';
|
|
38
|
+
/** How a walk treats a symlink: follow it, contained to the root; keep it as a link; or refuse the tree that holds it. */
|
|
39
|
+
export type LinkPolicy = 'follow-contained' | 'preserve' | 'refuse';
|
|
40
|
+
/** What every walk over a tree is told. */
|
|
41
|
+
export interface TreeWalkOptions {
|
|
42
|
+
/** `follow-contained`: a link is what it points at, inside the root only. `preserve`: a link is a link. `refuse`: a link refuses the walk. */
|
|
43
|
+
readonly links: LinkPolicy;
|
|
44
|
+
/** Keep the entry at this forward-slash path relative to the root; an excluded one is never touched. Omitted: keep everything. */
|
|
45
|
+
readonly filter?: (relative: string) => boolean;
|
|
46
|
+
}
|
|
47
|
+
/** One entry the walk accepted: where it is, and where it is relative to the root (`''` for the root). */
|
|
48
|
+
interface WalkedEntry {
|
|
49
|
+
readonly path: string;
|
|
50
|
+
readonly relative: string;
|
|
51
|
+
}
|
|
52
|
+
/** What the walk hands each accepted entry to. */
|
|
53
|
+
export interface TreeVisitor {
|
|
54
|
+
/** A directory, before any of its entries. */
|
|
55
|
+
directory(entry: WalkedEntry, stats: Stats): Promise<void>;
|
|
56
|
+
/** A regular file, through the handle `fstat` judged; the walk closes it. */
|
|
57
|
+
file(entry: WalkedEntry, handle: FileHandle, stats: Stats): Promise<void>;
|
|
58
|
+
/** A link under `links: 'preserve'`, never followed by the walk: the visitor decides what to read of it. */
|
|
59
|
+
link(entry: WalkedEntry): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A refusal by the OS while reading the tree, as a fault on `side` (origin
|
|
63
|
+
* `content`) naming `entry` when the OS named no path. A coded refusal and a defect
|
|
64
|
+
* come back as they are. The one classification every read of the tree goes through.
|
|
65
|
+
*/
|
|
66
|
+
export declare function treeReadFault(side: FsSide, entry: string, error: unknown): unknown;
|
|
67
|
+
/** Run one read of the tree; a refusal is rethrown by {@link treeReadFault}. */
|
|
68
|
+
export declare function readingTree<T>(side: FsSide, entry: string, read: () => Promise<T>): Promise<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Walk the directory `root` under `options`, handing each accepted entry to
|
|
71
|
+
* `visitor` in listing order, the root first (relative `''`).
|
|
72
|
+
*
|
|
73
|
+
* @param root - The directory to walk; one that is not a directory is refused by its listing, and one
|
|
74
|
+
* that is a link is judged by `options.links` like any link under it
|
|
75
|
+
* @param options - The link policy and the filter
|
|
76
|
+
* @param side - The side every read of the walk is classified on
|
|
77
|
+
* @param visitor - What to do with each entry
|
|
78
|
+
*/
|
|
79
|
+
export declare function walkTree(root: string, options: TreeWalkOptions, side: FsSide, visitor: TreeVisitor): Promise<void>;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=tree-walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-walk.d.ts","sourceRoot":"","sources":["../../src/tree-change/tree-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAU,KAAK,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAW,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAiC,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAUnF,0HAA0H;AAC1H,MAAM,MAAM,UAAU,GAAG,kBAAkB,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEpE,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,8IAA8I;IAC9I,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,kIAAkI;IAClI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACjD;AAED,0GAA0G;AAC1G,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,6EAA6E;IAC7E,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,4GAA4G;IAC5G,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAElF;AAmBD,gFAAgF;AAChF,wBAAsB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMpG;AAkID;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAExH"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one walk over a tree that is about to be read whole: `proveTreeReadable`
|
|
3
|
+
* runs it to prove a source before anything is written, and `copyTree` runs it to
|
|
4
|
+
* copy that source. One walk, so the proof and the copy can never disagree about
|
|
5
|
+
* which entries exist, which links are followed, or which entries are refused.
|
|
6
|
+
*
|
|
7
|
+
* Every entry is judged by one special-file policy, in this order:
|
|
8
|
+
*
|
|
9
|
+
* - A name the caller's `filter` excludes is skipped before anything touches it:
|
|
10
|
+
* no `stat`, no `open`, and a directory's subtree is never listed.
|
|
11
|
+
* - A link under `links: 'preserve'` is handed over as a link; its target is never
|
|
12
|
+
* examined.
|
|
13
|
+
* - A link under `links: 'refuse'` refuses the walk, naming it ({@link linkRefused}): the policy
|
|
14
|
+
* of a tree that holds none by construction, so one found there is never followed or copied.
|
|
15
|
+
* - A link under `links: 'follow-contained'` is answered by its target (`stat`, so
|
|
16
|
+
* a dangling link is an absent source). A directory target is entered first, so a
|
|
17
|
+
* link back into the walk is named as the loop it is
|
|
18
|
+
* ({@link DirectoryWalkRevisitedError}); then a target outside the root is refused
|
|
19
|
+
* ({@link CopyLinkEscapesSourceError}).
|
|
20
|
+
* - A named pipe, socket or device (or a link to one) is refused unopened, as the
|
|
21
|
+
* `EFTYPE` errno `refuseSpecialFile` raises.
|
|
22
|
+
* - A regular file is opened without blocking and judged again by `fstat` on that
|
|
23
|
+
* handle, which is the one the visitor reads: a pipe swapped in after the listing
|
|
24
|
+
* is refused there, never waited on.
|
|
25
|
+
*
|
|
26
|
+
* The ROOT is an entry like any other: it is `lstat`-ed, and a root that is a link is handed over
|
|
27
|
+
* (`preserve`) or refused (`refuse`) exactly as a link under it would be. Only `follow-contained`
|
|
28
|
+
* takes a root link for the directory it points at — the root is what containment is judged
|
|
29
|
+
* against, so it cannot be outside itself.
|
|
30
|
+
*
|
|
31
|
+
* Every READ the walk makes (list, `stat`, `open`, `fstat`, `realpath`) that the OS
|
|
32
|
+
* refuses is a classified fault on the caller's `side`, origin `content`, naming the
|
|
33
|
+
* entry. What a visitor WRITES is the visitor's: the walk never classifies it.
|
|
34
|
+
*/
|
|
35
|
+
import fs from 'node:fs/promises';
|
|
36
|
+
import { classifyFsFault, FsFaultError } from '../errors/fs-fault.js';
|
|
37
|
+
import { forEachInOrder } from '../in-order.js';
|
|
38
|
+
import { requireFollowedStat } from '../link-following.js';
|
|
39
|
+
import { isUnderRoot } from '../path-containment.js';
|
|
40
|
+
import { safePath } from '../path-core.js';
|
|
41
|
+
import { refuseSpecialFile } from '../special-file.js';
|
|
42
|
+
import { openForReading } from '../text-file.js';
|
|
43
|
+
import { CopyLinkEscapesSourceError, FollowedWalk } from './followed-walk.js';
|
|
44
|
+
/**
|
|
45
|
+
* A refusal by the OS while reading the tree, as a fault on `side` (origin
|
|
46
|
+
* `content`) naming `entry` when the OS named no path. A coded refusal and a defect
|
|
47
|
+
* come back as they are. The one classification every read of the tree goes through.
|
|
48
|
+
*/
|
|
49
|
+
export function treeReadFault(side, entry, error) {
|
|
50
|
+
return classifyFsFault(error, { side, origin: 'content', action: `read ${entry}`, path: entry });
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The refusal of a link in a tree walked with `links: 'refuse'`: a `wrong-type` fault on `side`
|
|
54
|
+
* (origin `content`) naming the link, under the errno a special file is refused with — an entry
|
|
55
|
+
* of a kind the read does not take — so the one refusal table decides what the caller publishes.
|
|
56
|
+
*/
|
|
57
|
+
function linkRefused(side, link) {
|
|
58
|
+
return new FsFaultError({
|
|
59
|
+
side,
|
|
60
|
+
faultClass: 'wrong-type',
|
|
61
|
+
errno: 'EFTYPE',
|
|
62
|
+
path: link,
|
|
63
|
+
origin: 'content',
|
|
64
|
+
action: `read ${link}: it is a symbolic link, and this tree may hold none`,
|
|
65
|
+
cause: undefined,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Run one read of the tree; a refusal is rethrown by {@link treeReadFault}. */
|
|
69
|
+
export async function readingTree(side, entry, read) {
|
|
70
|
+
try {
|
|
71
|
+
return await read();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw treeReadFault(side, entry, error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** {@link readingTree}, synchronously: the walk guard and the containment check resolve real paths. */
|
|
78
|
+
function readingTreeSync(side, entry, read) {
|
|
79
|
+
try {
|
|
80
|
+
return read();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw treeReadFault(side, entry, error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Open `path` without blocking and judge it by `fstat` on that handle; hand the
|
|
88
|
+
* handle to `use` and close it after. A special file is refused (`EFTYPE`) and its
|
|
89
|
+
* handle closed before `use` runs. When `use` fails, ITS error is the one reported:
|
|
90
|
+
* the handle is still released, but a close that also fails must not replace a
|
|
91
|
+
* write the visitor failed (a source fault hiding a destination one).
|
|
92
|
+
*/
|
|
93
|
+
async function withRegularFile(side, path, use) {
|
|
94
|
+
const handle = await readingTree(side, path, () => openForReading(path));
|
|
95
|
+
try {
|
|
96
|
+
const stats = await readingTree(side, path, () => handle.stat());
|
|
97
|
+
await use(handle, stats);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
await Promise.allSettled([handle.close()]);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
// Closing the source's handle is a read of the source: a refused close is the source's fault too.
|
|
104
|
+
await readingTree(side, path, () => handle.close());
|
|
105
|
+
}
|
|
106
|
+
/** One walk: its root, its policy, the guard a following walk holds, and the visitor. */
|
|
107
|
+
class TreeWalk {
|
|
108
|
+
#root;
|
|
109
|
+
#options;
|
|
110
|
+
#side;
|
|
111
|
+
#visitor;
|
|
112
|
+
#guard;
|
|
113
|
+
constructor(root, options, side, visitor) {
|
|
114
|
+
this.#root = root;
|
|
115
|
+
this.#options = options;
|
|
116
|
+
this.#side = side;
|
|
117
|
+
this.#visitor = visitor;
|
|
118
|
+
this.#guard = options.links === 'follow-contained' ? new FollowedWalk(side, root) : undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Walk from the root: judged by `lstat`, as every entry under it is by its listing. A root that is
|
|
122
|
+
* a link is the policy's to decide — except under `follow-contained`, where the root is the
|
|
123
|
+
* directory it points at.
|
|
124
|
+
*/
|
|
125
|
+
async root(entry) {
|
|
126
|
+
if (this.#options.links !== 'follow-contained') {
|
|
127
|
+
const stats = await readingTree(this.#side, entry.path, () => fs.lstat(entry.path));
|
|
128
|
+
if (stats.isSymbolicLink()) {
|
|
129
|
+
await this.#link(entry);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
await this.directory(entry);
|
|
134
|
+
}
|
|
135
|
+
/** Enter the directory `entry` and visit it and everything under it, in listing order. */
|
|
136
|
+
async directory(entry) {
|
|
137
|
+
// Listed first: a root that is not a directory is refused by the listing (`ENOTDIR`), never opened.
|
|
138
|
+
const listing = await this.#list(entry.path);
|
|
139
|
+
const stats = await readingTree(this.#side, entry.path, () => requireFollowedStat(entry.path, this.#root));
|
|
140
|
+
this.#enter(entry.path);
|
|
141
|
+
await this.#visitDirectory(entry, listing, stats);
|
|
142
|
+
}
|
|
143
|
+
#list(dir) {
|
|
144
|
+
return readingTree(this.#side, dir, () => fs.readdir(dir, { withFileTypes: true }));
|
|
145
|
+
}
|
|
146
|
+
/** Visit an entered directory, then its entries in order: the guard must see each directory entered before the walk recurses, and the first refusal names the first offending entry in listing order. */
|
|
147
|
+
async #visitDirectory(entry, listing, stats) {
|
|
148
|
+
await this.#visitor.directory(entry, stats);
|
|
149
|
+
await forEachInOrder(listing, (dirent) => this.#entry(entry, dirent));
|
|
150
|
+
}
|
|
151
|
+
async #entry(parent, dirent) {
|
|
152
|
+
const entry = {
|
|
153
|
+
path: safePath.join(parent.path, dirent.name),
|
|
154
|
+
relative: parent.relative === '' ? dirent.name : `${parent.relative}/${dirent.name}`,
|
|
155
|
+
};
|
|
156
|
+
if (this.#options.filter !== undefined && !this.#options.filter(entry.relative))
|
|
157
|
+
return;
|
|
158
|
+
if (dirent.isSymbolicLink()) {
|
|
159
|
+
await this.#link(entry);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (dirent.isDirectory()) {
|
|
163
|
+
await this.directory(entry);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
await this.#nonDirectory(entry, dirent);
|
|
167
|
+
}
|
|
168
|
+
async #link(entry) {
|
|
169
|
+
if (this.#options.links === 'preserve') {
|
|
170
|
+
await this.#visitor.link(entry);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (this.#options.links === 'refuse')
|
|
174
|
+
throw linkRefused(this.#side, entry.path);
|
|
175
|
+
const target = await readingTree(this.#side, entry.path, () => requireFollowedStat(entry.path, this.#root));
|
|
176
|
+
// Revisit first, so a link back into the tree is named as the loop it is; then
|
|
177
|
+
// containment, so a link out is named as the escape it is.
|
|
178
|
+
if (target.isDirectory())
|
|
179
|
+
this.#enter(entry.path);
|
|
180
|
+
const contained = readingTreeSync(this.#side, entry.path, () => isUnderRoot(this.#root, entry.path));
|
|
181
|
+
if (contained !== 'inside')
|
|
182
|
+
throw new CopyLinkEscapesSourceError(entry.path, this.#root);
|
|
183
|
+
if (target.isDirectory()) {
|
|
184
|
+
await this.#visitDirectory(entry, await this.#list(entry.path), target);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await this.#nonDirectory(entry, target);
|
|
188
|
+
}
|
|
189
|
+
/** A file by its listing or its link's target: a special one is refused unopened, a regular one opened. */
|
|
190
|
+
async #nonDirectory(entry, kind) {
|
|
191
|
+
readingTreeSync(this.#side, entry.path, () => refuseSpecialFile(kind, entry.path));
|
|
192
|
+
await withRegularFile(this.#side, entry.path, (handle, stats) => this.#visitor.file(entry, handle, stats));
|
|
193
|
+
}
|
|
194
|
+
#enter(dir) {
|
|
195
|
+
const guard = this.#guard;
|
|
196
|
+
guard?.enter(dir);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Walk the directory `root` under `options`, handing each accepted entry to
|
|
201
|
+
* `visitor` in listing order, the root first (relative `''`).
|
|
202
|
+
*
|
|
203
|
+
* @param root - The directory to walk; one that is not a directory is refused by its listing, and one
|
|
204
|
+
* that is a link is judged by `options.links` like any link under it
|
|
205
|
+
* @param options - The link policy and the filter
|
|
206
|
+
* @param side - The side every read of the walk is classified on
|
|
207
|
+
* @param visitor - What to do with each entry
|
|
208
|
+
*/
|
|
209
|
+
export async function walkTree(root, options, side, visitor) {
|
|
210
|
+
await new TreeWalk(root, options, side, visitor).root({ path: root, relative: '' });
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=tree-walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-walk.js","sourceRoot":"","sources":["../../src/tree-change/tree-walk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The win32 namespace prefix Node may report a path with. A leaf that imports NOTHING, so the
|
|
3
|
+
* errno table — itself a leaf, for the reason its header gives — can read every errno's path
|
|
4
|
+
* through it, once.
|
|
5
|
+
*
|
|
6
|
+
* Module-internal to `utils`: no barrel and no subpath re-exports it; its two callers are the
|
|
7
|
+
* errno table and the tree-change apply.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A path without the win32 namespace prefix Node may report it with. Under win32 Node hands the OS
|
|
11
|
+
* a namespaced path (`\\?\C:\…`, `\\?\UNC\server\share\…`) and reports some paths back in
|
|
12
|
+
* that form — the first directory a recursive `mkdir` made, an errno's `path` and `dest`. Held
|
|
13
|
+
* against a plain `C:\…` path such a report reads as another root, and displayed it names
|
|
14
|
+
* `//?/C:/…`. Pure and the same on every host; every other path is returned as it is.
|
|
15
|
+
*
|
|
16
|
+
* @param reported - A native path, as Node reported it
|
|
17
|
+
* @returns The path in the spelling the caller's own paths have (native separators kept)
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* withoutWin32Namespace('\\\\?\\C:\\a') // 'C:\\a'
|
|
21
|
+
* withoutWin32Namespace('\\\\?\\UNC\\host\\share') // '\\\\host\\share'
|
|
22
|
+
*/
|
|
23
|
+
export declare function withoutWin32Namespace(reported: string): string;
|
|
24
|
+
//# sourceMappingURL=win32-namespace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"win32-namespace.d.ts","sourceRoot":"","sources":["../src/win32-namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI9D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The win32 namespace prefix Node may report a path with. A leaf that imports NOTHING, so the
|
|
3
|
+
* errno table — itself a leaf, for the reason its header gives — can read every errno's path
|
|
4
|
+
* through it, once.
|
|
5
|
+
*
|
|
6
|
+
* Module-internal to `utils`: no barrel and no subpath re-exports it; its two callers are the
|
|
7
|
+
* errno table and the tree-change apply.
|
|
8
|
+
*/
|
|
9
|
+
const WIN32_NAMESPACE = String.raw `\\?` + '\\';
|
|
10
|
+
const WIN32_UNC_NAMESPACE = `${WIN32_NAMESPACE}UNC\\`;
|
|
11
|
+
/**
|
|
12
|
+
* A path without the win32 namespace prefix Node may report it with. Under win32 Node hands the OS
|
|
13
|
+
* a namespaced path (`\\?\C:\…`, `\\?\UNC\server\share\…`) and reports some paths back in
|
|
14
|
+
* that form — the first directory a recursive `mkdir` made, an errno's `path` and `dest`. Held
|
|
15
|
+
* against a plain `C:\…` path such a report reads as another root, and displayed it names
|
|
16
|
+
* `//?/C:/…`. Pure and the same on every host; every other path is returned as it is.
|
|
17
|
+
*
|
|
18
|
+
* @param reported - A native path, as Node reported it
|
|
19
|
+
* @returns The path in the spelling the caller's own paths have (native separators kept)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* withoutWin32Namespace('\\\\?\\C:\\a') // 'C:\\a'
|
|
23
|
+
* withoutWin32Namespace('\\\\?\\UNC\\host\\share') // '\\\\host\\share'
|
|
24
|
+
*/
|
|
25
|
+
export function withoutWin32Namespace(reported) {
|
|
26
|
+
// Matched on the NATIVE spelling, before any separator conversion: the prefix exists only there.
|
|
27
|
+
if (reported.startsWith(WIN32_UNC_NAMESPACE))
|
|
28
|
+
return `\\\\${reported.slice(WIN32_UNC_NAMESPACE.length)}`;
|
|
29
|
+
return reported.startsWith(WIN32_NAMESPACE) ? reported.slice(WIN32_NAMESPACE.length) : reported;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=win32-namespace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"win32-namespace.js","sourceRoot":"","sources":["../src/win32-namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA,KAAK,GAAG,IAAI,CAAC;AAC/C,MAAM,mBAAmB,GAAG,GAAG,eAAe,OAAO,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,iGAAiG;IACjG,IAAI,QAAQ,CAAC,UAAU,CAAC,mBAAmB,CAAC;QAAE,OAAO,OAAO,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;IACzG,OAAO,QAAQ,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClG,CAAC"}
|
|
@@ -22,6 +22,14 @@
|
|
|
22
22
|
*
|
|
23
23
|
* No filesystem access — these are pure string transforms.
|
|
24
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* The code {@link updateYamlIn} throws when the INPUT document cannot take the
|
|
27
|
+
* edit — it is not YAML, or the path lands on a collection or runs through a
|
|
28
|
+
* scalar. The input's shape, not a defect: a caller editing an adopter's file
|
|
29
|
+
* reports it as that file's problem. Its other throws (an empty path, a failed
|
|
30
|
+
* post-condition) stay uncoded — they are the caller's or this module's bug.
|
|
31
|
+
*/
|
|
32
|
+
export declare const YAML_EDIT_INPUT_REFUSED_CODE = "YAML_EDIT_INPUT_REFUSED";
|
|
25
33
|
/** A path into a YAML document: map keys (strings) and/or sequence indices. */
|
|
26
34
|
export type YamlPath = (string | number)[];
|
|
27
35
|
/** A scalar value this module knows how to serialize as a single YAML token. */
|
|
@@ -42,9 +50,10 @@ export type YamlScalarValue = string | number | boolean | null;
|
|
|
42
50
|
* @param path - Key/index path to the scalar to set.
|
|
43
51
|
* @param value - The scalar value to write (string, number, boolean, or null).
|
|
44
52
|
* @returns The updated document text.
|
|
45
|
-
* @throws
|
|
46
|
-
* to a collection that would be clobbered
|
|
47
|
-
* ancestor is a scalar that cannot hold a
|
|
53
|
+
* @throws A `VatError` coded {@link YAML_EDIT_INPUT_REFUSED_CODE} if `text` is
|
|
54
|
+
* not valid YAML, or `path` resolves to a collection that would be clobbered
|
|
55
|
+
* by a scalar (or an intermediate ancestor is a scalar that cannot hold a
|
|
56
|
+
* child); a plain `Error` if `path` is empty.
|
|
48
57
|
*
|
|
49
58
|
* @example
|
|
50
59
|
* updateYamlIn('model: haiku # note\n', ['model'], 'opus')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surgical-yaml.d.ts","sourceRoot":"","sources":["../../src/yaml/surgical-yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"surgical-yaml.d.ts","sourceRoot":"","sources":["../../src/yaml/surgical-yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAgBH;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE,+EAA+E;AAC/E,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAE3C,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAK/D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CA4BzF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,QAAQ,EAAE,GACvB,IAAI,CAYN"}
|