@vibe-agent-toolkit/utils 0.1.42 → 0.2.0-rc.10
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 +81 -19
- package/dist/asset-reference.js +3 -11
- package/dist/asset-reference.js.map +1 -1
- package/dist/compare-code-units.d.ts +28 -0
- package/dist/compare-code-units.d.ts.map +1 -0
- package/dist/compare-code-units.js +33 -0
- package/dist/compare-code-units.js.map +1 -0
- package/dist/crawl-timing.d.ts +657 -0
- package/dist/crawl-timing.d.ts.map +1 -0
- package/dist/crawl-timing.js +765 -0
- package/dist/crawl-timing.js.map +1 -0
- package/dist/crawl.d.ts +2 -4
- package/dist/crawl.d.ts.map +1 -1
- package/dist/crawl.js +2 -4
- package/dist/crawl.js.map +1 -1
- package/dist/dirent-kind.d.ts +83 -0
- package/dist/dirent-kind.d.ts.map +1 -0
- package/dist/dirent-kind.js +129 -0
- package/dist/dirent-kind.js.map +1 -0
- package/dist/entrypoint.d.ts +54 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +71 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/env-flag.d.ts +51 -0
- package/dist/env-flag.d.ts.map +1 -0
- package/dist/env-flag.js +62 -0
- package/dist/env-flag.js.map +1 -0
- package/dist/errors/errno.d.ts +51 -0
- package/dist/errors/errno.d.ts.map +1 -0
- package/dist/errors/errno.js +112 -0
- package/dist/errors/errno.js.map +1 -0
- package/dist/errors/vat-error.d.ts +60 -0
- package/dist/errors/vat-error.d.ts.map +1 -0
- package/dist/errors/vat-error.js +86 -0
- package/dist/errors/vat-error.js.map +1 -0
- package/dist/file-crawler.d.ts +60 -0
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +214 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs/file-hash.d.ts.map +1 -1
- package/dist/fs/file-hash.js +0 -1
- package/dist/fs/file-hash.js.map +1 -1
- package/dist/fs-utils.d.ts +628 -38
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +770 -71
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +9 -1
- package/dist/fs.js.map +1 -1
- package/dist/git-root-cache.d.ts +44 -0
- package/dist/git-root-cache.d.ts.map +1 -0
- package/dist/git-root-cache.js +68 -0
- package/dist/git-root-cache.js.map +1 -0
- package/dist/git-run.d.ts +167 -0
- package/dist/git-run.d.ts.map +1 -0
- package/dist/git-run.js +154 -0
- package/dist/git-run.js.map +1 -0
- package/dist/git-snapshot.d.ts +199 -0
- package/dist/git-snapshot.d.ts.map +1 -0
- package/dist/git-snapshot.js +250 -0
- package/dist/git-snapshot.js.map +1 -0
- package/dist/git-tracker.d.ts +166 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +250 -22
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts +95 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +227 -81
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +13 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +12 -4
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +15 -6
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/glob/glob-pattern.js +5 -5
- package/dist/glob/glob-pattern.js.map +1 -1
- package/dist/index.d.ts +33 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -33
- package/dist/index.js.map +1 -1
- package/dist/listing-refusal.d.ts +112 -0
- package/dist/listing-refusal.d.ts.map +1 -0
- package/dist/listing-refusal.js +105 -0
- package/dist/listing-refusal.js.map +1 -0
- package/dist/numeric-args.d.ts +24 -0
- package/dist/numeric-args.d.ts.map +1 -0
- package/dist/numeric-args.js +37 -0
- package/dist/numeric-args.js.map +1 -0
- package/dist/path-containment.d.ts +64 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +111 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-core.d.ts +143 -18
- package/dist/path-core.d.ts.map +1 -1
- package/dist/path-core.js +171 -23
- package/dist/path-core.js.map +1 -1
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +42 -47
- package/dist/path-utils.js.map +1 -1
- package/dist/path.d.ts +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -1
- package/dist/path.js.map +1 -1
- package/dist/process.d.ts +7 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +7 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts +7 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +24 -10
- package/dist/project-utils.js.map +1 -1
- package/dist/project.d.ts +7 -5
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -5
- package/dist/project.js.map +1 -1
- package/dist/safe-exec.d.ts +40 -11
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +111 -25
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts +2 -2
- package/dist/skill-targets.js +2 -2
- package/dist/skill-test/auth-resolver.d.ts +10 -2
- package/dist/skill-test/auth-resolver.d.ts.map +1 -1
- package/dist/skill-test/auth-resolver.js +26 -11
- package/dist/skill-test/auth-resolver.js.map +1 -1
- package/dist/skill-test/index.d.ts +1 -1
- package/dist/skill-test/index.d.ts.map +1 -1
- package/dist/skill-test/index.js +1 -1
- package/dist/skill-test/index.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +30 -1
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +71 -5
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/skill-test/transcript.d.ts +41 -2
- package/dist/skill-test/transcript.d.ts.map +1 -1
- package/dist/skill-test/transcript.js +55 -10
- package/dist/skill-test/transcript.js.map +1 -1
- package/dist/spawn-hardened.js +0 -1
- package/dist/spawn-hardened.js.map +1 -1
- package/dist/stdio-blocking.js +13 -14
- package/dist/stdio-blocking.js.map +1 -1
- package/dist/test-helpers.d.ts +130 -79
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +229 -124
- package/dist/test-helpers.js.map +1 -1
- package/dist/testing/executables.d.ts +32 -0
- package/dist/testing/executables.d.ts.map +1 -0
- package/dist/testing/executables.js +70 -0
- package/dist/testing/executables.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts +79 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -0
- package/dist/testing/hostile-tree.js +164 -0
- package/dist/testing/hostile-tree.js.map +1 -0
- package/dist/testing/platform-gates.d.ts +31 -0
- package/dist/testing/platform-gates.d.ts.map +1 -0
- package/dist/testing/platform-gates.js +31 -0
- package/dist/testing/platform-gates.js.map +1 -0
- package/dist/testing/temp-dir.d.ts +255 -0
- package/dist/testing/temp-dir.d.ts.map +1 -0
- package/dist/testing/temp-dir.js +363 -0
- package/dist/testing/temp-dir.js.map +1 -0
- package/dist/testing.d.ts +94 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +119 -3
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +197 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +354 -0
- package/dist/text-content.js.map +1 -0
- package/dist/text-file.d.ts +45 -0
- package/dist/text-file.d.ts.map +1 -0
- package/dist/text-file.js +51 -0
- package/dist/text-file.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/dist/timing-dump.d.ts +136 -0
- package/dist/timing-dump.d.ts.map +1 -0
- package/dist/timing-dump.js +229 -0
- package/dist/timing-dump.js.map +1 -0
- package/dist/windows-shell.d.ts +0 -1
- package/dist/windows-shell.d.ts.map +1 -1
- package/dist/windows-shell.js +0 -1
- package/dist/windows-shell.js.map +1 -1
- package/dist/zod-introspection.js +2 -2
- package/dist/zod-introspection.js.map +1 -1
- package/eslint/README.md +224 -50
- package/eslint/index.cjs +111 -97
- package/eslint/index.d.cts +16 -6
- package/eslint/rules/callee-name.cjs +23 -0
- package/eslint/rules/commands-import-boundary.cjs +148 -0
- package/eslint/rules/dead-import.cjs +62 -12
- package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
- package/eslint/rules/eslint-rule-factory.cjs +21 -3
- package/eslint/rules/exempt-path-matcher.cjs +2 -1
- package/eslint/rules/explicit-zod-strictness.cjs +120 -0
- package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
- package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
- package/eslint/rules/no-blind-catch.cjs +171 -0
- package/eslint/rules/no-child-process-execSync.cjs +8 -0
- package/eslint/rules/no-decaying-referent.cjs +217 -0
- package/eslint/rules/no-dotdot-containment.cjs +160 -0
- package/eslint/rules/no-file-url-string-concat.cjs +4 -1
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
- package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
- package/eslint/rules/no-fs-promises-cp.cjs +7 -0
- package/eslint/rules/no-fs-realpathSync.cjs +8 -0
- package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
- package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
- package/eslint/rules/no-literal-process-exit.cjs +129 -0
- package/eslint/rules/no-manual-path-normalize.cjs +146 -96
- package/eslint/rules/no-os-tmpdir.cjs +8 -0
- package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
- package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
- package/eslint/rules/no-path-startswith.cjs +7 -2
- package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
- package/eslint/rules/no-raw-node-path.cjs +563 -0
- package/eslint/rules/no-raw-text-decode.cjs +305 -0
- package/eslint/rules/no-registry-count-pin.cjs +255 -0
- package/eslint/rules/no-self-package-import.cjs +168 -0
- package/eslint/rules/no-test-scoped-functions.cjs +10 -3
- package/eslint/rules/no-unix-shell-commands.cjs +6 -2
- package/eslint/rules/no-unsafe-root-join.cjs +23 -2
- package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
- package/eslint/rules/no-version-literal.cjs +151 -0
- package/eslint/rules/prefer-startswith-over-regex.cjs +33 -1
- package/eslint/rules/require-justified-skip.cjs +8 -2
- package/eslint/rules/safe-import.cjs +1 -1
- package/package.json +16 -16
- package/dist/link-auth/build-headers.d.ts +0 -34
- package/dist/link-auth/build-headers.d.ts.map +0 -1
- package/dist/link-auth/build-headers.js +0 -58
- package/dist/link-auth/build-headers.js.map +0 -1
- package/dist/link-auth/expand-macro.d.ts +0 -38
- package/dist/link-auth/expand-macro.d.ts.map +0 -1
- package/dist/link-auth/expand-macro.js +0 -133
- package/dist/link-auth/expand-macro.js.map +0 -1
- package/dist/link-auth/macros.yaml +0 -50
- package/dist/link-auth/resolve-token.d.ts +0 -83
- package/dist/link-auth/resolve-token.d.ts.map +0 -1
- package/dist/link-auth/resolve-token.js +0 -101
- package/dist/link-auth/resolve-token.js.map +0 -1
- package/dist/link-auth/resolve.d.ts +0 -102
- package/dist/link-auth/resolve.d.ts.map +0 -1
- package/dist/link-auth/resolve.js +0 -66
- package/dist/link-auth/resolve.js.map +0 -1
- package/dist/link-auth/rewrite.d.ts +0 -52
- package/dist/link-auth/rewrite.d.ts.map +0 -1
- package/dist/link-auth/rewrite.js +0 -102
- package/dist/link-auth/rewrite.js.map +0 -1
- package/dist/link-auth/select-provider.d.ts +0 -30
- package/dist/link-auth/select-provider.d.ts.map +0 -1
- package/dist/link-auth/select-provider.js +0 -55
- package/dist/link-auth/select-provider.js.map +0 -1
- package/dist/link-auth/template.d.ts +0 -40
- package/dist/link-auth/template.d.ts.map +0 -1
- package/dist/link-auth/template.js +0 -89
- package/dist/link-auth/template.js.map +0 -1
- package/dist/link-auth/transforms.d.ts +0 -46
- package/dist/link-auth/transforms.d.ts.map +0 -1
- package/dist/link-auth/transforms.js +0 -52
- package/dist/link-auth/transforms.js.map +0 -1
- package/dist/template-entry.d.ts +0 -10
- package/dist/template-entry.d.ts.map +0 -1
- package/dist/template-entry.js +0 -10
- package/dist/template-entry.js.map +0 -1
- package/dist/template.d.ts +0 -7
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -18
- package/dist/template.js.map +0 -1
- package/eslint/rules/no-path-join.cjs +0 -14
- package/eslint/rules/no-path-relative.cjs +0 -14
- package/eslint/rules/no-path-resolve.cjs +0 -14
- package/eslint/rules/path-function-rule-factory.cjs +0 -417
package/dist/git-tracker.d.ts
CHANGED
|
@@ -56,6 +56,33 @@ export declare class GitTracker {
|
|
|
56
56
|
private readonly activeSet;
|
|
57
57
|
/** Absolute paths of every directory that contains at least one active-set file. */
|
|
58
58
|
private readonly activeAncestors;
|
|
59
|
+
/**
|
|
60
|
+
* Lowercased absolute path → the root-relative path spelled the way git
|
|
61
|
+
* spelled it. Built from the very same `git ls-files` output `activeSet`
|
|
62
|
+
* comes from, so it costs one extra `Map` and no extra git invocation.
|
|
63
|
+
*/
|
|
64
|
+
private readonly indexPaths;
|
|
65
|
+
/**
|
|
66
|
+
* Absolute, forward-slashed directories the active-set listing could not
|
|
67
|
+
* OPEN, so nothing beneath them is in the set — read off `git ls-files
|
|
68
|
+
* --others`' stderr through the listing's `unreadable` policy (`degrade`).
|
|
69
|
+
*
|
|
70
|
+
* 🚨 Beneath one of these the active set has no opinion. Its rule is "absent
|
|
71
|
+
* from the set and present on disk ⇒ ignored", and that rule is sound only
|
|
72
|
+
* where the listing LOOKED: an untracked, non-ignored file under a `--x`
|
|
73
|
+
* directory is absent because git could not read the directory, not because
|
|
74
|
+
* a pattern excludes it, and `git check-ignore` — which answers from the
|
|
75
|
+
* patterns and needs no listing — says so. Before this was recorded the
|
|
76
|
+
* tracker called such a file gitignored, silently, and every consumer of
|
|
77
|
+
* that answer (the leak judge, the link-graph walker, audit's distributed
|
|
78
|
+
* lane) was one refused `opendir` from a wrong verdict.
|
|
79
|
+
*
|
|
80
|
+
* The directory ITSELF is non-ignored by construction — git opened it
|
|
81
|
+
* precisely because no pattern excluded it — so it and its ancestors are
|
|
82
|
+
* entered into {@link activeAncestors}: a walker must not prune what the
|
|
83
|
+
* listing could not see into.
|
|
84
|
+
*/
|
|
85
|
+
private readonly unlistableDirectories;
|
|
59
86
|
private initialized;
|
|
60
87
|
private activeSetPopulated;
|
|
61
88
|
/** Whether `git ls-files` actually answered during {@link initialize}. */
|
|
@@ -72,8 +99,60 @@ export declare class GitTracker {
|
|
|
72
99
|
* With `includeUntracked: false`, only tracked files are pre-populated.
|
|
73
100
|
* Untracked non-ignored files will miss the cache and fall through to
|
|
74
101
|
* `git check-ignore` via {@link isIgnored}.
|
|
102
|
+
*
|
|
103
|
+
* ## This is the one bracket in the crawl-timing seam's `shared` stratum
|
|
104
|
+
*
|
|
105
|
+
* The `git ls-files` spawn below is preparation BOTH crawlers consume and
|
|
106
|
+
* NEITHER owns — the incumbent link walk and the projection's contributors are
|
|
107
|
+
* each handed a tracker by their caller — so it is charged to
|
|
108
|
+
* {@link CRAWL_SHARED_GIT_TRACKER_ID}, in a stratum belonging to no arm.
|
|
109
|
+
*
|
|
110
|
+
* The bracket is here, inside the class, and not at the six sites that build a
|
|
111
|
+
* tracker, for the reason `crawl-timing.ts` gives about `ResourceRegistry`: six
|
|
112
|
+
* copies are six chances to disagree, and a seventh site added later would
|
|
113
|
+
* silently go uncharged. Here, every caller is covered by construction — which
|
|
114
|
+
* includes `@vibe-agent-toolkit/discovery`, a package that could not have filed
|
|
115
|
+
* a row from its own call site at all, since it depends on `utils` alone.
|
|
116
|
+
*
|
|
117
|
+
* The early return above is deliberately OUTSIDE it: a re-entrant call does no
|
|
118
|
+
* work, and charging it would inflate `calls` with questions rather than
|
|
119
|
+
* spawns.
|
|
75
120
|
*/
|
|
76
121
|
initialize(options?: GitTrackerInitOptions): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* The active set read off a snapshot this process has ALREADY taken — or
|
|
124
|
+
* `null` to say "ask git yourself".
|
|
125
|
+
*
|
|
126
|
+
* ## Why this is the same question, not a similar one
|
|
127
|
+
*
|
|
128
|
+
* `git ls-files --cached --others --exclude-standard` returns
|
|
129
|
+
* `tracked ∪ (untracked ∧ ¬ignored)`. A snapshot is `git add --all` **without**
|
|
130
|
+
* `--force` staged into a throwaway index, whose membership is that same set
|
|
131
|
+
* by construction — the exclusion of ignored paths is `--force`'s absence in
|
|
132
|
+
* both cases. So where a snapshot is already in hand, the spawn this method
|
|
133
|
+
* replaces would re-derive a set the process is already holding.
|
|
134
|
+
*
|
|
135
|
+
* It is deliberately a PEEK ({@link peekGitTreeSnapshot}) and never a take: a
|
|
136
|
+
* snapshot costs a `git add --all`, so causing one to avoid an `ls-files`
|
|
137
|
+
* would be a large loss dressed as a saving. On the incumbent walk no snapshot
|
|
138
|
+
* is ever taken, the peek misses, and this tracker spawns exactly as it always
|
|
139
|
+
* has.
|
|
140
|
+
*
|
|
141
|
+
* ## The one divergence, and why it is an improvement
|
|
142
|
+
*
|
|
143
|
+
* `--cached` reads the REAL index, so a tracked file deleted from the working
|
|
144
|
+
* tree is still listed. `git add --all` stages that deletion, so the snapshot
|
|
145
|
+
* omits it. The set is therefore not identical — it is the same set minus
|
|
146
|
+
* paths that do not exist, which is what {@link isIgnoredByActiveSet} already
|
|
147
|
+
* documents the active set to be ("it can only ever contain paths that
|
|
148
|
+
* EXIST"). The snapshot-sourced set honours that sentence more exactly than
|
|
149
|
+
* the spawn does.
|
|
150
|
+
*
|
|
151
|
+
* @param includeUntracked - The caller's requested membership
|
|
152
|
+
* @returns Root-relative paths in `git ls-files` shape, or `null` when no
|
|
153
|
+
* snapshot is available or the request is one a snapshot cannot answer
|
|
154
|
+
*/
|
|
155
|
+
private activePathsFromOpenSnapshot;
|
|
77
156
|
/**
|
|
78
157
|
* Did git actually answer, or is this tracker an empty shell?
|
|
79
158
|
*
|
|
@@ -98,6 +177,22 @@ export declare class GitTracker {
|
|
|
98
177
|
* `hasActiveDescendant` / `isIgnoredByActiveSet` ancestor lookup misses on Windows.
|
|
99
178
|
*/
|
|
100
179
|
private populateAncestorSet;
|
|
180
|
+
/**
|
|
181
|
+
* Record `start` and every directory above it up to (not including) the
|
|
182
|
+
* project root as active ancestors, stopping at the first one already known
|
|
183
|
+
* — everything above it necessarily is too.
|
|
184
|
+
*
|
|
185
|
+
* @param start - Forward-slashed absolute directory to begin at
|
|
186
|
+
*/
|
|
187
|
+
private recordAncestorsFrom;
|
|
188
|
+
/**
|
|
189
|
+
* Whether the active set can say anything about a path: it cannot beneath a
|
|
190
|
+
* directory the listing was refused — see {@link unlistableDirectories}.
|
|
191
|
+
*
|
|
192
|
+
* @param normalizedAbsolutePath - Forward-slashed absolute path
|
|
193
|
+
* @returns True when the path lies strictly beneath a refused directory
|
|
194
|
+
*/
|
|
195
|
+
private beneathUnlistable;
|
|
101
196
|
/**
|
|
102
197
|
* Returns true if the given absolute path IS an active-set file OR is an
|
|
103
198
|
* ancestor directory of at least one active-set file.
|
|
@@ -117,7 +212,9 @@ export declare class GitTracker {
|
|
|
117
212
|
* For paths INSIDE the project root **that exist on disk**, membership in the
|
|
118
213
|
* active set is authoritative: such a path is ignored iff it is not in the
|
|
119
214
|
* active set AND not an ancestor of any active-set path. No `git
|
|
120
|
-
* check-ignore` spawn.
|
|
215
|
+
* check-ignore` spawn. The one exception is a path beneath a directory the
|
|
216
|
+
* listing could not OPEN — see {@link unlistableDirectories} — where absence
|
|
217
|
+
* says nothing and the question goes to `git check-ignore`.
|
|
121
218
|
*
|
|
122
219
|
* The existence qualifier is load-bearing, not a caveat. The active set is
|
|
123
220
|
* built from `git ls-files`, so it can only ever contain paths that EXIST — a
|
|
@@ -131,7 +228,38 @@ export declare class GitTracker {
|
|
|
131
228
|
* so a repeated miss on the same path stays O(1).
|
|
132
229
|
*
|
|
133
230
|
* For paths OUTSIDE the project root, falls back to {@link isIgnored} so
|
|
134
|
-
* legacy behavior is preserved.
|
|
231
|
+
* legacy behavior is preserved. That fallback is the expensive one, and its
|
|
232
|
+
* cost does not resemble the in-root cost at all.
|
|
233
|
+
*
|
|
234
|
+
* ## ⚠️ The out-of-root fallback costs an order of magnitude more per path
|
|
235
|
+
*
|
|
236
|
+
* Measured in the D9 parity fixture: an out-of-root path costs
|
|
237
|
+
* **185–427 ms**, against **12–28 ms** for every in-repo path — ≈7–36× on
|
|
238
|
+
* those ranges.
|
|
239
|
+
*
|
|
240
|
+
* The mechanism is the delegation above plus what git does with it.
|
|
241
|
+
* {@link isIgnored} runs `git check-ignore` with the PROJECT ROOT as cwd; git
|
|
242
|
+
* exits 128 for a path outside that repository; and {@link isGitIgnored}'s
|
|
243
|
+
* exit-128 recovery walk then spawns `check-ignore` ONCE PER ANCESTOR up to
|
|
244
|
+
* `/`. No ancestor of an out-of-root path is ever the project root that would
|
|
245
|
+
* stop the walk, and every one of those ancestors is outside the repository
|
|
246
|
+
* too — so each also exits 128 and the walk never breaks early either. The
|
|
247
|
+
* verdict is cached, so the price is paid once per DISTINCT out-of-root path
|
|
248
|
+
* rather than once per call.
|
|
249
|
+
*
|
|
250
|
+
* The consequence for a caller choosing between the two oracles: switching
|
|
251
|
+
* from {@link isIgnored} to this method removes the spawn population **only
|
|
252
|
+
* for in-root paths**. A lane feeding out-of-root paths in bulk keeps the
|
|
253
|
+
* per-path spawn in full. So a comment asserting that the out-of-project case
|
|
254
|
+
* is rare, safe or cheap is asserting something this measurement contradicts:
|
|
255
|
+
* it is rare in some lanes, and it is never cheap.
|
|
256
|
+
*
|
|
257
|
+
* The exit-128 walk itself is documented in {@link isGitIgnored} — but only
|
|
258
|
+
* for the two cases already answered there: the SYMLINK case it exists to
|
|
259
|
+
* serve, and the NO-REPOSITORY case, which an early return now short-circuits
|
|
260
|
+
* before any spawn. The IN-REPO OUT-OF-ROOT path is the third case, it is
|
|
261
|
+
* documented nowhere there, and it is the one that still pays the walk in
|
|
262
|
+
* full. That is why the number lives here.
|
|
135
263
|
*
|
|
136
264
|
* Requires {@link initialize} with `includeUntracked: true` (the default).
|
|
137
265
|
* When initialized without untracked files, this method delegates to
|
|
@@ -139,8 +267,21 @@ export declare class GitTracker {
|
|
|
139
267
|
* possible per-path spawn.
|
|
140
268
|
*
|
|
141
269
|
* @param absolutePath - Absolute path to check
|
|
270
|
+
* @param knownToExist - The caller's own answer to the existence question, when
|
|
271
|
+
* it has already asked. Supplying it skips this method's `existsSync`, which
|
|
272
|
+
* is otherwise paid once per path that is absent from the active set — i.e.
|
|
273
|
+
* once per ignored path, and the projection's `filesystem` extent enumerates
|
|
274
|
+
* all of them (11,108 calls on an 8,496-path adopter tree).
|
|
275
|
+
*
|
|
276
|
+
* **It must mean what `existsSync` means: `stat` succeeds, following
|
|
277
|
+
* symlinks.** A caller holding only an `lstat` result has a DIFFERENT fact —
|
|
278
|
+
* `lstat` succeeds on a dangling symlink where `existsSync` returns false —
|
|
279
|
+
* and must narrow it to `exists && symlinkResolves !== false` rather than
|
|
280
|
+
* pass the `lstat` boolean through, or dangling symlinks silently stop
|
|
281
|
+
* falling back to `git check-ignore` and start reporting as ignored.
|
|
282
|
+
* Omit it and nothing changes.
|
|
142
283
|
*/
|
|
143
|
-
isIgnoredByActiveSet(absolutePath: string): boolean;
|
|
284
|
+
isIgnoredByActiveSet(absolutePath: string, knownToExist?: boolean): boolean;
|
|
144
285
|
private isWithinProjectRoot;
|
|
145
286
|
/**
|
|
146
287
|
* Check if a file is ignored by git.
|
|
@@ -151,6 +292,28 @@ export declare class GitTracker {
|
|
|
151
292
|
* @returns true if file is gitignored, false otherwise
|
|
152
293
|
*/
|
|
153
294
|
isIgnored(filePath: string): boolean;
|
|
295
|
+
/**
|
|
296
|
+
* The spelling git records for a path, or `null` if git has no record of it.
|
|
297
|
+
*
|
|
298
|
+
* This is the casing oracle, not another ignore check. On a case-insensitive
|
|
299
|
+
* filesystem `docs/Readme.md` and `docs/README.md` are one inode with two
|
|
300
|
+
* spellings, and Node's two `realpath` implementations disagree about which
|
|
301
|
+
* one they hand back — so anything that derives an identity from a path needs
|
|
302
|
+
* a single authoritative spelling, and git's is it wherever git has one.
|
|
303
|
+
*
|
|
304
|
+
* The lookup key is lowercased, which is the point: the caller asks with
|
|
305
|
+
* whatever casing it observed and gets back the casing git holds.
|
|
306
|
+
*
|
|
307
|
+
* Answers only from the pre-populated set — never spawns. A path git does not
|
|
308
|
+
* know (untracked-and-ignored, non-existent, outside the project root, or any
|
|
309
|
+
* path at all when `git ls-files` did not answer) returns `null`, and the
|
|
310
|
+
* caller falls back to the on-disk casing.
|
|
311
|
+
*
|
|
312
|
+
* @param absolutePath - Absolute path to look up
|
|
313
|
+
* @returns Root-relative, forward-slashed path as git spells it — relative to
|
|
314
|
+
* THIS tracker's project root — or `null` when git has no record of it
|
|
315
|
+
*/
|
|
316
|
+
indexPathFor(absolutePath: string): string | null;
|
|
154
317
|
/**
|
|
155
318
|
* Get cache statistics.
|
|
156
319
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-tracker.d.ts","sourceRoot":"","sources":["../src/git-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"git-tracker.d.ts","sourceRoot":"","sources":["../src/git-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmC;IACzD,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,oFAAoF;IACpF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAgB;IACtD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,kBAAkB,CAAS;IACnC,0EAA0E;IAC1E,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM;IAK/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,UAAU,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ChE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,OAAO,CAAC,2BAA2B;IA6BnC;;;;;;;;;;;;;OAaG;IACH,QAAQ,IAAI,OAAO;IAInB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;;;;;;;;;;OAWG;IACH,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAUlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0EG;IACH,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO;IAiC3E,OAAO,CAAC,mBAAmB;IAQ3B;;;;;;;OAOG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAqBpC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIjD;;OAEG;IACH,QAAQ,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE;IAQrF;;OAEG;IACH,KAAK,IAAI,IAAI;CAUd"}
|
package/dist/git-tracker.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
8
|
import { dirname } from 'node:path';
|
|
9
|
+
import { CRAWL_SHARED_GIT_TRACKER_ID, crawlTimingStart, recordSharedPass, } from './crawl-timing.js';
|
|
10
|
+
import { peekGitTreeSnapshot } from './git-snapshot.js';
|
|
9
11
|
import { gitLsFiles, isGitIgnored } from './git-utils.js';
|
|
10
12
|
import { safePath, toForwardSlash } from './path-utils.js';
|
|
11
13
|
/**
|
|
@@ -42,6 +44,33 @@ export class GitTracker {
|
|
|
42
44
|
activeSet = new Set();
|
|
43
45
|
/** Absolute paths of every directory that contains at least one active-set file. */
|
|
44
46
|
activeAncestors = new Set();
|
|
47
|
+
/**
|
|
48
|
+
* Lowercased absolute path → the root-relative path spelled the way git
|
|
49
|
+
* spelled it. Built from the very same `git ls-files` output `activeSet`
|
|
50
|
+
* comes from, so it costs one extra `Map` and no extra git invocation.
|
|
51
|
+
*/
|
|
52
|
+
indexPaths = new Map();
|
|
53
|
+
/**
|
|
54
|
+
* Absolute, forward-slashed directories the active-set listing could not
|
|
55
|
+
* OPEN, so nothing beneath them is in the set — read off `git ls-files
|
|
56
|
+
* --others`' stderr through the listing's `unreadable` policy (`degrade`).
|
|
57
|
+
*
|
|
58
|
+
* 🚨 Beneath one of these the active set has no opinion. Its rule is "absent
|
|
59
|
+
* from the set and present on disk ⇒ ignored", and that rule is sound only
|
|
60
|
+
* where the listing LOOKED: an untracked, non-ignored file under a `--x`
|
|
61
|
+
* directory is absent because git could not read the directory, not because
|
|
62
|
+
* a pattern excludes it, and `git check-ignore` — which answers from the
|
|
63
|
+
* patterns and needs no listing — says so. Before this was recorded the
|
|
64
|
+
* tracker called such a file gitignored, silently, and every consumer of
|
|
65
|
+
* that answer (the leak judge, the link-graph walker, audit's distributed
|
|
66
|
+
* lane) was one refused `opendir` from a wrong verdict.
|
|
67
|
+
*
|
|
68
|
+
* The directory ITSELF is non-ignored by construction — git opened it
|
|
69
|
+
* precisely because no pattern excluded it — so it and its ancestors are
|
|
70
|
+
* entered into {@link activeAncestors}: a walker must not prune what the
|
|
71
|
+
* listing could not see into.
|
|
72
|
+
*/
|
|
73
|
+
unlistableDirectories = [];
|
|
45
74
|
initialized = false;
|
|
46
75
|
activeSetPopulated = false;
|
|
47
76
|
/** Whether `git ls-files` actually answered during {@link initialize}. */
|
|
@@ -61,27 +90,121 @@ export class GitTracker {
|
|
|
61
90
|
* With `includeUntracked: false`, only tracked files are pre-populated.
|
|
62
91
|
* Untracked non-ignored files will miss the cache and fall through to
|
|
63
92
|
* `git check-ignore` via {@link isIgnored}.
|
|
93
|
+
*
|
|
94
|
+
* ## This is the one bracket in the crawl-timing seam's `shared` stratum
|
|
95
|
+
*
|
|
96
|
+
* The `git ls-files` spawn below is preparation BOTH crawlers consume and
|
|
97
|
+
* NEITHER owns — the incumbent link walk and the projection's contributors are
|
|
98
|
+
* each handed a tracker by their caller — so it is charged to
|
|
99
|
+
* {@link CRAWL_SHARED_GIT_TRACKER_ID}, in a stratum belonging to no arm.
|
|
100
|
+
*
|
|
101
|
+
* The bracket is here, inside the class, and not at the six sites that build a
|
|
102
|
+
* tracker, for the reason `crawl-timing.ts` gives about `ResourceRegistry`: six
|
|
103
|
+
* copies are six chances to disagree, and a seventh site added later would
|
|
104
|
+
* silently go uncharged. Here, every caller is covered by construction — which
|
|
105
|
+
* includes `@vibe-agent-toolkit/discovery`, a package that could not have filed
|
|
106
|
+
* a row from its own call site at all, since it depends on `utils` alone.
|
|
107
|
+
*
|
|
108
|
+
* The early return above is deliberately OUTSIDE it: a re-entrant call does no
|
|
109
|
+
* work, and charging it would inflate `calls` with questions rather than
|
|
110
|
+
* spawns.
|
|
64
111
|
*/
|
|
65
112
|
async initialize(options) {
|
|
66
113
|
if (this.initialized) {
|
|
67
114
|
return;
|
|
68
115
|
}
|
|
116
|
+
const startedAt = crawlTimingStart();
|
|
69
117
|
const includeUntracked = options?.includeUntracked ?? true;
|
|
70
|
-
const files = gitLsFiles({
|
|
118
|
+
const files = this.activePathsFromOpenSnapshot(includeUntracked) ?? gitLsFiles({
|
|
71
119
|
cwd: this.projectRoot,
|
|
72
120
|
...(includeUntracked ? { includeUntracked: true } : {}),
|
|
121
|
+
// Degrade, and RECORD: a directory git could not open is a shorter list
|
|
122
|
+
// nothing can tell from a complete one — see {@link unlistableDirectories}
|
|
123
|
+
// for how the tracker keeps that gap honest. Only the untracked listing
|
|
124
|
+
// walks the tree, so the tracked-only one never calls this.
|
|
125
|
+
unreadable: {
|
|
126
|
+
degrade: (refusal) => {
|
|
127
|
+
this.unlistableDirectories.push(refusal.directory);
|
|
128
|
+
},
|
|
129
|
+
},
|
|
73
130
|
});
|
|
74
131
|
if (files !== null) {
|
|
75
132
|
for (const relativePath of files) {
|
|
76
133
|
const absolutePath = safePath.resolve(this.projectRoot, relativePath);
|
|
77
134
|
this.cache.set(absolutePath, false); // false = not ignored
|
|
78
135
|
this.activeSet.add(absolutePath);
|
|
136
|
+
this.indexPaths.set(absolutePath.toLowerCase(), toForwardSlash(relativePath));
|
|
79
137
|
}
|
|
80
138
|
this.populateAncestorSet();
|
|
81
139
|
}
|
|
82
140
|
this.gitAnswered = files !== null;
|
|
83
141
|
this.activeSetPopulated = includeUntracked && files !== null;
|
|
84
142
|
this.initialized = true;
|
|
143
|
+
// After the state above is settled, so a throw from the seam could never
|
|
144
|
+
// leave a half-initialized tracker; and charged even when git did not answer,
|
|
145
|
+
// because a failed `git ls-files` still spawned a process and still cost the
|
|
146
|
+
// command the time it took to fail.
|
|
147
|
+
recordSharedPass(CRAWL_SHARED_GIT_TRACKER_ID, startedAt);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The active set read off a snapshot this process has ALREADY taken — or
|
|
151
|
+
* `null` to say "ask git yourself".
|
|
152
|
+
*
|
|
153
|
+
* ## Why this is the same question, not a similar one
|
|
154
|
+
*
|
|
155
|
+
* `git ls-files --cached --others --exclude-standard` returns
|
|
156
|
+
* `tracked ∪ (untracked ∧ ¬ignored)`. A snapshot is `git add --all` **without**
|
|
157
|
+
* `--force` staged into a throwaway index, whose membership is that same set
|
|
158
|
+
* by construction — the exclusion of ignored paths is `--force`'s absence in
|
|
159
|
+
* both cases. So where a snapshot is already in hand, the spawn this method
|
|
160
|
+
* replaces would re-derive a set the process is already holding.
|
|
161
|
+
*
|
|
162
|
+
* It is deliberately a PEEK ({@link peekGitTreeSnapshot}) and never a take: a
|
|
163
|
+
* snapshot costs a `git add --all`, so causing one to avoid an `ls-files`
|
|
164
|
+
* would be a large loss dressed as a saving. On the incumbent walk no snapshot
|
|
165
|
+
* is ever taken, the peek misses, and this tracker spawns exactly as it always
|
|
166
|
+
* has.
|
|
167
|
+
*
|
|
168
|
+
* ## The one divergence, and why it is an improvement
|
|
169
|
+
*
|
|
170
|
+
* `--cached` reads the REAL index, so a tracked file deleted from the working
|
|
171
|
+
* tree is still listed. `git add --all` stages that deletion, so the snapshot
|
|
172
|
+
* omits it. The set is therefore not identical — it is the same set minus
|
|
173
|
+
* paths that do not exist, which is what {@link isIgnoredByActiveSet} already
|
|
174
|
+
* documents the active set to be ("it can only ever contain paths that
|
|
175
|
+
* EXIST"). The snapshot-sourced set honours that sentence more exactly than
|
|
176
|
+
* the spawn does.
|
|
177
|
+
*
|
|
178
|
+
* @param includeUntracked - The caller's requested membership
|
|
179
|
+
* @returns Root-relative paths in `git ls-files` shape, or `null` when no
|
|
180
|
+
* snapshot is available or the request is one a snapshot cannot answer
|
|
181
|
+
*/
|
|
182
|
+
activePathsFromOpenSnapshot(includeUntracked) {
|
|
183
|
+
// A snapshot cannot express the tracked-ONLY set: `add --all` stages
|
|
184
|
+
// untracked-not-ignored files too, and nothing in the result marks which
|
|
185
|
+
// entries were already tracked. Declining is the only correct answer.
|
|
186
|
+
if (!includeUntracked)
|
|
187
|
+
return null;
|
|
188
|
+
const snapshot = peekGitTreeSnapshot(this.projectRoot);
|
|
189
|
+
if (snapshot === undefined)
|
|
190
|
+
return null;
|
|
191
|
+
const relativePaths = [];
|
|
192
|
+
for (const entry of snapshot.entries) {
|
|
193
|
+
// A snapshot covers the whole REPOSITORY, which may be an ancestor of this
|
|
194
|
+
// tracker's root — `git ls-files` run at that root scopes its listing to
|
|
195
|
+
// it, so anything above is not this tracker's business and must not enter
|
|
196
|
+
// the active set. Everything below is kept verbatim, symlinks and
|
|
197
|
+
// submodule gitlinks included, because `--cached` lists those too.
|
|
198
|
+
const relativePath = toForwardSlash(safePath.relative(this.normalizedProjectRoot, entry.absolutePath));
|
|
199
|
+
// A leading `..` SEGMENT, not a `../` prefix: compared as a path segment,
|
|
200
|
+
// a file legitimately named `..hidden` is kept rather than silently
|
|
201
|
+
// dropped as if it were an escape.
|
|
202
|
+
if (relativePath.length === 0 || relativePath.split('/')[0] === '..') {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
relativePaths.push(relativePath);
|
|
206
|
+
}
|
|
207
|
+
return relativePaths;
|
|
85
208
|
}
|
|
86
209
|
/**
|
|
87
210
|
* Did git actually answer, or is this tracker an empty shell?
|
|
@@ -109,24 +232,49 @@ export class GitTracker {
|
|
|
109
232
|
* `hasActiveDescendant` / `isIgnoredByActiveSet` ancestor lookup misses on Windows.
|
|
110
233
|
*/
|
|
111
234
|
populateAncestorSet() {
|
|
112
|
-
const root = this.normalizedProjectRoot;
|
|
113
235
|
for (const absolutePath of this.activeSet) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const parent = toForwardSlash(dirname(current));
|
|
122
|
-
if (parent === current) {
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
current = parent;
|
|
126
|
-
}
|
|
236
|
+
this.recordAncestorsFrom(toForwardSlash(dirname(absolutePath)));
|
|
237
|
+
}
|
|
238
|
+
// A directory the listing could not open is itself non-ignored (git opened
|
|
239
|
+
// it because no pattern excluded it) and may hold active files nobody could
|
|
240
|
+
// list — so it counts as an ancestor from ITSELF down, not from its parent.
|
|
241
|
+
for (const directory of this.unlistableDirectories) {
|
|
242
|
+
this.recordAncestorsFrom(directory);
|
|
127
243
|
}
|
|
128
244
|
// projectRoot itself is always an implicit ancestor of everything under it.
|
|
129
|
-
this.activeAncestors.add(
|
|
245
|
+
this.activeAncestors.add(this.normalizedProjectRoot);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Record `start` and every directory above it up to (not including) the
|
|
249
|
+
* project root as active ancestors, stopping at the first one already known
|
|
250
|
+
* — everything above it necessarily is too.
|
|
251
|
+
*
|
|
252
|
+
* @param start - Forward-slashed absolute directory to begin at
|
|
253
|
+
*/
|
|
254
|
+
recordAncestorsFrom(start) {
|
|
255
|
+
const root = this.normalizedProjectRoot;
|
|
256
|
+
let current = start;
|
|
257
|
+
while (current !== root && current.length > root.length) {
|
|
258
|
+
if (this.activeAncestors.has(current)) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
this.activeAncestors.add(current);
|
|
262
|
+
const parent = toForwardSlash(dirname(current));
|
|
263
|
+
if (parent === current) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
current = parent;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Whether the active set can say anything about a path: it cannot beneath a
|
|
271
|
+
* directory the listing was refused — see {@link unlistableDirectories}.
|
|
272
|
+
*
|
|
273
|
+
* @param normalizedAbsolutePath - Forward-slashed absolute path
|
|
274
|
+
* @returns True when the path lies strictly beneath a refused directory
|
|
275
|
+
*/
|
|
276
|
+
beneathUnlistable(normalizedAbsolutePath) {
|
|
277
|
+
return this.unlistableDirectories.some((directory) => normalizedAbsolutePath.startsWith(`${directory}/`));
|
|
130
278
|
}
|
|
131
279
|
/**
|
|
132
280
|
* Returns true if the given absolute path IS an active-set file OR is an
|
|
@@ -145,7 +293,9 @@ export class GitTracker {
|
|
|
145
293
|
return true;
|
|
146
294
|
}
|
|
147
295
|
const normalized = safePath.resolve(absolutePath);
|
|
148
|
-
|
|
296
|
+
// Beneath a refused directory nothing was listed, so "no active descendant"
|
|
297
|
+
// would be a claim the listing never earned.
|
|
298
|
+
return this.activeSet.has(normalized) || this.activeAncestors.has(normalized) || this.beneathUnlistable(normalized);
|
|
149
299
|
}
|
|
150
300
|
/**
|
|
151
301
|
* Fast O(1) ignore check against the pre-populated active set.
|
|
@@ -153,7 +303,9 @@ export class GitTracker {
|
|
|
153
303
|
* For paths INSIDE the project root **that exist on disk**, membership in the
|
|
154
304
|
* active set is authoritative: such a path is ignored iff it is not in the
|
|
155
305
|
* active set AND not an ancestor of any active-set path. No `git
|
|
156
|
-
* check-ignore` spawn.
|
|
306
|
+
* check-ignore` spawn. The one exception is a path beneath a directory the
|
|
307
|
+
* listing could not OPEN — see {@link unlistableDirectories} — where absence
|
|
308
|
+
* says nothing and the question goes to `git check-ignore`.
|
|
157
309
|
*
|
|
158
310
|
* The existence qualifier is load-bearing, not a caveat. The active set is
|
|
159
311
|
* built from `git ls-files`, so it can only ever contain paths that EXIST — a
|
|
@@ -167,7 +319,38 @@ export class GitTracker {
|
|
|
167
319
|
* so a repeated miss on the same path stays O(1).
|
|
168
320
|
*
|
|
169
321
|
* For paths OUTSIDE the project root, falls back to {@link isIgnored} so
|
|
170
|
-
* legacy behavior is preserved.
|
|
322
|
+
* legacy behavior is preserved. That fallback is the expensive one, and its
|
|
323
|
+
* cost does not resemble the in-root cost at all.
|
|
324
|
+
*
|
|
325
|
+
* ## ⚠️ The out-of-root fallback costs an order of magnitude more per path
|
|
326
|
+
*
|
|
327
|
+
* Measured in the D9 parity fixture: an out-of-root path costs
|
|
328
|
+
* **185–427 ms**, against **12–28 ms** for every in-repo path — ≈7–36× on
|
|
329
|
+
* those ranges.
|
|
330
|
+
*
|
|
331
|
+
* The mechanism is the delegation above plus what git does with it.
|
|
332
|
+
* {@link isIgnored} runs `git check-ignore` with the PROJECT ROOT as cwd; git
|
|
333
|
+
* exits 128 for a path outside that repository; and {@link isGitIgnored}'s
|
|
334
|
+
* exit-128 recovery walk then spawns `check-ignore` ONCE PER ANCESTOR up to
|
|
335
|
+
* `/`. No ancestor of an out-of-root path is ever the project root that would
|
|
336
|
+
* stop the walk, and every one of those ancestors is outside the repository
|
|
337
|
+
* too — so each also exits 128 and the walk never breaks early either. The
|
|
338
|
+
* verdict is cached, so the price is paid once per DISTINCT out-of-root path
|
|
339
|
+
* rather than once per call.
|
|
340
|
+
*
|
|
341
|
+
* The consequence for a caller choosing between the two oracles: switching
|
|
342
|
+
* from {@link isIgnored} to this method removes the spawn population **only
|
|
343
|
+
* for in-root paths**. A lane feeding out-of-root paths in bulk keeps the
|
|
344
|
+
* per-path spawn in full. So a comment asserting that the out-of-project case
|
|
345
|
+
* is rare, safe or cheap is asserting something this measurement contradicts:
|
|
346
|
+
* it is rare in some lanes, and it is never cheap.
|
|
347
|
+
*
|
|
348
|
+
* The exit-128 walk itself is documented in {@link isGitIgnored} — but only
|
|
349
|
+
* for the two cases already answered there: the SYMLINK case it exists to
|
|
350
|
+
* serve, and the NO-REPOSITORY case, which an early return now short-circuits
|
|
351
|
+
* before any spawn. The IN-REPO OUT-OF-ROOT path is the third case, it is
|
|
352
|
+
* documented nowhere there, and it is the one that still pays the walk in
|
|
353
|
+
* full. That is why the number lives here.
|
|
171
354
|
*
|
|
172
355
|
* Requires {@link initialize} with `includeUntracked: true` (the default).
|
|
173
356
|
* When initialized without untracked files, this method delegates to
|
|
@@ -175,8 +358,21 @@ export class GitTracker {
|
|
|
175
358
|
* possible per-path spawn.
|
|
176
359
|
*
|
|
177
360
|
* @param absolutePath - Absolute path to check
|
|
361
|
+
* @param knownToExist - The caller's own answer to the existence question, when
|
|
362
|
+
* it has already asked. Supplying it skips this method's `existsSync`, which
|
|
363
|
+
* is otherwise paid once per path that is absent from the active set — i.e.
|
|
364
|
+
* once per ignored path, and the projection's `filesystem` extent enumerates
|
|
365
|
+
* all of them (11,108 calls on an 8,496-path adopter tree).
|
|
366
|
+
*
|
|
367
|
+
* **It must mean what `existsSync` means: `stat` succeeds, following
|
|
368
|
+
* symlinks.** A caller holding only an `lstat` result has a DIFFERENT fact —
|
|
369
|
+
* `lstat` succeeds on a dangling symlink where `existsSync` returns false —
|
|
370
|
+
* and must narrow it to `exists && symlinkResolves !== false` rather than
|
|
371
|
+
* pass the `lstat` boolean through, or dangling symlinks silently stop
|
|
372
|
+
* falling back to `git check-ignore` and start reporting as ignored.
|
|
373
|
+
* Omit it and nothing changes.
|
|
178
374
|
*/
|
|
179
|
-
isIgnoredByActiveSet(absolutePath) {
|
|
375
|
+
isIgnoredByActiveSet(absolutePath, knownToExist) {
|
|
180
376
|
if (!this.activeSetPopulated) {
|
|
181
377
|
return this.isIgnored(absolutePath);
|
|
182
378
|
}
|
|
@@ -188,10 +384,16 @@ export class GitTracker {
|
|
|
188
384
|
if (this.activeSet.has(normalized) || this.activeAncestors.has(normalized)) {
|
|
189
385
|
return false;
|
|
190
386
|
}
|
|
387
|
+
// Absent from the set beneath a directory the listing could not open: the
|
|
388
|
+
// set never looked there, so absence is not evidence — see
|
|
389
|
+
// {@link unlistableDirectories}. Only `git check-ignore` can answer.
|
|
390
|
+
if (this.beneathUnlistable(normalized)) {
|
|
391
|
+
return this.isIgnored(absolutePath);
|
|
392
|
+
}
|
|
191
393
|
// Absent from the active set. That means "ignored" only for a path that is
|
|
192
394
|
// actually there; otherwise the set has no opinion and git must be asked.
|
|
193
|
-
|
|
194
|
-
if (!
|
|
395
|
+
const present = knownToExist ?? existsSync(normalized);
|
|
396
|
+
if (!present) {
|
|
195
397
|
return this.isIgnored(absolutePath);
|
|
196
398
|
}
|
|
197
399
|
return true;
|
|
@@ -228,6 +430,30 @@ export class GitTracker {
|
|
|
228
430
|
this.cache.set(cacheKey, ignored);
|
|
229
431
|
return ignored;
|
|
230
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* The spelling git records for a path, or `null` if git has no record of it.
|
|
435
|
+
*
|
|
436
|
+
* This is the casing oracle, not another ignore check. On a case-insensitive
|
|
437
|
+
* filesystem `docs/Readme.md` and `docs/README.md` are one inode with two
|
|
438
|
+
* spellings, and Node's two `realpath` implementations disagree about which
|
|
439
|
+
* one they hand back — so anything that derives an identity from a path needs
|
|
440
|
+
* a single authoritative spelling, and git's is it wherever git has one.
|
|
441
|
+
*
|
|
442
|
+
* The lookup key is lowercased, which is the point: the caller asks with
|
|
443
|
+
* whatever casing it observed and gets back the casing git holds.
|
|
444
|
+
*
|
|
445
|
+
* Answers only from the pre-populated set — never spawns. A path git does not
|
|
446
|
+
* know (untracked-and-ignored, non-existent, outside the project root, or any
|
|
447
|
+
* path at all when `git ls-files` did not answer) returns `null`, and the
|
|
448
|
+
* caller falls back to the on-disk casing.
|
|
449
|
+
*
|
|
450
|
+
* @param absolutePath - Absolute path to look up
|
|
451
|
+
* @returns Root-relative, forward-slashed path as git spells it — relative to
|
|
452
|
+
* THIS tracker's project root — or `null` when git has no record of it
|
|
453
|
+
*/
|
|
454
|
+
indexPathFor(absolutePath) {
|
|
455
|
+
return this.indexPaths.get(safePath.resolve(absolutePath).toLowerCase()) ?? null;
|
|
456
|
+
}
|
|
231
457
|
/**
|
|
232
458
|
* Get cache statistics.
|
|
233
459
|
*/
|
|
@@ -245,6 +471,8 @@ export class GitTracker {
|
|
|
245
471
|
this.cache.clear();
|
|
246
472
|
this.activeSet.clear();
|
|
247
473
|
this.activeAncestors.clear();
|
|
474
|
+
this.indexPaths.clear();
|
|
475
|
+
this.unlistableDirectories.length = 0;
|
|
248
476
|
this.initialized = false;
|
|
249
477
|
this.activeSetPopulated = false;
|
|
250
478
|
this.gitAnswered = false;
|
package/dist/git-tracker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-tracker.js","sourceRoot":"","sources":["../src/git-tracker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAqB3D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,UAAU;IACJ,WAAW,CAAS;IACpB,qBAAqB,CAAS;IAC9B,KAAK,GAAyB,IAAI,GAAG,EAAE,CAAC;IACzD,6FAA6F;IAC5E,SAAS,GAAgB,IAAI,GAAG,EAAE,CAAC;IACpD,oFAAoF;IACnE,eAAe,GAAgB,IAAI,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"git-tracker.js","sourceRoot":"","sources":["../src/git-tracker.ts"],"names":[],"mappings":"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"}
|