@any-nav/scan 0.1.0 → 0.2.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.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Read pipeline for descriptions — the machinery both entry points share
3
+ * (file split 2026-10-24): the bounded in-flight read window, the bounded
4
+ * head ladder that provably covers the extraction chain's input (frontmatter
5
+ * plus the body-head window, extended adaptively, never more bytes than the
6
+ * chain can consume), the walk-order read events, and the settle that lands
7
+ * a description — or the unreadable-file / no-description paths — on an
8
+ * item. The extraction chain itself lives in description.ts.
9
+ */
10
+ import { open } from 'node:fs/promises';
11
+ import { extractDescription, BODY_HEAD_LINE_LIMIT } from './description.js';
12
+ export const builtinDescriptionProvider = {
13
+ extract: (file) => Promise.resolve(extractDescription(file.content)),
14
+ };
15
+ /** Read fan-out ceiling: the 2026-10-20 probe saturated at 8 lanes (~4.3x,
16
+ * 39→9ms over 177 files) with nothing at 16 — the window keeps stragglers
17
+ * from stalling the pipeline and bounds transient memory to the window's
18
+ * in-flight file heads. */
19
+ const MAX_INFLIGHT_READS = 16;
20
+ /** Bounded in-flight read window: kicks never reject (read errors resolve
21
+ * null — the unreadable-file path), and the gate waits for the next settle
22
+ * before kicking past the ceiling. gate() then start() keeps the started
23
+ * promise unwrapped for fire-and-continue callers. */
24
+ export class BoundedReads {
25
+ pending = [];
26
+ /** Wait for a window slot (no-op while under the ceiling). */
27
+ async gate() {
28
+ if (this.pending.length >= MAX_INFLIGHT_READS) {
29
+ await Promise.race(this.pending);
30
+ }
31
+ }
32
+ /** Start one read and return its still-running promise. */
33
+ start(abs) {
34
+ const content = readDescriptionInput(abs);
35
+ this.pending.push(content);
36
+ void content.then(() => {
37
+ const index = this.pending.indexOf(content);
38
+ if (index !== -1)
39
+ this.pending.splice(index, 1);
40
+ });
41
+ return content;
42
+ }
43
+ }
44
+ /** First head rung and the extension factor: extraction reads frontmatter
45
+ * plus a 15-line body-head window (fences/HTML comments stripped inside
46
+ * it), so a head closing the frontmatter and showing 16+ open body lines
47
+ * with nothing dangling at the cut is provably equivalent to the full file
48
+ * for the chain — the ladder only climbs for long frontmatter or
49
+ * fence/comment-heavy heads. */
50
+ const DESC_HEAD_BYTES = 16 * 1024;
51
+ const DESC_HEAD_LADDER = 4;
52
+ /**
53
+ * Read the file head the description chain consumes — never more bytes than
54
+ * the chain can read, provably equivalent input or the ladder climbs (the
55
+ * whole file is the terminal rung). Any I/O error resolves null (the
56
+ * unreadable-file path); the full readFile this replaced had the same
57
+ * contract with far more bytes.
58
+ */
59
+ async function readDescriptionInput(abs) {
60
+ let handle;
61
+ try {
62
+ handle = await open(abs);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ try {
68
+ let capacity = DESC_HEAD_BYTES;
69
+ for (;;) {
70
+ const bytes = await readUpTo(handle, capacity);
71
+ const text = bytes.toString('utf8');
72
+ if (bytes.length < capacity || headCoversExtraction(text))
73
+ return text;
74
+ capacity *= DESC_HEAD_LADDER;
75
+ }
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ finally {
81
+ await handle.close().catch(() => { });
82
+ }
83
+ }
84
+ async function readUpTo(handle, capacity) {
85
+ const buffer = Buffer.alloc(capacity);
86
+ let filled = 0;
87
+ while (filled < capacity) {
88
+ const { bytesRead } = await handle.read(buffer, filled, capacity - filled, filled);
89
+ if (bytesRead === 0)
90
+ break;
91
+ filled += bytesRead;
92
+ }
93
+ return filled === capacity ? buffer : buffer.subarray(0, filled);
94
+ }
95
+ /**
96
+ * Whether a head provably covers every byte extractDescription reads:
97
+ * frontmatter closed when opened; past it, at least BODY_HEAD_LINE_LIMIT + 1
98
+ * open lines (the chain slices 15 — the +1 guarantees the 15th ended with a
99
+ * newline and is complete) with no code fence or HTML comment left dangling
100
+ * at the cut (mirrors stripCodeFences / stripHtmlComments state shapes; a
101
+ * dangling marker means later content could still change the strip).
102
+ */
103
+ function headCoversExtraction(text) {
104
+ const lines = text.split(/\r?\n/u);
105
+ let start = 0;
106
+ if (lines[0]?.trim() === '---') {
107
+ const end = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
108
+ if (end === -1)
109
+ return false;
110
+ start = end + 1;
111
+ }
112
+ let inFence = false;
113
+ let inComment = false;
114
+ let openLines = 0;
115
+ for (let index = start; index < lines.length; index += 1) {
116
+ const line = lines[index];
117
+ if (/^\s*(`{3,}|~{3,})/u.test(line)) {
118
+ inFence = !inFence;
119
+ continue;
120
+ }
121
+ if (inFence)
122
+ continue;
123
+ let rest = line;
124
+ while (rest.length > 0) {
125
+ if (!inComment) {
126
+ const opens = rest.indexOf('<!--');
127
+ if (opens === -1)
128
+ break;
129
+ rest = rest.slice(opens + 4);
130
+ inComment = true;
131
+ }
132
+ else {
133
+ const closes = rest.indexOf('-->');
134
+ if (closes === -1) {
135
+ rest = '';
136
+ break;
137
+ }
138
+ rest = rest.slice(closes + 3);
139
+ inComment = false;
140
+ }
141
+ }
142
+ openLines += 1;
143
+ }
144
+ return !inFence && !inComment && openLines >= BODY_HEAD_LINE_LIMIT + 1;
145
+ }
146
+ /** Settle one started read: the description lands on the item and any
147
+ * per-file diagnostic (unreadable-file / no-description placeholder) goes
148
+ * to the sink at the caller's position. */
149
+ export async function settleDescription(read, provider, sink) {
150
+ const content = await read.content;
151
+ if (content === null) {
152
+ // routes port: an unreadable Markdown file still becomes a bare-path
153
+ // route (diagnostic fires, the entry survives) — it must not silently
154
+ // vanish from navigation, and it still consumes the maxFiles budget.
155
+ sink.push({
156
+ code: 'unreadable-file',
157
+ severity: 'warning',
158
+ message: `Unable to read Markdown file ${read.rel}.`,
159
+ path: read.rel,
160
+ audience: 'maintainer',
161
+ });
162
+ read.item.description = `Undocumented — unreadable file ${read.rel}`;
163
+ return;
164
+ }
165
+ const extracted = await provider.extract({ path: read.rel, content });
166
+ read.item.description =
167
+ extracted !== null && extracted.trim() !== '' ? extracted : placeholder(read.rel, sink);
168
+ }
169
+ function placeholder(relPath, diagnostics) {
170
+ diagnostics.push({
171
+ code: 'no-description',
172
+ severity: 'warning',
173
+ // The code states the condition; the message carries only the datum
174
+ // (the WARN | code | source=... convention — no restating sentence).
175
+ message: relPath,
176
+ path: relPath,
177
+ audience: 'maintainer',
178
+ });
179
+ return `Undocumented — add a description to ${relPath}`;
180
+ }
181
+ //# sourceMappingURL=hydration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydration.js","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,16 +2,27 @@
2
2
  * @any-nav/scan public API — the lean engine face.
3
3
  *
4
4
  * Boundary rules this surface must preserve:
5
- * 1. zero runtime dependencies beyond node builtins — embedders install
6
- * nothing else (this package exists so they never carry the SDK's
7
- * smol-toml / fast-bm25);
5
+ * 1. one runtime dependency by ruling — @catheadowl/pathscope, the shared
6
+ * file-scope engine the walk delegates to. That delegation replaced the
7
+ * zero-dependency premise: embedders carry pathscope
8
+ * and its three deps (ignore/fdir/find-up) in the install closure, and
9
+ * no other dependency may join them;
8
10
  * 2. read-only over the filesystem — scanning never modifies sources
9
11
  * (map is not territory);
10
12
  * 3. terminal options: ResolvedScanOptions is what it says — callers
11
13
  * hand the scanner final values; the additive default floor is a
12
- * convenience (resolveScanOptions), not an obligation.
14
+ * convenience (resolveScanOptions), not an obligation;
15
+ * 4. API shell policy: export what real consumption forms
16
+ * need — a consumption form is someone importing today, a local
17
+ * fork being the unwired equivalent — never speculative breadth;
18
+ * each future export passes a light ruling first.
13
19
  */
14
20
  export type { DiagnosticAudience, NavItem, NavLocator, NavLocatorKind, NavNode, ScanConfig, ScanDiagnostic, } from './types.ts';
15
- export type { DescriptionProvider, ResolvedScanOptions, ScanOutput, ScannedFile, StructureScanOutput, } from './scan.ts';
16
- export { builtinDescriptionProvider, DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_FILES, DIR_SIDECAR_NAMES, isDirSidecarName, isExcludedFileName, resolveScanOptions, scanSource, scanStructure, } from './scan.ts';
21
+ export type { ResolvedScanOptions } from './engine-mapping.ts';
22
+ export type { DescriptionProvider, ScannedFile, } from './hydration.ts';
23
+ export type { ScanOutput, StructureScanOutput, } from './scan.ts';
24
+ export { builtinDescriptionProvider, } from './hydration.ts';
25
+ export { DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_FILES, resolveScanOptions, } from './engine-mapping.ts';
26
+ export { DIR_DESCRIPTION_CARRIERS, isDirDescriptionCarrier, isExcludedFileName, } from './tree-replay.ts';
27
+ export { scanSource, scanStructure, } from './scan.ts';
17
28
  export { extractDescription } from './description.ts';
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
- export { builtinDescriptionProvider, DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_FILES, DIR_SIDECAR_NAMES, isDirSidecarName, isExcludedFileName, resolveScanOptions, scanSource, scanStructure, } from './scan.js';
1
+ export { builtinDescriptionProvider, } from './hydration.js';
2
+ export { DEFAULT_EXCLUDE_DIRS, DEFAULT_EXCLUDE_FILES, resolveScanOptions, } from './engine-mapping.js';
3
+ export { DIR_DESCRIPTION_CARRIERS, isDirDescriptionCarrier, isExcludedFileName, } from './tree-replay.js';
4
+ export { scanSource, scanStructure, } from './scan.js';
2
5
  export { extractDescription } from './description.js';
3
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqCA,OAAO,EACL,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/scan.d.ts CHANGED
@@ -1,55 +1,19 @@
1
- import type { NavNode, ScanConfig, ScanDiagnostic } from './types.ts';
2
- /**
3
- * Agent-instruction files harnesses load on their own; they carry no
4
- * navigation value (the agent already has them) so they never appear in
5
- * navigation (routes DEFAULT_EXCLUDE_FILES, ported).
6
- */
7
- export declare const DEFAULT_EXCLUDE_FILES: readonly string[];
8
- /** routes DEFAULT_EXCLUDE_DIRS, ported verbatim. Always on (the safety
9
- * floor); user excludeDirs entries append to it, never replace it — a
10
- * generate-era 'any-nav' entry was retired with `list --write` (the
11
- * materialization target is now any file the caller names). */
12
- export declare const DEFAULT_EXCLUDE_DIRS: readonly string[];
13
- export interface ResolvedScanOptions {
14
- excludeDirs: readonly string[];
15
- excludeFiles: readonly string[];
16
- excludeDotEntries: boolean;
17
- respectGitignore: boolean;
18
- maxFiles: number;
19
- }
20
- export declare function resolveScanOptions(config?: ScanConfig): ResolvedScanOptions;
21
- export interface ScannedFile {
22
- path: string;
23
- content: string;
24
- }
25
- /** Port: built-in chain is the ported routes extraction; LLM providers arrive in v1.x. */
26
- export interface DescriptionProvider {
27
- extract(file: ScannedFile): Promise<string | null>;
28
- }
1
+ import { type ResolvedScanOptions } from './engine-mapping.ts';
2
+ import { type DescriptionProvider } from './hydration.ts';
3
+ import type { NavNode, ScanDiagnostic } from './types.ts';
29
4
  export interface ScanOutput {
30
5
  tree: NavNode;
31
6
  itemCount: number;
32
7
  diagnostics: ScanDiagnostic[];
33
8
  /**
34
9
  * Dir paths (walk order) whose descent the maxFiles budget cut short —
35
- * the cut chain (fact-safe cut, ADR-0010 U12): the folder where the
10
+ * the cut chain (fact-safe cut): the folder where the
36
11
  * budget flipped plus every surviving ancestor. Their recursive counts
37
12
  * are floors, never totals; views render ≥N and the walk events carry a
38
13
  * per-row diagnostic. Identical for the structure walk.
39
14
  */
40
15
  countIncompletePaths: string[];
41
16
  }
42
- export declare const builtinDescriptionProvider: DescriptionProvider;
43
- export declare function isExcludedFileName(name: string, excludeFiles: readonly string[]): boolean;
44
- /**
45
- * Directory description sidecars (2026-10-07): the Docusaurus ecosystem
46
- * names — a folder may state its navigation description in one of these
47
- * instead of its README (release-face READMEs stay untouched). The first
48
- * existing name in this order is authoritative; the files are never Markdown
49
- * so they never enter the tree, and they never consume the maxFiles budget.
50
- */
51
- export declare const DIR_SIDECAR_NAMES: readonly string[];
52
- export declare function isDirSidecarName(name: string): boolean;
53
17
  export declare function scanSource(root: string, provider?: DescriptionProvider, options?: ResolvedScanOptions): Promise<ScanOutput>;
54
18
  export interface StructureScanOutput {
55
19
  tree: NavNode;
@@ -60,13 +24,13 @@ export interface StructureScanOutput {
60
24
  /**
61
25
  * Dir paths whose descent the maxFiles budget cut short — counts are
62
26
  * floors (≥ rows, per-row diagnostics already in `diagnostics`);
63
- * identical to scanSource's list (same cut point, U11/U12).
27
+ * identical to scanSource's list (same cut point).
64
28
  */
65
29
  countIncompletePaths: string[];
66
30
  /**
67
31
  * Hydrate descriptions for the given source-relative Markdown paths, in
68
32
  * place: file nodes gain their NavItem, and folders fold descriptions
69
- * (sidecar ?? own README chain, identical to a full scan) once their
33
+ * (carrier ?? own README chain, identical to a full scan) once their
70
34
  * README hydrates. Returns the per-file diagnostics in walk order.
71
35
  * Idempotent — already-hydrated paths are skipped; unknown paths are
72
36
  * ignored. Reads fetch bounded heads under the same in-flight window.