@smeltjs/core 0.2.0 → 0.4.0
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 +3 -3
- package/dist/agents/guide.d.ts +68 -0
- package/dist/agents/guide.d.ts.map +1 -0
- package/dist/agents/guide.js +72 -0
- package/dist/agents/guide.js.map +1 -0
- package/dist/agents/instructions.d.ts +133 -0
- package/dist/agents/instructions.d.ts.map +1 -0
- package/dist/agents/instructions.js +212 -0
- package/dist/agents/instructions.js.map +1 -0
- package/dist/agents/lint.d.ts +154 -0
- package/dist/agents/lint.d.ts.map +1 -0
- package/dist/agents/lint.js +761 -0
- package/dist/agents/lint.js.map +1 -0
- package/dist/agents/split.d.ts +113 -0
- package/dist/agents/split.d.ts.map +1 -0
- package/dist/agents/split.js +234 -0
- package/dist/agents/split.js.map +1 -0
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js.map +1 -1
- package/dist/cache/prefix.d.ts.map +1 -1
- package/dist/cache/prefix.js.map +1 -1
- package/dist/cli/agents.d.ts +39 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +121 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/args.d.ts +17 -70
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +18 -338
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/config.d.ts +60 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +89 -2
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/hooks.d.ts +55 -51
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +206 -658
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/init.d.ts +30 -15
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +167 -38
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/report.d.ts +23 -0
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +142 -4
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/run.d.ts +29 -90
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +34 -342
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +106 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +100 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/subcommands/agents.d.ts +72 -0
- package/dist/cli/subcommands/agents.d.ts.map +1 -0
- package/dist/cli/subcommands/agents.js +162 -0
- package/dist/cli/subcommands/agents.js.map +1 -0
- package/dist/cli/subcommands/flags.d.ts +144 -0
- package/dist/cli/subcommands/flags.d.ts.map +1 -0
- package/dist/cli/subcommands/flags.js +220 -0
- package/dist/cli/subcommands/flags.js.map +1 -0
- package/dist/cli/subcommands/hooks.d.ts +18 -0
- package/dist/cli/subcommands/hooks.d.ts.map +1 -0
- package/dist/cli/subcommands/hooks.js +80 -0
- package/dist/cli/subcommands/hooks.js.map +1 -0
- package/dist/cli/subcommands/init.d.ts +19 -0
- package/dist/cli/subcommands/init.d.ts.map +1 -0
- package/dist/cli/subcommands/init.js +44 -0
- package/dist/cli/subcommands/init.js.map +1 -0
- package/dist/cli/subcommands/map.d.ts +75 -0
- package/dist/cli/subcommands/map.d.ts.map +1 -0
- package/dist/cli/subcommands/map.js +132 -0
- package/dist/cli/subcommands/map.js.map +1 -0
- package/dist/cli/subcommands/registry.d.ts +81 -0
- package/dist/cli/subcommands/registry.d.ts.map +1 -0
- package/dist/cli/subcommands/registry.js +141 -0
- package/dist/cli/subcommands/registry.js.map +1 -0
- package/dist/cli/subcommands/retrieve.d.ts +74 -0
- package/dist/cli/subcommands/retrieve.d.ts.map +1 -0
- package/dist/cli/subcommands/retrieve.js +84 -0
- package/dist/cli/subcommands/retrieve.js.map +1 -0
- package/dist/cli/subcommands/smelt.d.ts +107 -0
- package/dist/cli/subcommands/smelt.d.ts.map +1 -0
- package/dist/cli/subcommands/smelt.js +304 -0
- package/dist/cli/subcommands/smelt.js.map +1 -0
- package/dist/cli/subcommands/stats.d.ts +43 -0
- package/dist/cli/subcommands/stats.d.ts.map +1 -0
- package/dist/cli/subcommands/stats.js +62 -0
- package/dist/cli/subcommands/stats.js.map +1 -0
- package/dist/cli/subcommands/subcommand.d.ts +113 -0
- package/dist/cli/subcommands/subcommand.d.ts.map +1 -0
- package/dist/cli/subcommands/subcommand.js +3 -0
- package/dist/cli/subcommands/subcommand.js.map +1 -0
- package/dist/cli/usage.d.ts +3 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +95 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/errors.d.ts +21 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -1
- package/dist/errors.js.map +1 -1
- package/dist/harness/aider.d.ts +10 -0
- package/dist/harness/aider.d.ts.map +1 -0
- package/dist/harness/aider.js +21 -0
- package/dist/harness/aider.js.map +1 -0
- package/dist/harness/claude-code.d.ts +3 -0
- package/dist/harness/claude-code.d.ts.map +1 -0
- package/dist/harness/claude-code.js +68 -0
- package/dist/harness/claude-code.js.map +1 -0
- package/dist/harness/cline.d.ts +3 -0
- package/dist/harness/cline.d.ts.map +1 -0
- package/dist/harness/cline.js +54 -0
- package/dist/harness/cline.js.map +1 -0
- package/dist/harness/codex.d.ts +3 -0
- package/dist/harness/codex.d.ts.map +1 -0
- package/dist/harness/codex.js +92 -0
- package/dist/harness/codex.js.map +1 -0
- package/dist/harness/cursor.d.ts +3 -0
- package/dist/harness/cursor.d.ts.map +1 -0
- package/dist/harness/cursor.js +54 -0
- package/dist/harness/cursor.js.map +1 -0
- package/dist/harness/gemini.d.ts +3 -0
- package/dist/harness/gemini.d.ts.map +1 -0
- package/dist/harness/gemini.js +60 -0
- package/dist/harness/gemini.js.map +1 -0
- package/dist/harness/grok.d.ts +3 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +47 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/hermes.d.ts +3 -0
- package/dist/harness/hermes.d.ts.map +1 -0
- package/dist/harness/hermes.js +72 -0
- package/dist/harness/hermes.js.map +1 -0
- package/dist/harness/kilocode.d.ts +3 -0
- package/dist/harness/kilocode.d.ts.map +1 -0
- package/dist/harness/kilocode.js +35 -0
- package/dist/harness/kilocode.js.map +1 -0
- package/dist/harness/opencode.d.ts +3 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/paths.d.ts +17 -0
- package/dist/harness/paths.d.ts.map +1 -0
- package/dist/harness/paths.js +45 -0
- package/dist/harness/paths.js.map +1 -0
- package/dist/harness/profile.d.ts +209 -0
- package/dist/harness/profile.d.ts.map +1 -0
- package/dist/harness/profile.js +41 -0
- package/dist/harness/profile.js.map +1 -0
- package/dist/harness/registry.d.ts +100 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +144 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/snippet.d.ts +19 -0
- package/dist/harness/snippet.d.ts.map +1 -0
- package/dist/harness/snippet.js +41 -0
- package/dist/harness/snippet.js.map +1 -0
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js.map +1 -1
- package/dist/hooks/guard-core.d.ts +14 -14
- package/dist/hooks/guard-core.d.ts.map +1 -1
- package/dist/hooks/guard-core.js +15 -37
- package/dist/hooks/guard-core.js.map +1 -1
- package/dist/hooks/shim.d.ts +95 -4
- package/dist/hooks/shim.d.ts.map +1 -1
- package/dist/hooks/shim.js +87 -2
- package/dist/hooks/shim.js.map +1 -1
- package/dist/hooks/shims/claude-code.d.ts +5 -18
- package/dist/hooks/shims/claude-code.d.ts.map +1 -1
- package/dist/hooks/shims/claude-code.js +9 -56
- package/dist/hooks/shims/claude-code.js.map +1 -1
- package/dist/hooks/shims/cline.d.ts +5 -12
- package/dist/hooks/shims/cline.d.ts.map +1 -1
- package/dist/hooks/shims/cline.js +9 -34
- package/dist/hooks/shims/cline.js.map +1 -1
- package/dist/hooks/shims/codex.d.ts +5 -18
- package/dist/hooks/shims/codex.d.ts.map +1 -1
- package/dist/hooks/shims/codex.js +9 -51
- package/dist/hooks/shims/codex.js.map +1 -1
- package/dist/hooks/shims/cursor.d.ts +5 -14
- package/dist/hooks/shims/cursor.d.ts.map +1 -1
- package/dist/hooks/shims/cursor.js +9 -42
- package/dist/hooks/shims/cursor.js.map +1 -1
- package/dist/hooks/shims/gemini.d.ts +5 -18
- package/dist/hooks/shims/gemini.d.ts.map +1 -1
- package/dist/hooks/shims/gemini.js +9 -48
- package/dist/hooks/shims/gemini.js.map +1 -1
- package/dist/hooks/shims/grok.d.ts +5 -13
- package/dist/hooks/shims/grok.d.ts.map +1 -1
- package/dist/hooks/shims/grok.js +9 -32
- package/dist/hooks/shims/grok.js.map +1 -1
- package/dist/hooks/shims/hermes.d.ts +5 -17
- package/dist/hooks/shims/hermes.d.ts.map +1 -1
- package/dist/hooks/shims/hermes.js +9 -45
- package/dist/hooks/shims/hermes.js.map +1 -1
- package/dist/index.d.ts +43 -76
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -67
- package/dist/index.js.map +1 -1
- package/dist/lang/bash.d.ts.map +1 -1
- package/dist/lang/bash.js.map +1 -1
- package/dist/lang/c.d.ts.map +1 -1
- package/dist/lang/c.js.map +1 -1
- package/dist/lang/c_sharp.d.ts.map +1 -1
- package/dist/lang/c_sharp.js.map +1 -1
- package/dist/lang/cpp.d.ts.map +1 -1
- package/dist/lang/cpp.js.map +1 -1
- package/dist/lang/go.d.ts.map +1 -1
- package/dist/lang/go.js.map +1 -1
- package/dist/lang/java.d.ts.map +1 -1
- package/dist/lang/java.js.map +1 -1
- package/dist/lang/javascript.d.ts.map +1 -1
- package/dist/lang/javascript.js.map +1 -1
- package/dist/lang/kotlin.d.ts.map +1 -1
- package/dist/lang/kotlin.js.map +1 -1
- package/dist/lang/php.d.ts.map +1 -1
- package/dist/lang/php.js.map +1 -1
- package/dist/lang/profile.d.ts.map +1 -1
- package/dist/lang/profile.js.map +1 -1
- package/dist/lang/python.d.ts.map +1 -1
- package/dist/lang/python.js.map +1 -1
- package/dist/lang/registry.d.ts.map +1 -1
- package/dist/lang/registry.js.map +1 -1
- package/dist/lang/ruby.d.ts.map +1 -1
- package/dist/lang/ruby.js.map +1 -1
- package/dist/lang/rust.d.ts.map +1 -1
- package/dist/lang/rust.js.map +1 -1
- package/dist/lang/swift.d.ts.map +1 -1
- package/dist/lang/swift.js.map +1 -1
- package/dist/lang/tsx.d.ts.map +1 -1
- package/dist/lang/tsx.js.map +1 -1
- package/dist/lang/typescript.d.ts.map +1 -1
- package/dist/lang/typescript.js.map +1 -1
- package/dist/net/policy.d.ts +18 -1
- package/dist/net/policy.d.ts.map +1 -1
- package/dist/net/policy.js +6 -4
- package/dist/net/policy.js.map +1 -1
- package/dist/ops/index.d.ts +39 -0
- package/dist/ops/index.d.ts.map +1 -0
- package/dist/ops/index.js +37 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/ops/inputs.d.ts +170 -0
- package/dist/ops/inputs.d.ts.map +1 -0
- package/dist/ops/inputs.js +142 -0
- package/dist/ops/inputs.js.map +1 -0
- package/dist/ops/verbs.d.ts +136 -0
- package/dist/ops/verbs.d.ts.map +1 -0
- package/dist/ops/verbs.js +76 -0
- package/dist/ops/verbs.js.map +1 -0
- package/dist/plan/auto.d.ts +69 -0
- package/dist/plan/auto.d.ts.map +1 -0
- package/dist/plan/auto.js +70 -0
- package/dist/plan/auto.js.map +1 -0
- package/dist/plan/budget.d.ts +21 -0
- package/dist/plan/budget.d.ts.map +1 -0
- package/dist/plan/budget.js +26 -0
- package/dist/plan/budget.js.map +1 -0
- package/dist/plan/grammar.d.ts +14 -0
- package/dist/plan/grammar.d.ts.map +1 -1
- package/dist/plan/grammar.js +47 -6
- package/dist/plan/grammar.js.map +1 -1
- package/dist/plan/lexical.d.ts.map +1 -1
- package/dist/plan/lexical.js +1 -7
- package/dist/plan/lexical.js.map +1 -1
- package/dist/plan/planners.d.ts +35 -2
- package/dist/plan/planners.d.ts.map +1 -1
- package/dist/plan/planners.js +35 -2
- package/dist/plan/planners.js.map +1 -1
- package/dist/plan/structural.d.ts +19 -5
- package/dist/plan/structural.d.ts.map +1 -1
- package/dist/plan/structural.js +180 -33
- package/dist/plan/structural.js.map +1 -1
- package/dist/repomap/cache.d.ts +48 -0
- package/dist/repomap/cache.d.ts.map +1 -1
- package/dist/repomap/cache.js +112 -13
- package/dist/repomap/cache.js.map +1 -1
- package/dist/repomap/io.d.ts +24 -0
- package/dist/repomap/io.d.ts.map +1 -0
- package/dist/repomap/io.js +34 -0
- package/dist/repomap/io.js.map +1 -0
- package/dist/repomap/map.d.ts +69 -6
- package/dist/repomap/map.d.ts.map +1 -1
- package/dist/repomap/map.js +104 -22
- package/dist/repomap/map.js.map +1 -1
- package/dist/repomap/rank.d.ts +26 -2
- package/dist/repomap/rank.d.ts.map +1 -1
- package/dist/repomap/rank.js +20 -0
- package/dist/repomap/rank.js.map +1 -1
- package/dist/repomap/reader.d.ts +73 -0
- package/dist/repomap/reader.d.ts.map +1 -0
- package/dist/repomap/reader.js +30 -0
- package/dist/repomap/reader.js.map +1 -0
- package/dist/repomap/tags.d.ts.map +1 -1
- package/dist/repomap/tags.js.map +1 -1
- package/dist/retrieve.d.ts +8 -0
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +12 -1
- package/dist/retrieve.js.map +1 -1
- package/dist/smelter.d.ts +79 -0
- package/dist/smelter.d.ts.map +1 -0
- package/dist/smelter.js +65 -0
- package/dist/smelter.js.map +1 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +2 -2
- package/dist/stages.js.map +1 -1
- package/dist/stats.d.ts +2 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +2 -1
- package/dist/stats.js.map +1 -1
- package/dist/store-dir.d.ts +43 -4
- package/dist/store-dir.d.ts.map +1 -1
- package/dist/store-dir.js +48 -5
- package/dist/store-dir.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +6 -0
- package/dist/store.js.map +1 -1
- package/dist/text/json-edit.d.ts +71 -0
- package/dist/text/json-edit.d.ts.map +1 -0
- package/dist/text/json-edit.js +219 -0
- package/dist/text/json-edit.js.map +1 -0
- package/dist/types.d.ts +35 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +11 -11
- package/dist/cli/resolve.d.ts +0 -103
- package/dist/cli/resolve.d.ts.map +0 -1
- package/dist/cli/resolve.js +0 -106
- package/dist/cli/resolve.js.map +0 -1
package/dist/repomap/map.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { lstatSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
1
|
import { join } from 'node:path';
|
|
3
2
|
import { detectLanguage } from '../detect.js';
|
|
4
3
|
import { SmeltError } from '../errors.js';
|
|
5
4
|
import { TagsCache, tagsCacheKey } from './cache.js';
|
|
5
|
+
import { fsCall } from './io.js';
|
|
6
6
|
import { rankDefinitions } from './rank.js';
|
|
7
|
+
import { nodeFsReader } from './reader.js';
|
|
7
8
|
import { extractTags } from './tags.js';
|
|
8
9
|
/**
|
|
9
|
-
* The repo-map builder —
|
|
10
|
+
* The repo-map builder — the cross-file shape.
|
|
10
11
|
*
|
|
11
12
|
* **Modelled on Aider's repo-map, and credited as such.** The whole design is prior
|
|
12
13
|
* art: Paul Gauthier's Aider extracts tree-sitter definition/reference tags per file,
|
|
@@ -18,6 +19,9 @@ import { extractTags } from './tags.js';
|
|
|
18
19
|
*
|
|
19
20
|
* - **Local files only** (Law 1). The caller names the root; symlinks are never
|
|
20
21
|
* followed, so the walk cannot leave it; binary files are skipped; no network.
|
|
22
|
+
* The whole tree is read through one read-only seam, {@link RepoReader} — the
|
|
23
|
+
* default is `node:fs` and the interface has no writer, so the only bytes this
|
|
24
|
+
* module can write are the tags cache the caller asked for by name.
|
|
21
25
|
* - **Every inclusion is explainable** (Law 2, applied to inclusion rather than
|
|
22
26
|
* elision): each symbol in the map carries a rule id and a sentence stating its
|
|
23
27
|
* definition site and the measured reference counts that ranked it.
|
|
@@ -25,8 +29,32 @@ import { extractTags } from './tags.js';
|
|
|
25
29
|
* path, name, and line. Two runs over the same tree are byte-identical.
|
|
26
30
|
* - **The budget is bytes**, same contract as the planners, and it is respected by
|
|
27
31
|
* construction: symbols are added in rank order until the next line would not fit.
|
|
28
|
-
* - **The cache lives only in a directory the caller explicitly hands in**,
|
|
29
|
-
*
|
|
32
|
+
* - **The cache lives only in a directory the caller explicitly hands in**, a corrupt
|
|
33
|
+
* entry is discarded loudly — a warning in the result — never trusted, and it is
|
|
34
|
+
* bounded: each build sweeps the entries it did not use, so the superseded
|
|
35
|
+
* pre-edit version of every file does not accumulate forever. The sweep is
|
|
36
|
+
* housekeeping and never fatal: it runs after the map is computed, so a cache it
|
|
37
|
+
* cannot tidy costs a re-parse next time, never this map. See `cache.ts`.
|
|
38
|
+
* - **Every failure is a `SmeltError`**: the consumer contract's one promise about
|
|
39
|
+
* errors holds here too, so a missing root or an unreadable file arrives as
|
|
40
|
+
* {@link RepoMapIoError} naming the path, not as a raw Node `ENOENT`. Every
|
|
41
|
+
* filesystem call in this module goes through `fsCall` in `io.ts`.
|
|
42
|
+
*
|
|
43
|
+
* **What the ranking can and cannot resolve.** A reference binds to a definition **by
|
|
44
|
+
* bare identifier** — the tags carry names, not resolved symbols — so every definition
|
|
45
|
+
* of a name receives every reference to that name, wherever either lives. Two files
|
|
46
|
+
* that both define `run` share one another's inbound references and therefore rank
|
|
47
|
+
* alike; two overloads of one name each count the whole traffic to it, so a name is
|
|
48
|
+
* counted once per definition of it; an identifier that happens to collide with an
|
|
49
|
+
* unrelated one somewhere else in the tree lends it rank. This is Aider's design, not
|
|
50
|
+
* a defect introduced here: resolving properly means per-language import and scope
|
|
51
|
+
* resolution — a type checker per language — and the map is a *ranking heuristic* for
|
|
52
|
+
* deciding what a human or a model should look at first, never a symbol resolver.
|
|
53
|
+
* What it must not do is *claim* a resolution it did not perform, and it does not: the
|
|
54
|
+
* receipt on each entry says what was actually measured — `refsIn` is the number of
|
|
55
|
+
* references to that **name** across the scanned tree — so a reader who knows this
|
|
56
|
+
* paragraph can read the numbers for exactly what they are. Anything that needs true
|
|
57
|
+
* binding (rename, call graph, dead-code detection) needs a different tool.
|
|
30
58
|
*
|
|
31
59
|
* **Deliberately NOT a `Planner`.** `buildRepoMap` returns a {@link RepoMap},
|
|
32
60
|
* not an `ElisionPlan`: nothing here is elided, nothing is stored under a hash, and
|
|
@@ -57,11 +85,35 @@ export const REPO_MAP_PATH_ONLY_RULE = 'path-only';
|
|
|
57
85
|
/** Rule id for the warning left behind when a corrupt cache entry is discarded. */
|
|
58
86
|
export const REPO_MAP_CACHE_CORRUPT_RULE = 'cache-entry-corrupt';
|
|
59
87
|
/**
|
|
60
|
-
* The ignore list used when the caller supplies none.
|
|
61
|
-
*
|
|
62
|
-
*
|
|
88
|
+
* The ignore list used when the caller supplies none.
|
|
89
|
+
*
|
|
90
|
+
* Two kinds of directory are on it, for one reason: **including them makes the map
|
|
91
|
+
* wrong, not merely large.** `.git` is object storage and `node_modules` is other
|
|
92
|
+
* people's code — neither is the repository's own source. The rest are build outputs,
|
|
93
|
+
* and they are the sharper case: a built TypeScript repo carries `dist/foo.js` and
|
|
94
|
+
* `dist/foo.d.ts` beside `src/foo.ts`, so *every* symbol was ranked and rendered three
|
|
95
|
+
* times, the duplicates referenced each other, and the default map of the commonest
|
|
96
|
+
* repo shape in this ecosystem was a third source and two-thirds its own compiler
|
|
97
|
+
* output. The names here (`dist`, `build`, `out`, `coverage`) are the conventional
|
|
98
|
+
* output directories of the toolchains this map is most often pointed at; a repo that
|
|
99
|
+
* builds somewhere else passes its own list.
|
|
100
|
+
*
|
|
101
|
+
* Deliberately still tiny, and deliberately **not** a `.gitignore` parser: an ignore
|
|
102
|
+
* list smelt cannot state in one sentence is one nobody can predict.
|
|
103
|
+
*
|
|
104
|
+
* A caller with opinions passes its own list, which *replaces* this one — it is not
|
|
105
|
+
* merged with it. Replacement is the documented contract because a merge means there
|
|
106
|
+
* is no way to say "map my `dist`, I meant it", and a default you cannot turn off is
|
|
107
|
+
* not a default.
|
|
63
108
|
*/
|
|
64
|
-
export const DEFAULT_REPO_IGNORE = [
|
|
109
|
+
export const DEFAULT_REPO_IGNORE = [
|
|
110
|
+
'.git',
|
|
111
|
+
'node_modules',
|
|
112
|
+
'dist',
|
|
113
|
+
'build',
|
|
114
|
+
'out',
|
|
115
|
+
'coverage',
|
|
116
|
+
];
|
|
65
117
|
/**
|
|
66
118
|
* Build a ranked symbol map of the repository under `options.root`.
|
|
67
119
|
*
|
|
@@ -70,6 +122,10 @@ export const DEFAULT_REPO_IGNORE = ['.git', 'node_modules'];
|
|
|
70
122
|
* `test/guards/repo-map.test.ts`, not assumed.
|
|
71
123
|
*
|
|
72
124
|
* @throws {SmeltError} when `budgetBytes` is not a positive integer.
|
|
125
|
+
* @throws {RepoMapIoError} when a filesystem call fails — a root that is not there, a
|
|
126
|
+
* directory that will not list, a file that will not read. Never a raw Node error:
|
|
127
|
+
* the contract says every error smelt throws is a `SmeltError`, and a repo map that
|
|
128
|
+
* let `ENOENT` past would be the exception that made the promise worthless.
|
|
73
129
|
* @throws {GrammarUnavailableError} when a supported language's grammar cannot load —
|
|
74
130
|
* never a silent skip that would make the map quietly incomplete.
|
|
75
131
|
*/
|
|
@@ -81,15 +137,24 @@ export async function buildRepoMap(options) {
|
|
|
81
137
|
`how much of the map to throw away.`);
|
|
82
138
|
}
|
|
83
139
|
const ignore = options.ignore ?? DEFAULT_REPO_IGNORE;
|
|
84
|
-
const
|
|
85
|
-
const
|
|
140
|
+
const reader = options.reader ?? nodeFsReader();
|
|
141
|
+
const cacheDir = options.cacheDir;
|
|
142
|
+
const cache = cacheDir === undefined
|
|
143
|
+
? undefined
|
|
144
|
+
: fsCall('open the tags cache directory', cacheDir, () => new TagsCache(cacheDir));
|
|
145
|
+
const cacheCounts = { hits: 0, misses: 0, discarded: 0, pruned: 0 };
|
|
146
|
+
// The keys this build used. Everything else under the cache directory is a
|
|
147
|
+
// superseded entry — the pre-edit version of a file, or another tree's tags — and
|
|
148
|
+
// is swept once the walk is done. See the bound in `cache.ts`.
|
|
149
|
+
const liveKeys = new Set();
|
|
86
150
|
const warnings = [];
|
|
87
|
-
const files = scanFiles(root, ignore);
|
|
151
|
+
const files = scanFiles(reader, root, ignore);
|
|
88
152
|
let binarySkipped = 0;
|
|
89
153
|
const parsed = [];
|
|
90
154
|
const pathOnlyPaths = [];
|
|
91
155
|
for (const rel of files) {
|
|
92
|
-
const
|
|
156
|
+
const path = join(root, ...rel.split('/'));
|
|
157
|
+
const bytes = fsCall('read the file', path, () => reader.read(path));
|
|
93
158
|
if (bytes.includes(0)) {
|
|
94
159
|
binarySkipped += 1;
|
|
95
160
|
continue;
|
|
@@ -99,13 +164,18 @@ export async function buildRepoMap(options) {
|
|
|
99
164
|
pathOnlyPaths.push(rel);
|
|
100
165
|
continue;
|
|
101
166
|
}
|
|
102
|
-
|
|
103
|
-
|
|
167
|
+
// `RepoReader.read` promises `Uint8Array`, not `Buffer` (its own note says why),
|
|
168
|
+
// and `ignoreBOM` keeps this byte-for-byte what `Buffer.toString('utf8')` did: a
|
|
169
|
+
// leading U+FEFF stays in the text instead of being silently eaten.
|
|
170
|
+
const text = new TextDecoder('utf-8', { ignoreBOM: true }).decode(bytes);
|
|
171
|
+
const tags = await tagsFor(text, language, cache, cacheCounts, liveKeys, warnings, rel);
|
|
104
172
|
if (tags.defs.length === 0)
|
|
105
173
|
pathOnlyPaths.push(rel);
|
|
106
174
|
if (tags.defs.length > 0 || tags.refs.length > 0)
|
|
107
175
|
parsed.push({ path: rel, tags });
|
|
108
176
|
}
|
|
177
|
+
if (cache !== undefined)
|
|
178
|
+
cacheCounts.pruned = cache.sweep(liveKeys);
|
|
109
179
|
const ranked = rankDefinitions(parsed);
|
|
110
180
|
const focus = (options.focus ?? []).filter((term) => term.length > 0);
|
|
111
181
|
const ordered = orderWithFocus(ranked, focus);
|
|
@@ -161,10 +231,13 @@ export async function buildRepoMap(options) {
|
|
|
161
231
|
};
|
|
162
232
|
}
|
|
163
233
|
/** One file's tags — from the cache when possible, from the grammar when not. */
|
|
164
|
-
async function tagsFor(text, language, cache, counts, warnings, rel) {
|
|
234
|
+
async function tagsFor(text, language, cache, counts, liveKeys, warnings, rel) {
|
|
165
235
|
if (cache === undefined)
|
|
166
236
|
return extractTags(text, language);
|
|
167
237
|
const key = tagsCacheKey(language, text);
|
|
238
|
+
// Recorded before the lookup, not after: this key is what the tree holds now, so it
|
|
239
|
+
// survives the sweep whether it was a hit, a miss, or a discard-and-rewrite.
|
|
240
|
+
liveKeys.add(key);
|
|
168
241
|
const found = cache.read(key);
|
|
169
242
|
if (found === 'corrupt') {
|
|
170
243
|
counts.discarded += 1;
|
|
@@ -191,22 +264,31 @@ async function tagsFor(text, language, cache, counts, warnings, rel) {
|
|
|
191
264
|
*
|
|
192
265
|
* Symlinks are skipped outright — file or directory, in-root or out. Never following
|
|
193
266
|
* one is the simplest true implementation of "never follow a symlink out of the
|
|
194
|
-
* root": there is no resolution step to get wrong.
|
|
267
|
+
* root": there is no resolution step to get wrong. The refusal is stated on
|
|
268
|
+
* `isSymlink`, not left to the accident that an `lstat` of a link is neither file
|
|
269
|
+
* nor directory — so a reader whose `stat` resolves the target is refused just the
|
|
270
|
+
* same, and the guard can watch the refusal happen by counting reader calls.
|
|
271
|
+
*
|
|
272
|
+
* The ignore list is applied before the entry is statted, so an ignored path costs
|
|
273
|
+
* nothing and is never even looked at.
|
|
195
274
|
*/
|
|
196
|
-
function scanFiles(root, ignore) {
|
|
275
|
+
function scanFiles(reader, root, ignore) {
|
|
197
276
|
const found = [];
|
|
198
277
|
const walk = (dir, relDir) => {
|
|
199
|
-
|
|
278
|
+
const listed = fsCall('list the directory', dir, () => reader.list(dir));
|
|
279
|
+
for (const entry of listed.map((item) => item.name).toSorted()) {
|
|
200
280
|
const rel = relDir === '' ? entry : `${relDir}/${entry}`;
|
|
201
281
|
if (isIgnored(rel, ignore))
|
|
202
282
|
continue;
|
|
203
283
|
const full = join(dir, entry);
|
|
204
|
-
const stat =
|
|
205
|
-
if (stat
|
|
284
|
+
const stat = fsCall('stat', full, () => reader.stat(full));
|
|
285
|
+
if (stat === undefined)
|
|
286
|
+
continue; // the reader has nothing there
|
|
287
|
+
if (stat.isSymlink)
|
|
206
288
|
continue;
|
|
207
|
-
if (stat.isDirectory
|
|
289
|
+
if (stat.isDirectory)
|
|
208
290
|
walk(full, rel);
|
|
209
|
-
else if (stat.isFile
|
|
291
|
+
else if (stat.isFile)
|
|
210
292
|
found.push(rel);
|
|
211
293
|
}
|
|
212
294
|
};
|
package/dist/repomap/map.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/repomap/map.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/repomap/map.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path';\n\nimport { detectLanguage } from '../detect.ts';\nimport { SmeltError } from '../errors.ts';\nimport type { LanguageId } from '../types.ts';\n\nimport { TagsCache, tagsCacheKey } from './cache.ts';\nimport { fsCall } from './io.ts';\nimport { rankDefinitions } from './rank.ts';\nimport type { FileTagsEntry, RankedDefinition } from './rank.ts';\nimport { nodeFsReader } from './reader.ts';\nimport type { RepoReader } from './reader.ts';\nimport { extractTags } from './tags.ts';\nimport type { FileTags } from './tags.ts';\n\n/**\n * The repo-map builder — the cross-file shape.\n *\n * **Modelled on Aider's repo-map, and credited as such.** The whole design is prior\n * art: Paul Gauthier's Aider extracts tree-sitter definition/reference tags per file,\n * runs PageRank over the cross-file reference graph, fits the ranked result to a\n * budget, and caches tags on disk keyed by content — see\n * https://aider.chat/docs/repomap.html and `aider/repomap.py` in\n * https://github.com/Aider-AI/aider. Nothing about the approach is this project's\n * invention; what this module adds is only smelt's own house rules:\n *\n * - **Local files only** (Law 1). The caller names the root; symlinks are never\n * followed, so the walk cannot leave it; binary files are skipped; no network.\n * The whole tree is read through one read-only seam, {@link RepoReader} — the\n * default is `node:fs` and the interface has no writer, so the only bytes this\n * module can write are the tags cache the caller asked for by name.\n * - **Every inclusion is explainable** (Law 2, applied to inclusion rather than\n * elision): each symbol in the map carries a rule id and a sentence stating its\n * definition site and the measured reference counts that ranked it.\n * - **Deterministic**: fixed PageRank constants, sorted walks, a total tie-break by\n * path, name, and line. Two runs over the same tree are byte-identical.\n * - **The budget is bytes**, same contract as the planners, and it is respected by\n * construction: symbols are added in rank order until the next line would not fit.\n * - **The cache lives only in a directory the caller explicitly hands in**, a corrupt\n * entry is discarded loudly — a warning in the result — never trusted, and it is\n * bounded: each build sweeps the entries it did not use, so the superseded\n * pre-edit version of every file does not accumulate forever. The sweep is\n * housekeeping and never fatal: it runs after the map is computed, so a cache it\n * cannot tidy costs a re-parse next time, never this map. See `cache.ts`.\n * - **Every failure is a `SmeltError`**: the consumer contract's one promise about\n * errors holds here too, so a missing root or an unreadable file arrives as\n * {@link RepoMapIoError} naming the path, not as a raw Node `ENOENT`. Every\n * filesystem call in this module goes through `fsCall` in `io.ts`.\n *\n * **What the ranking can and cannot resolve.** A reference binds to a definition **by\n * bare identifier** — the tags carry names, not resolved symbols — so every definition\n * of a name receives every reference to that name, wherever either lives. Two files\n * that both define `run` share one another's inbound references and therefore rank\n * alike; two overloads of one name each count the whole traffic to it, so a name is\n * counted once per definition of it; an identifier that happens to collide with an\n * unrelated one somewhere else in the tree lends it rank. This is Aider's design, not\n * a defect introduced here: resolving properly means per-language import and scope\n * resolution — a type checker per language — and the map is a *ranking heuristic* for\n * deciding what a human or a model should look at first, never a symbol resolver.\n * What it must not do is *claim* a resolution it did not perform, and it does not: the\n * receipt on each entry says what was actually measured — `refsIn` is the number of\n * references to that **name** across the scanned tree — so a reader who knows this\n * paragraph can read the numbers for exactly what they are. Anything that needs true\n * binding (rename, call graph, dead-code detection) needs a different tool.\n *\n * **Deliberately NOT a `Planner`.** `buildRepoMap` returns a {@link RepoMap},\n * not an `ElisionPlan`: nothing here is elided, nothing is stored under a hash, and\n * there is no original to reconstruct — the map is a *summary built up under a\n * budget*, not a *removal to be reversed*. Forcing the Planner interface onto it\n * would claim Law 3 (reversibility) about output that has no bytes to give back,\n * which is exactly the kind of lie the interface exists to prevent. The CLI serves\n * it as its own subcommand (`smelt map`), never as a `--strategy`.\n */\n\n/** The id stamped on every map this module emits. */\nexport const REPO_MAP_ID = 'repomap/v1';\n\n/**\n * Rule id for a symbol something in the scanned tree references — its own file\n * included, so its rank may still be zero when every reference is same-file (only\n * cross-file references move rank).\n */\nexport const REPO_MAP_RANKED_RULE = 'ranked-definition';\n\n/** Rule id for a definition nothing in the scanned tree references. */\nexport const REPO_MAP_UNREFERENCED_RULE = 'unreferenced-definition';\n\n/**\n * Rule id for a definition promoted because it matches a caller-supplied focus term.\n * The rank stays the measured PageRank share — focus moves a symbol's *place in the\n * fill order*, never its numbers.\n */\nexport const REPO_MAP_FOCUS_RULE = 'focus-match';\n\n/** Rule id for a file listed by path because no definitions were extracted from it. */\nexport const REPO_MAP_PATH_ONLY_RULE = 'path-only';\n\n/** Rule id for the warning left behind when a corrupt cache entry is discarded. */\nexport const REPO_MAP_CACHE_CORRUPT_RULE = 'cache-entry-corrupt';\n\n/**\n * The ignore list used when the caller supplies none.\n *\n * Two kinds of directory are on it, for one reason: **including them makes the map\n * wrong, not merely large.** `.git` is object storage and `node_modules` is other\n * people's code — neither is the repository's own source. The rest are build outputs,\n * and they are the sharper case: a built TypeScript repo carries `dist/foo.js` and\n * `dist/foo.d.ts` beside `src/foo.ts`, so *every* symbol was ranked and rendered three\n * times, the duplicates referenced each other, and the default map of the commonest\n * repo shape in this ecosystem was a third source and two-thirds its own compiler\n * output. The names here (`dist`, `build`, `out`, `coverage`) are the conventional\n * output directories of the toolchains this map is most often pointed at; a repo that\n * builds somewhere else passes its own list.\n *\n * Deliberately still tiny, and deliberately **not** a `.gitignore` parser: an ignore\n * list smelt cannot state in one sentence is one nobody can predict.\n *\n * A caller with opinions passes its own list, which *replaces* this one — it is not\n * merged with it. Replacement is the documented contract because a merge means there\n * is no way to say \"map my `dist`, I meant it\", and a default you cannot turn off is\n * not a default.\n */\nexport const DEFAULT_REPO_IGNORE: readonly string[] = [\n '.git',\n 'node_modules',\n 'dist',\n 'build',\n 'out',\n 'coverage',\n];\n\n/** Why an entry is in the map — same two-register shape as {@link ElisionReason}. */\nexport interface RepoMapReason {\n /** Stable machine id, e.g. `'ranked-definition'`. */\n readonly rule: string;\n /** A sentence a human can read: definition site and the counts that ranked it. */\n readonly explanation: string;\n}\n\nexport interface RepoMapOptions {\n /** The repository root to read. Local files only; symlinks are never followed. */\n readonly root: string;\n /** Ceiling for the rendered map, in UTF-8 bytes. Respected, not aimed at. */\n readonly budgetBytes: number;\n /**\n * What the task is actually about — same meaning as a planner's focus. A\n * definition whose name or path contains a term (case-insensitive, like the\n * lexical planner's default) is promoted to the front of the fill order, so it\n * survives a tight budget. Promotion only: the measured rank and reference\n * counts are never altered, and each promoted entry says which term it matched.\n */\n readonly focus?: readonly string[];\n /**\n * Paths to skip, replacing {@link DEFAULT_REPO_IGNORE}. An entry containing `/` is\n * matched as a root-relative path prefix — a trailing slash counts, so `build/`\n * means the root-level `build` tree, never every `build` segment anywhere; a bare\n * name matches any path segment.\n */\n readonly ignore?: readonly string[];\n /**\n * Directory for the tags cache. **Only** when this is passed is anything written\n * to disk — smelt never writes outside a store or cache it was explicitly handed.\n */\n readonly cacheDir?: string;\n /**\n * The filesystem the map reads through. Defaults to {@link nodeFsReader} — plain\n * `node:fs`, the calls this module used to make in-line — so callers that do not\n * care never mention it. Hand in a {@link RepoReader} to map a tree that is not on\n * this disk, or to watch, call by call, exactly what the walk touched. Read-only\n * by construction: the interface has no writer.\n */\n readonly reader?: RepoReader;\n}\n\n/** One symbol included in the rendered map, with the receipt for its inclusion. */\nexport interface RepoMapEntry {\n readonly path: string;\n readonly name: string;\n readonly kind: string;\n /** 1-based line of the definition. */\n readonly line: number;\n /** PageRank share this definition attracted. `0` for unreferenced definitions. */\n readonly rank: number;\n /** Measured: total references to this name across the scanned tree. */\n readonly refsIn: number;\n /** Measured: distinct files holding those references. */\n readonly refsInFiles: number;\n /** Measured: references the defining file makes to names defined elsewhere. */\n readonly refsOut: number;\n readonly reason: RepoMapReason;\n}\n\n/** A file listed by path because no definitions were extracted from it. */\nexport interface RepoMapPathEntry {\n readonly path: string;\n readonly reason: RepoMapReason;\n}\n\n/** Something worth telling the caller that is not worth failing the build over. */\nexport interface RepoMapWarning {\n readonly rule: string;\n readonly explanation: string;\n}\n\n/** Measured cache activity for one build. Counts only — smelt claims no rates. */\nexport interface RepoMapCacheCounts {\n /** Lookups answered from disk. */\n readonly hits: number;\n /** Lookups that found nothing and re-extracted from source. */\n readonly misses: number;\n /** Corrupt entries deleted and re-extracted; each one also left a warning. */\n readonly discarded: number;\n /**\n * Entries this build swept because it did not use them — the bound on the cache,\n * made visible. A non-zero count on a repeat build over an unchanged tree means the\n * cache directory is shared with another tree, which costs re-parses.\n */\n readonly pruned: number;\n}\n\nexport interface RepoMap {\n readonly id: typeof REPO_MAP_ID;\n /** The rendered map. Always at most `budgetBytes` UTF-8 bytes. */\n readonly text: string;\n readonly outputBytes: number;\n readonly budgetBytes: number;\n /** The symbols that fit, in fill order (focus matches first, then rank). */\n readonly entries: readonly RepoMapEntry[];\n /** Path-only files that fit, after the symbols, in path order. */\n readonly pathOnly: readonly RepoMapPathEntry[];\n /** All ranked definitions found, before the budget cut anything. */\n readonly definitionsTotal: number;\n /** All path-only candidates found, before the budget cut anything. */\n readonly pathOnlyTotal: number;\n /** Regular files examined under the root, after the ignore list. */\n readonly filesScanned: number;\n /** Files skipped because their bytes contain a NUL — binary, not mappable. */\n readonly binarySkipped: number;\n readonly warnings: readonly RepoMapWarning[];\n /** Present only when the caller handed in a `cacheDir`. Never invented. */\n readonly cache?: RepoMapCacheCounts;\n}\n\n/**\n * Build a ranked symbol map of the repository under `options.root`.\n *\n * Reads local files only. Deterministic: two runs over the same tree, with or without\n * a warm cache, produce byte-identical maps — asserted by the guard in\n * `test/guards/repo-map.test.ts`, not assumed.\n *\n * @throws {SmeltError} when `budgetBytes` is not a positive integer.\n * @throws {RepoMapIoError} when a filesystem call fails — a root that is not there, a\n * directory that will not list, a file that will not read. Never a raw Node error:\n * the contract says every error smelt throws is a `SmeltError`, and a repo map that\n * let `ENOENT` past would be the exception that made the promise worthless.\n * @throws {GrammarUnavailableError} when a supported language's grammar cannot load —\n * never a silent skip that would make the map quietly incomplete.\n */\nexport async function buildRepoMap(options: RepoMapOptions): Promise<RepoMap> {\n const { root, budgetBytes } = options;\n if (!Number.isInteger(budgetBytes) || budgetBytes < 1) {\n throw new SmeltError(\n `smelt: budgetBytes must be a positive integer, got ${String(budgetBytes)}. ` +\n `There is no default budget — a budget smelt invented would silently decide ` +\n `how much of the map to throw away.`,\n );\n }\n const ignore = options.ignore ?? DEFAULT_REPO_IGNORE;\n const reader = options.reader ?? nodeFsReader();\n const cacheDir = options.cacheDir;\n const cache =\n cacheDir === undefined\n ? undefined\n : fsCall('open the tags cache directory', cacheDir, () => new TagsCache(cacheDir));\n const cacheCounts = { hits: 0, misses: 0, discarded: 0, pruned: 0 };\n // The keys this build used. Everything else under the cache directory is a\n // superseded entry — the pre-edit version of a file, or another tree's tags — and\n // is swept once the walk is done. See the bound in `cache.ts`.\n const liveKeys = new Set<string>();\n const warnings: RepoMapWarning[] = [];\n\n const files = scanFiles(reader, root, ignore);\n let binarySkipped = 0;\n const parsed: FileTagsEntry[] = [];\n const pathOnlyPaths: string[] = [];\n\n for (const rel of files) {\n const path = join(root, ...rel.split('/'));\n const bytes = fsCall('read the file', path, () => reader.read(path));\n if (bytes.includes(0)) {\n binarySkipped += 1;\n continue;\n }\n const language = detectLanguage(rel);\n if (language === 'unknown') {\n pathOnlyPaths.push(rel);\n continue;\n }\n // `RepoReader.read` promises `Uint8Array`, not `Buffer` (its own note says why),\n // and `ignoreBOM` keeps this byte-for-byte what `Buffer.toString('utf8')` did: a\n // leading U+FEFF stays in the text instead of being silently eaten.\n const text = new TextDecoder('utf-8', { ignoreBOM: true }).decode(bytes);\n const tags = await tagsFor(text, language, cache, cacheCounts, liveKeys, warnings, rel);\n if (tags.defs.length === 0) pathOnlyPaths.push(rel);\n if (tags.defs.length > 0 || tags.refs.length > 0) parsed.push({ path: rel, tags });\n }\n\n if (cache !== undefined) cacheCounts.pruned = cache.sweep(liveKeys);\n\n const ranked = rankDefinitions(parsed);\n const focus = (options.focus ?? []).filter((term) => term.length > 0);\n const ordered = orderWithFocus(ranked, focus);\n\n // Fit to the budget: ranked symbols first — focus matches promoted to the front,\n // each partition in rank order — then path-only files, in path order. Filling\n // stops at the first line that does not fit, so the included set is always a\n // prefix of that order — a map that skipped its #2 symbol to squeeze in its #9\n // would be lying about what mattered.\n const lines: string[] = [];\n let bytes = 0;\n const tryAppend = (line: string): boolean => {\n const lineBytes = Buffer.byteLength(line, 'utf8') + 1; // its trailing newline\n if (bytes + lineBytes > budgetBytes) return false;\n lines.push(line);\n bytes += lineBytes;\n return true;\n };\n\n const entries: RepoMapEntry[] = [];\n for (const { definition, focusTerm } of ordered) {\n if (!tryAppend(renderDefinition(definition))) break;\n entries.push(toEntry(definition, focusTerm));\n }\n const pathOnly: RepoMapPathEntry[] = [];\n if (entries.length === ordered.length) {\n for (const rel of pathOnlyPaths) {\n if (!tryAppend(`${rel} [path only]`)) break;\n pathOnly.push({\n path: rel,\n reason: {\n rule: REPO_MAP_PATH_ONLY_RULE,\n explanation: `no definitions extracted from ${rel}; listed by path so the file stays visible`,\n },\n });\n }\n }\n\n const text = lines.length === 0 ? '' : `${lines.join('\\n')}\\n`;\n return {\n id: REPO_MAP_ID,\n text,\n outputBytes: Buffer.byteLength(text, 'utf8'),\n budgetBytes,\n entries,\n pathOnly,\n definitionsTotal: ranked.length,\n pathOnlyTotal: pathOnlyPaths.length,\n filesScanned: files.length,\n binarySkipped,\n warnings,\n ...(cache === undefined ? {} : { cache: { ...cacheCounts } }),\n };\n}\n\n/** One file's tags — from the cache when possible, from the grammar when not. */\nasync function tagsFor(\n text: string,\n language: LanguageId,\n cache: TagsCache | undefined,\n counts: { hits: number; misses: number; discarded: number; pruned: number },\n liveKeys: Set<string>,\n warnings: RepoMapWarning[],\n rel: string,\n): Promise<FileTags> {\n if (cache === undefined) return extractTags(text, language);\n\n const key = tagsCacheKey(language, text);\n // Recorded before the lookup, not after: this key is what the tree holds now, so it\n // survives the sweep whether it was a hit, a miss, or a discard-and-rewrite.\n liveKeys.add(key);\n const found = cache.read(key);\n if (found === 'corrupt') {\n counts.discarded += 1;\n warnings.push({\n rule: REPO_MAP_CACHE_CORRUPT_RULE,\n explanation:\n `discarded a corrupt cache entry for ${rel} and re-extracted its tags from ` +\n `source; the entry file was deleted so it cannot be read again`,\n });\n } else if (found !== undefined) {\n counts.hits += 1;\n return found;\n } else {\n counts.misses += 1;\n }\n const tags = await extractTags(text, language);\n cache.write(key, tags);\n return tags;\n}\n\n/**\n * Walk the tree under `root`, depth-first in sorted order, returning `/`-separated\n * relative paths of every regular file that survives the ignore list.\n *\n * Symlinks are skipped outright — file or directory, in-root or out. Never following\n * one is the simplest true implementation of \"never follow a symlink out of the\n * root\": there is no resolution step to get wrong. The refusal is stated on\n * `isSymlink`, not left to the accident that an `lstat` of a link is neither file\n * nor directory — so a reader whose `stat` resolves the target is refused just the\n * same, and the guard can watch the refusal happen by counting reader calls.\n *\n * The ignore list is applied before the entry is statted, so an ignored path costs\n * nothing and is never even looked at.\n */\nfunction scanFiles(reader: RepoReader, root: string, ignore: readonly string[]): readonly string[] {\n const found: string[] = [];\n const walk = (dir: string, relDir: string): void => {\n const listed = fsCall('list the directory', dir, () => reader.list(dir));\n for (const entry of listed.map((item) => item.name).toSorted()) {\n const rel = relDir === '' ? entry : `${relDir}/${entry}`;\n if (isIgnored(rel, ignore)) continue;\n const full = join(dir, entry);\n const stat = fsCall('stat', full, () => reader.stat(full));\n if (stat === undefined) continue; // the reader has nothing there\n if (stat.isSymlink) continue;\n if (stat.isDirectory) walk(full, rel);\n else if (stat.isFile) found.push(rel);\n }\n };\n walk(root, '');\n return found;\n}\n\n/** See {@link RepoMapOptions.ignore} for the two match modes. */\nfunction isIgnored(rel: string, ignore: readonly string[]): boolean {\n for (const entry of ignore) {\n const cleaned = entry.replace(/\\/+$/, '');\n if (cleaned === '') continue;\n // Prefix mode is decided on the entry AS WRITTEN, before the trailing slash is\n // trimmed: `build/` contains a `/` and so is the documented root-relative prefix\n // (the root-level `build` tree), not a bare name that would match a `build`\n // segment at any depth.\n if (entry.includes('/')) {\n if (rel === cleaned || rel.startsWith(`${cleaned}/`)) return true;\n } else if (rel.split('/').includes(cleaned)) {\n return true;\n }\n }\n return false;\n}\n\n/** One ranked definition in fill order, with the focus term that promoted it, if any. */\ninterface OrderedDefinition {\n readonly definition: RankedDefinition;\n readonly focusTerm?: string;\n}\n\n/**\n * The fill order: focus-matched definitions first, then the rest, each partition\n * keeping the ranker's total order. A stable partition of a deterministic order is\n * itself deterministic, so the map's byte-for-byte claim survives focus untouched.\n * The match is a case-insensitive substring over name and path — the lexical\n * planner's default, so \"focus\" means the same thing in both places — and the\n * *first* matching term in caller order is the one the receipt names.\n */\nfunction orderWithFocus(\n ranked: readonly RankedDefinition[],\n focus: readonly string[],\n): readonly OrderedDefinition[] {\n if (focus.length === 0) return ranked.map((definition) => ({ definition }));\n const needles = focus.map((term) => term.toLowerCase());\n const matched: OrderedDefinition[] = [];\n const rest: OrderedDefinition[] = [];\n for (const definition of ranked) {\n const haystack = `${definition.path}\\0${definition.name}`.toLowerCase();\n const index = needles.findIndex((needle) => haystack.includes(needle));\n if (index === -1) rest.push({ definition });\n else matched.push({ definition, focusTerm: focus[index]! });\n }\n return [...matched, ...rest];\n}\n\n/** One rendered map line. Everything in it is measured, nothing estimated. */\nfunction renderDefinition(definition: RankedDefinition): string {\n return (\n `${definition.path}:${String(definition.line)} ${definition.kind} ${definition.name} ` +\n `[${String(definition.refsIn)} in from ${String(definition.refsInFiles)} ` +\n `${plural('file', definition.refsInFiles)}, ${String(definition.refsOut)} out]`\n );\n}\n\n/** Law 2, applied to inclusion: the receipt every included symbol carries. */\nfunction toEntry(definition: RankedDefinition, focusTerm?: string): RepoMapEntry {\n const site = `defined at ${definition.path}:${String(definition.line)}`;\n const counts =\n `${String(definition.refsIn)} ${plural('reference', definition.refsIn)} in ` +\n `from ${String(definition.refsInFiles)} ${plural('file', definition.refsInFiles)}; ` +\n `its file makes ${String(definition.refsOut)} ` +\n `${plural('reference', definition.refsOut)} out`;\n const reason: RepoMapReason =\n focusTerm !== undefined\n ? {\n rule: REPO_MAP_FOCUS_RULE,\n explanation: `${site}; matches focus \"${focusTerm}\"; ${counts}`,\n }\n : definition.refsIn === 0\n ? {\n rule: REPO_MAP_UNREFERENCED_RULE,\n explanation: `${site}; no references to it anywhere in the scanned tree`,\n }\n : {\n rule: REPO_MAP_RANKED_RULE,\n explanation: `${site}; ${counts}`,\n };\n return {\n path: definition.path,\n name: definition.name,\n kind: definition.kind,\n line: definition.line,\n rank: definition.rank,\n refsIn: definition.refsIn,\n refsInFiles: definition.refsInFiles,\n refsOut: definition.refsOut,\n reason,\n };\n}\n\nfunction plural(word: string, count: number): string {\n return count === 1 ? word : `${word}s`;\n}\n"]}
|
package/dist/repomap/rank.d.ts
CHANGED
|
@@ -13,6 +13,26 @@ import type { FileTags } from './tags.ts';
|
|
|
13
13
|
* fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a
|
|
14
14
|
* total tie-break (rank, then path, then name, then line). No `Math.random`, no
|
|
15
15
|
* `Date`, no map-iteration order leaking into the output.
|
|
16
|
+
*
|
|
17
|
+
* **The resolution limit, stated plainly: references bind by bare identifier.** A
|
|
18
|
+
* reference tag is a name and a count; there is no import graph, no scope, no
|
|
19
|
+
* declaration lookup. So `defsByName` below maps one identifier to *every* definition
|
|
20
|
+
* of it in the tree, and each reference to that identifier reaches all of them:
|
|
21
|
+
*
|
|
22
|
+
* - Same-name symbols in different files share rank and share `refsIn`. Two `run`s
|
|
23
|
+
* are, to this ranker, one name that several files happen to define.
|
|
24
|
+
* - Overloads and re-exports double-count: a name defined twice attracts the whole
|
|
25
|
+
* traffic to it twice, so it takes two lines near the top of the map.
|
|
26
|
+
* - A common identifier (`get`, `render`, `main`) collects references that in truth
|
|
27
|
+
* belong to something else entirely.
|
|
28
|
+
*
|
|
29
|
+
* This is Aider's design, inherited on purpose. The alternative is per-language import
|
|
30
|
+
* and scope resolution — a type checker per language — and the map is a *ranking
|
|
31
|
+
* heuristic* for what to read first, not a symbol resolver; something needing true
|
|
32
|
+
* binding (rename, call graph, dead code) needs a different tool. What matters under
|
|
33
|
+
* Law 4 is that nothing here claims otherwise: `refsIn` and `refsInFiles` are honestly
|
|
34
|
+
* the references to and the files mentioning the **name**, and each entry's receipt
|
|
35
|
+
* says so, so the numbers can be read for exactly what they measure.
|
|
16
36
|
*/
|
|
17
37
|
/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */
|
|
18
38
|
export declare const PAGERANK_DAMPING = 0.85;
|
|
@@ -31,9 +51,13 @@ export interface RankedDefinition {
|
|
|
31
51
|
readonly line: number;
|
|
32
52
|
/** Accumulated PageRank share. `0` when no other file references this name. */
|
|
33
53
|
readonly rank: number;
|
|
34
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Total references to this **name** across every scanned file (its own included).
|
|
56
|
+
* Per name, not per symbol: see the module comment — every definition of a name
|
|
57
|
+
* shares the count, because references bind by bare identifier.
|
|
58
|
+
*/
|
|
35
59
|
readonly refsIn: number;
|
|
36
|
-
/** Distinct files holding at least one of those references. */
|
|
60
|
+
/** Distinct files holding at least one of those references. Also per name. */
|
|
37
61
|
readonly refsInFiles: number;
|
|
38
62
|
/** References the defining file makes to names defined in *other* files. */
|
|
39
63
|
readonly refsOut: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AAEH,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAkH5F"}
|
|
1
|
+
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAkH5F","sourcesContent":["import type { FileTags } from './tags.ts';\n\n/**\n * Deterministic PageRank over the cross-file reference graph.\n *\n * Modelled on Aider's repo-map ranking (https://aider.chat/docs/repomap.html; see\n * `aider/repomap.py` in https://github.com/Aider-AI/aider): files are the graph's\n * nodes, and a file that references an identifier defined in another file contributes\n * a weighted edge toward the definer. A file's rank is then distributed across its\n * definitions in proportion to the reference weight each one attracted. The idea is\n * Aider's; only the implementation is this repository's.\n *\n * Deterministic by construction, which is what makes the map snapshot-testable:\n * fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a\n * total tie-break (rank, then path, then name, then line). No `Math.random`, no\n * `Date`, no map-iteration order leaking into the output.\n *\n * **The resolution limit, stated plainly: references bind by bare identifier.** A\n * reference tag is a name and a count; there is no import graph, no scope, no\n * declaration lookup. So `defsByName` below maps one identifier to *every* definition\n * of it in the tree, and each reference to that identifier reaches all of them:\n *\n * - Same-name symbols in different files share rank and share `refsIn`. Two `run`s\n * are, to this ranker, one name that several files happen to define.\n * - Overloads and re-exports double-count: a name defined twice attracts the whole\n * traffic to it twice, so it takes two lines near the top of the map.\n * - A common identifier (`get`, `render`, `main`) collects references that in truth\n * belong to something else entirely.\n *\n * This is Aider's design, inherited on purpose. The alternative is per-language import\n * and scope resolution — a type checker per language — and the map is a *ranking\n * heuristic* for what to read first, not a symbol resolver; something needing true\n * binding (rename, call graph, dead code) needs a different tool. What matters under\n * Law 4 is that nothing here claims otherwise: `refsIn` and `refsInFiles` are honestly\n * the references to and the files mentioning the **name**, and each entry's receipt\n * says so, so the numbers can be read for exactly what they measure.\n */\n\n/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */\nexport const PAGERANK_DAMPING = 0.85;\n\n/** Fixed iteration count — no convergence test, so runtime and output never vary. */\nexport const PAGERANK_ITERATIONS = 50;\n\n/** What the caller hands the ranker: one file's path and its extracted tags. */\nexport interface FileTagsEntry {\n readonly path: string;\n readonly tags: FileTags;\n}\n\n/** One definition with its computed rank and the measured counts that explain it. */\nexport interface RankedDefinition {\n readonly path: string;\n readonly name: string;\n readonly kind: string;\n readonly line: number;\n /** Accumulated PageRank share. `0` when no other file references this name. */\n readonly rank: number;\n /**\n * Total references to this **name** across every scanned file (its own included).\n * Per name, not per symbol: see the module comment — every definition of a name\n * shares the count, because references bind by bare identifier.\n */\n readonly refsIn: number;\n /** Distinct files holding at least one of those references. Also per name. */\n readonly refsInFiles: number;\n /** References the defining file makes to names defined in *other* files. */\n readonly refsOut: number;\n}\n\n/**\n * Rank every definition in the tag set.\n *\n * Only cross-file references move rank — a file citing its own definitions does not\n * boost itself — but `refsIn`/`refsInFiles` count every reference, so the explanation\n * a symbol carries never hides same-file usage.\n *\n * Returns all definitions (rank `0` included), sorted by rank descending with the\n * stable tie-break: path ascending, then name ascending, then line ascending.\n */\nexport function rankDefinitions(files: readonly FileTagsEntry[]): readonly RankedDefinition[] {\n const sorted = [...files].toSorted((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n const paths = sorted.map((file) => file.path);\n\n // Which files define each identifier, and every definition, in deterministic order.\n interface MutableDef {\n readonly path: string;\n readonly name: string;\n readonly kind: string;\n readonly line: number;\n rank: number;\n }\n const defsByName = new Map<string, MutableDef[]>();\n const allDefs: MutableDef[] = [];\n for (const { path, tags } of sorted) {\n for (const def of tags.defs) {\n const mutable: MutableDef = { path, name: def.name, kind: def.kind, line: def.line, rank: 0 };\n allDefs.push(mutable);\n const existing = defsByName.get(def.name);\n if (existing === undefined) defsByName.set(def.name, [mutable]);\n else existing.push(mutable);\n }\n }\n\n // Cross-file edges: referencing file → defining file, weighted by reference count.\n // outWeight is each file's total outgoing weight — the PageRank edge denominator,\n // which legitimately grows once per definer file so each edge keeps its full weight.\n // refsOutByFile is the *measured* count a Law 2 explanation may quote: it adds\n // `ref.count` exactly once per reference to a name some other file defines, no\n // matter how many files define it. edgeWeights aggregates per file pair for the\n // PageRank pass, while rank distribution below re-walks the refs so each\n // *definition* receives its own share.\n const outWeight = new Map<string, number>(paths.map((path) => [path, 0]));\n const refsOutByFile = new Map<string, number>(paths.map((path) => [path, 0]));\n const edgeWeights = new Map<string, Map<string, number>>(paths.map((path) => [path, new Map()]));\n const refsInTotal = new Map<string, number>();\n const refsInFiles = new Map<string, number>();\n for (const { path, tags } of sorted) {\n for (const ref of tags.refs) {\n const definers = defsByName.get(ref.name);\n if (definers === undefined) continue;\n refsInTotal.set(ref.name, (refsInTotal.get(ref.name) ?? 0) + ref.count);\n refsInFiles.set(ref.name, (refsInFiles.get(ref.name) ?? 0) + 1);\n const targets = new Set(definers.map((def) => def.path));\n let crossFile = false;\n for (const target of [...targets].toSorted()) {\n if (target === path) continue;\n crossFile = true;\n outWeight.set(path, (outWeight.get(path) ?? 0) + ref.count);\n const row = edgeWeights.get(path)!;\n row.set(target, (row.get(target) ?? 0) + ref.count);\n }\n if (crossFile) refsOutByFile.set(path, (refsOutByFile.get(path) ?? 0) + ref.count);\n }\n }\n\n // PageRank, fixed iterations. Dangling files (no outgoing weight) spread their rank\n // uniformly, the standard treatment; every sum runs in sorted-path order so the\n // floating-point result is identical on every run.\n const nodeCount = paths.length;\n let rank = new Map<string, number>(\n paths.map((path) => [path, nodeCount === 0 ? 0 : 1 / nodeCount]),\n );\n for (let iteration = 0; iteration < PAGERANK_ITERATIONS; iteration += 1) {\n let danglingSum = 0;\n for (const path of paths) {\n if (outWeight.get(path) === 0) danglingSum += rank.get(path)!;\n }\n const next = new Map<string, number>();\n const base =\n nodeCount === 0\n ? 0\n : (1 - PAGERANK_DAMPING) / nodeCount + (PAGERANK_DAMPING * danglingSum) / nodeCount;\n for (const path of paths) next.set(path, base);\n for (const source of paths) {\n const total = outWeight.get(source)!;\n if (total === 0) continue;\n const share = (PAGERANK_DAMPING * rank.get(source)!) / total;\n for (const [target, weight] of edgeWeights.get(source)!) {\n next.set(target, next.get(target)! + share * weight);\n }\n }\n rank = next;\n }\n\n // Distribute each file's rank across the definitions that attracted it, exactly the\n // way Aider does: every cross-file reference edge hands the referencing file's rank,\n // scaled by the edge's share of that file's outgoing weight, to the definition it\n // points at.\n for (const { path, tags } of sorted) {\n const total = outWeight.get(path)!;\n if (total === 0) continue;\n const sourceRank = rank.get(path)!;\n for (const ref of tags.refs) {\n const definers = defsByName.get(ref.name);\n if (definers === undefined) continue;\n for (const def of definers) {\n if (def.path === path) continue;\n def.rank += (sourceRank * ref.count) / total;\n }\n }\n }\n\n const ranked: RankedDefinition[] = allDefs.map((def) => ({\n path: def.path,\n name: def.name,\n kind: def.kind,\n line: def.line,\n rank: def.rank,\n refsIn: refsInTotal.get(def.name) ?? 0,\n refsInFiles: refsInFiles.get(def.name) ?? 0,\n refsOut: refsOutByFile.get(def.path) ?? 0,\n }));\n return ranked.toSorted(compareRanked);\n}\n\n/**\n * The total order every emitted map obeys: rank descending, then the stable\n * tie-break by path, name, and line. Total, so equal-rank symbols land in the same\n * place on every run and on every machine — remove any leg of it and the map's\n * byte-for-byte determinism claim quietly dies.\n */\nfunction compareRanked(a: RankedDefinition, b: RankedDefinition): number {\n if (a.rank !== b.rank) return a.rank > b.rank ? -1 : 1;\n if (a.path !== b.path) return a.path < b.path ? -1 : 1;\n if (a.name !== b.name) return a.name < b.name ? -1 : 1;\n return a.line - b.line;\n}\n"]}
|
package/dist/repomap/rank.js
CHANGED
|
@@ -12,6 +12,26 @@
|
|
|
12
12
|
* fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a
|
|
13
13
|
* total tie-break (rank, then path, then name, then line). No `Math.random`, no
|
|
14
14
|
* `Date`, no map-iteration order leaking into the output.
|
|
15
|
+
*
|
|
16
|
+
* **The resolution limit, stated plainly: references bind by bare identifier.** A
|
|
17
|
+
* reference tag is a name and a count; there is no import graph, no scope, no
|
|
18
|
+
* declaration lookup. So `defsByName` below maps one identifier to *every* definition
|
|
19
|
+
* of it in the tree, and each reference to that identifier reaches all of them:
|
|
20
|
+
*
|
|
21
|
+
* - Same-name symbols in different files share rank and share `refsIn`. Two `run`s
|
|
22
|
+
* are, to this ranker, one name that several files happen to define.
|
|
23
|
+
* - Overloads and re-exports double-count: a name defined twice attracts the whole
|
|
24
|
+
* traffic to it twice, so it takes two lines near the top of the map.
|
|
25
|
+
* - A common identifier (`get`, `render`, `main`) collects references that in truth
|
|
26
|
+
* belong to something else entirely.
|
|
27
|
+
*
|
|
28
|
+
* This is Aider's design, inherited on purpose. The alternative is per-language import
|
|
29
|
+
* and scope resolution — a type checker per language — and the map is a *ranking
|
|
30
|
+
* heuristic* for what to read first, not a symbol resolver; something needing true
|
|
31
|
+
* binding (rename, call graph, dead code) needs a different tool. What matters under
|
|
32
|
+
* Law 4 is that nothing here claims otherwise: `refsIn` and `refsInFiles` are honestly
|
|
33
|
+
* the references to and the files mentioning the **name**, and each entry's receipt
|
|
34
|
+
* says so, so the numbers can be read for exactly what they measure.
|
|
15
35
|
*/
|
|
16
36
|
/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */
|
|
17
37
|
export const PAGERANK_DAMPING = 0.85;
|
package/dist/repomap/rank.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../src/repomap/rank.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FileTags } from './tags.ts';\n\n/**\n * Deterministic PageRank over the cross-file reference graph.\n *\n * Modelled on Aider's repo-map ranking (https://aider.chat/docs/repomap.html; see\n * `aider/repomap.py` in https://github.com/Aider-AI/aider): files are the graph's\n * nodes, and a file that references an identifier defined in another file contributes\n * a weighted edge toward the definer. A file's rank is then distributed across its\n * definitions in proportion to the reference weight each one attracted. The idea is\n * Aider's; only the implementation is this repository's.\n *\n * Deterministic by construction, which is what makes the map snapshot-testable:\n * fixed damping, a fixed iteration count, nodes visited in sorted-path order, and a\n * total tie-break (rank, then path, then name, then line). No `Math.random`, no\n * `Date`, no map-iteration order leaking into the output.\n *\n * **The resolution limit, stated plainly: references bind by bare identifier.** A\n * reference tag is a name and a count; there is no import graph, no scope, no\n * declaration lookup. So `defsByName` below maps one identifier to *every* definition\n * of it in the tree, and each reference to that identifier reaches all of them:\n *\n * - Same-name symbols in different files share rank and share `refsIn`. Two `run`s\n * are, to this ranker, one name that several files happen to define.\n * - Overloads and re-exports double-count: a name defined twice attracts the whole\n * traffic to it twice, so it takes two lines near the top of the map.\n * - A common identifier (`get`, `render`, `main`) collects references that in truth\n * belong to something else entirely.\n *\n * This is Aider's design, inherited on purpose. The alternative is per-language import\n * and scope resolution — a type checker per language — and the map is a *ranking\n * heuristic* for what to read first, not a symbol resolver; something needing true\n * binding (rename, call graph, dead code) needs a different tool. What matters under\n * Law 4 is that nothing here claims otherwise: `refsIn` and `refsInFiles` are honestly\n * the references to and the files mentioning the **name**, and each entry's receipt\n * says so, so the numbers can be read for exactly what they measure.\n */\n\n/** The damping factor. 0.85 is the classic PageRank constant; fixed, never sampled. */\nexport const PAGERANK_DAMPING = 0.85;\n\n/** Fixed iteration count — no convergence test, so runtime and output never vary. */\nexport const PAGERANK_ITERATIONS = 50;\n\n/** What the caller hands the ranker: one file's path and its extracted tags. */\nexport interface FileTagsEntry {\n readonly path: string;\n readonly tags: FileTags;\n}\n\n/** One definition with its computed rank and the measured counts that explain it. */\nexport interface RankedDefinition {\n readonly path: string;\n readonly name: string;\n readonly kind: string;\n readonly line: number;\n /** Accumulated PageRank share. `0` when no other file references this name. */\n readonly rank: number;\n /**\n * Total references to this **name** across every scanned file (its own included).\n * Per name, not per symbol: see the module comment — every definition of a name\n * shares the count, because references bind by bare identifier.\n */\n readonly refsIn: number;\n /** Distinct files holding at least one of those references. Also per name. */\n readonly refsInFiles: number;\n /** References the defining file makes to names defined in *other* files. */\n readonly refsOut: number;\n}\n\n/**\n * Rank every definition in the tag set.\n *\n * Only cross-file references move rank — a file citing its own definitions does not\n * boost itself — but `refsIn`/`refsInFiles` count every reference, so the explanation\n * a symbol carries never hides same-file usage.\n *\n * Returns all definitions (rank `0` included), sorted by rank descending with the\n * stable tie-break: path ascending, then name ascending, then line ascending.\n */\nexport function rankDefinitions(files: readonly FileTagsEntry[]): readonly RankedDefinition[] {\n const sorted = [...files].toSorted((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n const paths = sorted.map((file) => file.path);\n\n // Which files define each identifier, and every definition, in deterministic order.\n interface MutableDef {\n readonly path: string;\n readonly name: string;\n readonly kind: string;\n readonly line: number;\n rank: number;\n }\n const defsByName = new Map<string, MutableDef[]>();\n const allDefs: MutableDef[] = [];\n for (const { path, tags } of sorted) {\n for (const def of tags.defs) {\n const mutable: MutableDef = { path, name: def.name, kind: def.kind, line: def.line, rank: 0 };\n allDefs.push(mutable);\n const existing = defsByName.get(def.name);\n if (existing === undefined) defsByName.set(def.name, [mutable]);\n else existing.push(mutable);\n }\n }\n\n // Cross-file edges: referencing file → defining file, weighted by reference count.\n // outWeight is each file's total outgoing weight — the PageRank edge denominator,\n // which legitimately grows once per definer file so each edge keeps its full weight.\n // refsOutByFile is the *measured* count a Law 2 explanation may quote: it adds\n // `ref.count` exactly once per reference to a name some other file defines, no\n // matter how many files define it. edgeWeights aggregates per file pair for the\n // PageRank pass, while rank distribution below re-walks the refs so each\n // *definition* receives its own share.\n const outWeight = new Map<string, number>(paths.map((path) => [path, 0]));\n const refsOutByFile = new Map<string, number>(paths.map((path) => [path, 0]));\n const edgeWeights = new Map<string, Map<string, number>>(paths.map((path) => [path, new Map()]));\n const refsInTotal = new Map<string, number>();\n const refsInFiles = new Map<string, number>();\n for (const { path, tags } of sorted) {\n for (const ref of tags.refs) {\n const definers = defsByName.get(ref.name);\n if (definers === undefined) continue;\n refsInTotal.set(ref.name, (refsInTotal.get(ref.name) ?? 0) + ref.count);\n refsInFiles.set(ref.name, (refsInFiles.get(ref.name) ?? 0) + 1);\n const targets = new Set(definers.map((def) => def.path));\n let crossFile = false;\n for (const target of [...targets].toSorted()) {\n if (target === path) continue;\n crossFile = true;\n outWeight.set(path, (outWeight.get(path) ?? 0) + ref.count);\n const row = edgeWeights.get(path)!;\n row.set(target, (row.get(target) ?? 0) + ref.count);\n }\n if (crossFile) refsOutByFile.set(path, (refsOutByFile.get(path) ?? 0) + ref.count);\n }\n }\n\n // PageRank, fixed iterations. Dangling files (no outgoing weight) spread their rank\n // uniformly, the standard treatment; every sum runs in sorted-path order so the\n // floating-point result is identical on every run.\n const nodeCount = paths.length;\n let rank = new Map<string, number>(\n paths.map((path) => [path, nodeCount === 0 ? 0 : 1 / nodeCount]),\n );\n for (let iteration = 0; iteration < PAGERANK_ITERATIONS; iteration += 1) {\n let danglingSum = 0;\n for (const path of paths) {\n if (outWeight.get(path) === 0) danglingSum += rank.get(path)!;\n }\n const next = new Map<string, number>();\n const base =\n nodeCount === 0\n ? 0\n : (1 - PAGERANK_DAMPING) / nodeCount + (PAGERANK_DAMPING * danglingSum) / nodeCount;\n for (const path of paths) next.set(path, base);\n for (const source of paths) {\n const total = outWeight.get(source)!;\n if (total === 0) continue;\n const share = (PAGERANK_DAMPING * rank.get(source)!) / total;\n for (const [target, weight] of edgeWeights.get(source)!) {\n next.set(target, next.get(target)! + share * weight);\n }\n }\n rank = next;\n }\n\n // Distribute each file's rank across the definitions that attracted it, exactly the\n // way Aider does: every cross-file reference edge hands the referencing file's rank,\n // scaled by the edge's share of that file's outgoing weight, to the definition it\n // points at.\n for (const { path, tags } of sorted) {\n const total = outWeight.get(path)!;\n if (total === 0) continue;\n const sourceRank = rank.get(path)!;\n for (const ref of tags.refs) {\n const definers = defsByName.get(ref.name);\n if (definers === undefined) continue;\n for (const def of definers) {\n if (def.path === path) continue;\n def.rank += (sourceRank * ref.count) / total;\n }\n }\n }\n\n const ranked: RankedDefinition[] = allDefs.map((def) => ({\n path: def.path,\n name: def.name,\n kind: def.kind,\n line: def.line,\n rank: def.rank,\n refsIn: refsInTotal.get(def.name) ?? 0,\n refsInFiles: refsInFiles.get(def.name) ?? 0,\n refsOut: refsOutByFile.get(def.path) ?? 0,\n }));\n return ranked.toSorted(compareRanked);\n}\n\n/**\n * The total order every emitted map obeys: rank descending, then the stable\n * tie-break by path, name, and line. Total, so equal-rank symbols land in the same\n * place on every run and on every machine — remove any leg of it and the map's\n * byte-for-byte determinism claim quietly dies.\n */\nfunction compareRanked(a: RankedDefinition, b: RankedDefinition): number {\n if (a.rank !== b.rank) return a.rank > b.rank ? -1 : 1;\n if (a.path !== b.path) return a.path < b.path ? -1 : 1;\n if (a.name !== b.name) return a.name < b.name ? -1 : 1;\n return a.line - b.line;\n}\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo map's view of the filesystem — the seam `buildRepoMap` reads a tree
|
|
3
|
+
* through.
|
|
4
|
+
*
|
|
5
|
+
* The map used to call `node:fs` in-line, so every claim about the *walk* — symlinks
|
|
6
|
+
* refused, binary files skipped, nothing read that the ignore list excluded — could
|
|
7
|
+
* only be checked by materialising a real tree on disk and inspecting the map that
|
|
8
|
+
* came back. That makes the cheap cases expensive to write and the awkward ones
|
|
9
|
+
* (an unreadable file, a symlink whose stat resolves its target) impossible to
|
|
10
|
+
* express portably. `decide(request, settings, cwd, statFile?)` in
|
|
11
|
+
* `src/hooks/guard-core.ts` is the counter-example this interface copies: one
|
|
12
|
+
* injectable stat turned seven shims into table-driven tests over a four-line stub.
|
|
13
|
+
*
|
|
14
|
+
* Three methods, all read-only, and that is the point: this is smelt's whole door to
|
|
15
|
+
* the tree it maps, and **there is no writer on it**. The only bytes `buildRepoMap`
|
|
16
|
+
* ever writes are the tags cache, which exists only when the caller hands in a
|
|
17
|
+
* `cacheDir` (Law 1's sibling ruling: smelt never writes outside a store or cache it
|
|
18
|
+
* was explicitly given).
|
|
19
|
+
*
|
|
20
|
+
* The interface is deliberately small and deliberately optional —
|
|
21
|
+
* {@link RepoMapOptions.reader} defaults to {@link nodeFsReader}, so the map's
|
|
22
|
+
* interface is unchanged for every caller that does not care.
|
|
23
|
+
*/
|
|
24
|
+
export interface RepoReader {
|
|
25
|
+
/** One directory's entries, in whatever order the backing store returns them. */
|
|
26
|
+
list(dir: string): readonly DirEntry[];
|
|
27
|
+
/**
|
|
28
|
+
* One file's bytes. Throws what the backing store throws; nothing is swallowed.
|
|
29
|
+
*
|
|
30
|
+
* `Uint8Array`, not `Buffer`, and for the same reason `AnswerStream` is not
|
|
31
|
+
* `NodeJS.ReadableStream`: `Buffer` is a global only a compilation that included
|
|
32
|
+
* `@types/node` has, so naming it here put an error into the shipped `.d.ts` for
|
|
33
|
+
* every consumer who builds with `skipLibCheck: false` and no node types of their
|
|
34
|
+
* own. A `Buffer` *is* a `Uint8Array`, so `nodeFsReader` and every reader that
|
|
35
|
+
* returns `readFileSync(path)` satisfies this unchanged.
|
|
36
|
+
*/
|
|
37
|
+
read(path: string): Uint8Array;
|
|
38
|
+
/**
|
|
39
|
+
* What `path` is, **without following it**: a symlink reports `isSymlink`, and a
|
|
40
|
+
* reader whose backing store has nothing at `path` reports `undefined`.
|
|
41
|
+
*/
|
|
42
|
+
stat(path: string): FileStat | undefined;
|
|
43
|
+
}
|
|
44
|
+
/** One entry of a directory listing. Only the name — {@link RepoReader.stat} says what it is. */
|
|
45
|
+
export interface DirEntry {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* What the reader knows about one path.
|
|
50
|
+
*
|
|
51
|
+
* `isDirectory` sits beside `isFile` rather than being inferred from it, because the
|
|
52
|
+
* walk must tell a directory from a fifo, a socket or a device node: "not a file"
|
|
53
|
+
* would send the walk into one, where the map has always simply skipped it.
|
|
54
|
+
*/
|
|
55
|
+
export interface FileStat {
|
|
56
|
+
/** The file's size in bytes, as the reader sees it. */
|
|
57
|
+
readonly size: number;
|
|
58
|
+
readonly isFile: boolean;
|
|
59
|
+
readonly isDirectory: boolean;
|
|
60
|
+
/** True for the link itself. A reader must never resolve a link to answer this. */
|
|
61
|
+
readonly isSymlink: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The default reader: `node:fs`, exactly the calls `buildRepoMap` used to make
|
|
65
|
+
* in-line — `readdirSync`, `lstatSync` (never `statSync`: the link, never its
|
|
66
|
+
* target), `readFileSync`.
|
|
67
|
+
*
|
|
68
|
+
* It never returns `undefined` from `stat`: an entry that vanished between the
|
|
69
|
+
* listing and the stat throws, as `lstatSync` has always done here. `undefined` is
|
|
70
|
+
* for readers whose backing store can honestly say "nothing there".
|
|
71
|
+
*/
|
|
72
|
+
export declare function nodeFsReader(): RepoReader;
|
|
73
|
+
//# sourceMappingURL=reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/repomap/reader.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,UAAU;IACzB,iFAAiF;IACjF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE,CAAC;IACvC;;;;;;;;;OASG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC1C;AAED,iGAAiG;AACjG,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,UAAU,CAkBzC","sourcesContent":["import { lstatSync, readdirSync, readFileSync } from 'node:fs';\n\n/**\n * The repo map's view of the filesystem — the seam `buildRepoMap` reads a tree\n * through.\n *\n * The map used to call `node:fs` in-line, so every claim about the *walk* — symlinks\n * refused, binary files skipped, nothing read that the ignore list excluded — could\n * only be checked by materialising a real tree on disk and inspecting the map that\n * came back. That makes the cheap cases expensive to write and the awkward ones\n * (an unreadable file, a symlink whose stat resolves its target) impossible to\n * express portably. `decide(request, settings, cwd, statFile?)` in\n * `src/hooks/guard-core.ts` is the counter-example this interface copies: one\n * injectable stat turned seven shims into table-driven tests over a four-line stub.\n *\n * Three methods, all read-only, and that is the point: this is smelt's whole door to\n * the tree it maps, and **there is no writer on it**. The only bytes `buildRepoMap`\n * ever writes are the tags cache, which exists only when the caller hands in a\n * `cacheDir` (Law 1's sibling ruling: smelt never writes outside a store or cache it\n * was explicitly given).\n *\n * The interface is deliberately small and deliberately optional —\n * {@link RepoMapOptions.reader} defaults to {@link nodeFsReader}, so the map's\n * interface is unchanged for every caller that does not care.\n */\nexport interface RepoReader {\n /** One directory's entries, in whatever order the backing store returns them. */\n list(dir: string): readonly DirEntry[];\n /**\n * One file's bytes. Throws what the backing store throws; nothing is swallowed.\n *\n * `Uint8Array`, not `Buffer`, and for the same reason `AnswerStream` is not\n * `NodeJS.ReadableStream`: `Buffer` is a global only a compilation that included\n * `@types/node` has, so naming it here put an error into the shipped `.d.ts` for\n * every consumer who builds with `skipLibCheck: false` and no node types of their\n * own. A `Buffer` *is* a `Uint8Array`, so `nodeFsReader` and every reader that\n * returns `readFileSync(path)` satisfies this unchanged.\n */\n read(path: string): Uint8Array;\n /**\n * What `path` is, **without following it**: a symlink reports `isSymlink`, and a\n * reader whose backing store has nothing at `path` reports `undefined`.\n */\n stat(path: string): FileStat | undefined;\n}\n\n/** One entry of a directory listing. Only the name — {@link RepoReader.stat} says what it is. */\nexport interface DirEntry {\n readonly name: string;\n}\n\n/**\n * What the reader knows about one path.\n *\n * `isDirectory` sits beside `isFile` rather than being inferred from it, because the\n * walk must tell a directory from a fifo, a socket or a device node: \"not a file\"\n * would send the walk into one, where the map has always simply skipped it.\n */\nexport interface FileStat {\n /** The file's size in bytes, as the reader sees it. */\n readonly size: number;\n readonly isFile: boolean;\n readonly isDirectory: boolean;\n /** True for the link itself. A reader must never resolve a link to answer this. */\n readonly isSymlink: boolean;\n}\n\n/**\n * The default reader: `node:fs`, exactly the calls `buildRepoMap` used to make\n * in-line — `readdirSync`, `lstatSync` (never `statSync`: the link, never its\n * target), `readFileSync`.\n *\n * It never returns `undefined` from `stat`: an entry that vanished between the\n * listing and the stat throws, as `lstatSync` has always done here. `undefined` is\n * for readers whose backing store can honestly say \"nothing there\".\n */\nexport function nodeFsReader(): RepoReader {\n return {\n list(dir) {\n return readdirSync(dir).map((name) => ({ name }));\n },\n read(path) {\n return readFileSync(path);\n },\n stat(path) {\n const stat = lstatSync(path);\n return {\n size: stat.size,\n isFile: stat.isFile(),\n isDirectory: stat.isDirectory(),\n isSymlink: stat.isSymbolicLink(),\n };\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* The default reader: `node:fs`, exactly the calls `buildRepoMap` used to make
|
|
4
|
+
* in-line — `readdirSync`, `lstatSync` (never `statSync`: the link, never its
|
|
5
|
+
* target), `readFileSync`.
|
|
6
|
+
*
|
|
7
|
+
* It never returns `undefined` from `stat`: an entry that vanished between the
|
|
8
|
+
* listing and the stat throws, as `lstatSync` has always done here. `undefined` is
|
|
9
|
+
* for readers whose backing store can honestly say "nothing there".
|
|
10
|
+
*/
|
|
11
|
+
export function nodeFsReader() {
|
|
12
|
+
return {
|
|
13
|
+
list(dir) {
|
|
14
|
+
return readdirSync(dir).map((name) => ({ name }));
|
|
15
|
+
},
|
|
16
|
+
read(path) {
|
|
17
|
+
return readFileSync(path);
|
|
18
|
+
},
|
|
19
|
+
stat(path) {
|
|
20
|
+
const stat = lstatSync(path);
|
|
21
|
+
return {
|
|
22
|
+
size: stat.size,
|
|
23
|
+
isFile: stat.isFile(),
|
|
24
|
+
isDirectory: stat.isDirectory(),
|
|
25
|
+
isSymlink: stat.isSymbolicLink(),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/repomap/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAmE/D;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,IAAI,CAAC,GAAG;YACN,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,IAAI;YACP,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,IAAI;YACP,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;gBACrB,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;gBAC/B,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE;aACjC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { lstatSync, readdirSync, readFileSync } from 'node:fs';\n\n/**\n * The repo map's view of the filesystem — the seam `buildRepoMap` reads a tree\n * through.\n *\n * The map used to call `node:fs` in-line, so every claim about the *walk* — symlinks\n * refused, binary files skipped, nothing read that the ignore list excluded — could\n * only be checked by materialising a real tree on disk and inspecting the map that\n * came back. That makes the cheap cases expensive to write and the awkward ones\n * (an unreadable file, a symlink whose stat resolves its target) impossible to\n * express portably. `decide(request, settings, cwd, statFile?)` in\n * `src/hooks/guard-core.ts` is the counter-example this interface copies: one\n * injectable stat turned seven shims into table-driven tests over a four-line stub.\n *\n * Three methods, all read-only, and that is the point: this is smelt's whole door to\n * the tree it maps, and **there is no writer on it**. The only bytes `buildRepoMap`\n * ever writes are the tags cache, which exists only when the caller hands in a\n * `cacheDir` (Law 1's sibling ruling: smelt never writes outside a store or cache it\n * was explicitly given).\n *\n * The interface is deliberately small and deliberately optional —\n * {@link RepoMapOptions.reader} defaults to {@link nodeFsReader}, so the map's\n * interface is unchanged for every caller that does not care.\n */\nexport interface RepoReader {\n /** One directory's entries, in whatever order the backing store returns them. */\n list(dir: string): readonly DirEntry[];\n /**\n * One file's bytes. Throws what the backing store throws; nothing is swallowed.\n *\n * `Uint8Array`, not `Buffer`, and for the same reason `AnswerStream` is not\n * `NodeJS.ReadableStream`: `Buffer` is a global only a compilation that included\n * `@types/node` has, so naming it here put an error into the shipped `.d.ts` for\n * every consumer who builds with `skipLibCheck: false` and no node types of their\n * own. A `Buffer` *is* a `Uint8Array`, so `nodeFsReader` and every reader that\n * returns `readFileSync(path)` satisfies this unchanged.\n */\n read(path: string): Uint8Array;\n /**\n * What `path` is, **without following it**: a symlink reports `isSymlink`, and a\n * reader whose backing store has nothing at `path` reports `undefined`.\n */\n stat(path: string): FileStat | undefined;\n}\n\n/** One entry of a directory listing. Only the name — {@link RepoReader.stat} says what it is. */\nexport interface DirEntry {\n readonly name: string;\n}\n\n/**\n * What the reader knows about one path.\n *\n * `isDirectory` sits beside `isFile` rather than being inferred from it, because the\n * walk must tell a directory from a fifo, a socket or a device node: \"not a file\"\n * would send the walk into one, where the map has always simply skipped it.\n */\nexport interface FileStat {\n /** The file's size in bytes, as the reader sees it. */\n readonly size: number;\n readonly isFile: boolean;\n readonly isDirectory: boolean;\n /** True for the link itself. A reader must never resolve a link to answer this. */\n readonly isSymlink: boolean;\n}\n\n/**\n * The default reader: `node:fs`, exactly the calls `buildRepoMap` used to make\n * in-line — `readdirSync`, `lstatSync` (never `statSync`: the link, never its\n * target), `readFileSync`.\n *\n * It never returns `undefined` from `stat`: an entry that vanished between the\n * listing and the stat throws, as `lstatSync` has always done here. `undefined` is\n * for readers whose backing store can honestly say \"nothing there\".\n */\nexport function nodeFsReader(): RepoReader {\n return {\n list(dir) {\n return readdirSync(dir).map((name) => ({ name }));\n },\n read(path) {\n return readFileSync(path);\n },\n stat(path) {\n const stat = lstatSync(path);\n return {\n size: stat.size,\n isFile: stat.isFile(),\n isDirectory: stat.isDirectory(),\n isSymlink: stat.isSymbolicLink(),\n };\n },\n };\n}\n"]}
|