@any-nav/scan 0.0.0-stage → 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.
- package/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/README.md +126 -2
- package/dist/budget.d.ts +26 -0
- package/dist/budget.js +25 -0
- package/dist/budget.js.map +1 -0
- package/dist/description.d.ts +44 -0
- package/dist/description.js +292 -0
- package/dist/description.js.map +1 -0
- package/dist/engine-mapping.d.ts +48 -0
- package/dist/engine-mapping.js +127 -0
- package/dist/engine-mapping.js.map +1 -0
- package/dist/hydration.d.ts +49 -0
- package/dist/hydration.js +181 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/scan.d.ts +46 -0
- package/dist/scan.js +154 -0
- package/dist/scan.js.map +1 -0
- package/dist/tree-replay.d.ts +61 -0
- package/dist/tree-replay.js +256 -0
- package/dist/tree-replay.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-10-31
|
|
4
|
+
|
|
5
|
+
- The walk engine is delegated to `@catheadowl/pathscope` (facade
|
|
6
|
+
transition): `walkScope` answers "which files are in scope" — the
|
|
7
|
+
gitignore cascade (now inherited above the scan root, git-native), the
|
|
8
|
+
mounted exclusion floor, the dot category, boundary stones — while every
|
|
9
|
+
Markdown-shaped concern (the tree, the maxFiles budget and its fact-safe
|
|
10
|
+
cut, pruning, description chains) stays here. Public API and behavior
|
|
11
|
+
stand; the delegation ends the package split's zero-dependency premise —
|
|
12
|
+
`@catheadowl/pathscope` (^0.1.1) is now the package's one runtime
|
|
13
|
+
dependency.
|
|
14
|
+
- Accepted semantic increments with the delegation: the default floor now
|
|
15
|
+
also excludes `vendor/` and `coverage/` (engine presets; a `.gitignore`
|
|
16
|
+
negation naming the directory is the exit key), gitignore inheritance
|
|
17
|
+
reaches above the scan root, a subtree carrying its own `.git` is foreign
|
|
18
|
+
territory (truncated whole; `boundaryExempt` registers named subtrees
|
|
19
|
+
back in, below), and `unreadable-directory` diagnostics are gone
|
|
20
|
+
(unreadable directories are skipped engine-side). A narrowed terminal
|
|
21
|
+
floor keeps exact name semantics — nothing is force-mounted.
|
|
22
|
+
- `boundaryExempt`: `[scan]` and `ScanConfig` gain the named-subtree
|
|
23
|
+
core-stone exemption key — gitignore-style patterns anchored at the scan
|
|
24
|
+
root (e.g. `deepseek-harness/`) naming subtrees whose own nested `.git`
|
|
25
|
+
no longer cuts them from the view, passed verbatim to the engine's
|
|
26
|
+
`boundaryStones.exempt` (git's submodule-registration face: the subtree
|
|
27
|
+
descends as a native subtree with its nested `.gitignore` rules honored,
|
|
28
|
+
deeper embedded repos inside it still cut, registration is not rescue).
|
|
29
|
+
`ResolvedScanOptions.boundaryExempt?` is an optional field —
|
|
30
|
+
pre-existing terminal callers compile unchanged, and the default
|
|
31
|
+
behavior is byte-identical (no exemptions).
|
|
32
|
+
- The in-package gitignore engine (`gitignore.ts` and the
|
|
33
|
+
traversal-boundary family exports `GitignoreRule`,
|
|
34
|
+
`readInheritedGitignoreRules`, `readGitignoreRules`, `isGitignored`) is
|
|
35
|
+
removed outright, no deprecation window — breaking: after the delegation
|
|
36
|
+
it had zero importing consumers, and its fixture corpus has already been
|
|
37
|
+
absorbed into the shared engine's conformance suite. If a "read boundary
|
|
38
|
+
rules without a scan" consumption form ever materializes, the right home
|
|
39
|
+
is a public `@catheadowl/pathscope` API, not a private engine copy
|
|
40
|
+
inside a consumer.
|
|
41
|
+
- Folder descriptions now read standardized carriers in descending order
|
|
42
|
+
of standardization: the directory's own `package.json` `description`
|
|
43
|
+
first, then its `manifest.yml` `metadata.description`, else its own
|
|
44
|
+
`README.md` as before. Per stage, present-but-empty or key-less falls
|
|
45
|
+
through to the next; an unreadable or invalid carrier fires the
|
|
46
|
+
maintainer diagnostic `description-carrier-unreadable` and falls
|
|
47
|
+
through the same way. The carriers are never Markdown, so they never
|
|
48
|
+
enter the tree or consume the maxFiles budget. Export face:
|
|
49
|
+
`DIR_SIDECAR_NAMES` / `isDirSidecarName` become
|
|
50
|
+
`DIR_DESCRIPTION_CARRIERS` / `isDirDescriptionCarrier` — breaking for
|
|
51
|
+
direct importers of the renamed exports.
|
|
52
|
+
- `NavNode.markdown`: dir nodes, root included, carry the representative
|
|
53
|
+
README child's POSIX-relative path, present iff that README is in the
|
|
54
|
+
tree — consumers read a folder's front page without re-deriving which
|
|
55
|
+
child is the README.
|
|
56
|
+
- `scan.ts` is split into its four identifiable responsibilities:
|
|
57
|
+
`engine-mapping.ts` (the options/preset mapping and the walkScope
|
|
58
|
+
bridge), `tree-replay.ts` (the name-sorted DFS replay with pruning and
|
|
59
|
+
the fact-safe-cut chain), `budget.ts` (the maxFiles state shape and cut
|
|
60
|
+
diagnostics), and `hydration.ts` (the bounded read pipeline and the
|
|
61
|
+
description-provider face), while `scan.ts` keeps the dual entry points
|
|
62
|
+
and their output faces. Pure internal move — the public API and the
|
|
63
|
+
byte-level replay promises are untouched.
|
|
64
|
+
|
|
65
|
+
## 0.1.0 — 2026-10-21
|
|
66
|
+
|
|
67
|
+
First registry release: the zero-dependency scan engine, split out of
|
|
68
|
+
`@any-nav/core` (2026-10-19) as the lean consumption face for embedders.
|
|
69
|
+
|
|
70
|
+
- `scanSource()` walks a Markdown folder into a `NavNode` tree under the
|
|
71
|
+
routes traversal semantics: additive exclusion lists over an always-on
|
|
72
|
+
built-in floor, `.gitignore` inheritance, a loud `maxFiles` budget
|
|
73
|
+
(diagnostic, never a silent cut), empty-directory pruning, and folder
|
|
74
|
+
descriptions via the `_category_` sidecar → README chain.
|
|
75
|
+
- Budget cuts are disclosed as lower bounds: folders cut by the cap carry
|
|
76
|
+
floor counts (`countIncompletePaths` on the output; views render ≥N
|
|
77
|
+
with a per-row `count-truncated-at-limit` diagnostic) — surviving rows
|
|
78
|
+
never overstate completeness.
|
|
79
|
+
- `scanStructure()` runs the identical walk with zero content reads: file
|
|
80
|
+
nodes carry no `NavItem` until `describe(paths)` hydrates exactly the
|
|
81
|
+
rendered rows in place; bounded-head reads keep extraction identical to
|
|
82
|
+
a full read.
|
|
83
|
+
- Walk throughput: overlapped directory reads and memoized `.gitignore`
|
|
84
|
+
matching.
|
|
85
|
+
- Description extraction: frontmatter `description:` → document-head
|
|
86
|
+
`description:` line → first substantive body line → placeholder +
|
|
87
|
+
`no-description` diagnostic.
|
|
88
|
+
- Terminal `ResolvedScanOptions` with `resolveScanOptions` as the additive
|
|
89
|
+
default floor; `builtinDescriptionProvider` as the default
|
|
90
|
+
`DescriptionProvider` port implementation.
|
|
91
|
+
- The knowledge model: `NavItem { name, description, locator }` plus
|
|
92
|
+
`NavNode`, `ScanConfig`, and audience-tagged `ScanDiagnostic`s.
|
|
93
|
+
- Zero npm dependencies — the isolated install closure is this package alone.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CatheadOwl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @any-nav/scan
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Walk any Markdown folder into a navigation tree.
|
|
4
|
+
|
|
5
|
+
`@any-nav/scan` is the lean scan engine behind Any-Nav: it reads a folder of
|
|
6
|
+
Markdown files and produces a navigation tree — names, one-line descriptions,
|
|
7
|
+
and file locators — ready for listings, indexes, and agent navigation
|
|
8
|
+
surfaces. The walk's scope resolution — gitignore cascade, exclusion floors,
|
|
9
|
+
the dot category, boundary stones — is delegated to
|
|
10
|
+
[`@catheadowl/pathscope`](https://www.npmjs.com/package/@catheadowl/pathscope)
|
|
11
|
+
(one runtime dependency); everything Markdown-shaped — the tree, the budget,
|
|
12
|
+
pruning, description chains — lives here. The full SDK (config, budget guard,
|
|
13
|
+
rendering, query) lives in `@any-nav/core`, which composes and re-exports
|
|
14
|
+
this package's full face.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @any-nav/scan
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { builtinDescriptionProvider, resolveScanOptions, scanSource } from '@any-nav/scan';
|
|
26
|
+
|
|
27
|
+
const output = await scanSource(
|
|
28
|
+
'docs', // the folder to walk (absolute or relative)
|
|
29
|
+
builtinDescriptionProvider, // description chain; this is the default, shown for clarity
|
|
30
|
+
resolveScanOptions({ maxFiles: 2000, respectGitignore: true }),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
output.tree; // NavNode tree — directories are nodes, files carry a NavItem
|
|
34
|
+
output.itemCount; // number of Markdown items read
|
|
35
|
+
output.diagnostics; // loud notes (budget hit, no-description files, …) — never a silent cut
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The knowledge model is one shape: `NavItem { name, description, locator }`
|
|
39
|
+
(`name` defaults to the file stem; `locator.path` is POSIX-relative to the
|
|
40
|
+
scan root). There is no second model — the SDK's view types build on it, never
|
|
41
|
+
beside it.
|
|
42
|
+
|
|
43
|
+
### Structure-only walks (lazy descriptions)
|
|
44
|
+
|
|
45
|
+
When a consumer renders a slice of the tree (a depth-0 listing shows only
|
|
46
|
+
its own rows), reading every file up front is wasted work. `scanStructure`
|
|
47
|
+
runs the identical walk — exclusions, gitignore, budget, pruning — with
|
|
48
|
+
zero content reads; file nodes carry no `NavItem` until `describe(paths)`
|
|
49
|
+
hydrates exactly the ones a view renders, in place:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { resolveScanOptions, scanStructure } from '@any-nav/scan';
|
|
53
|
+
|
|
54
|
+
const structure = await scanStructure('docs', undefined, resolveScanOptions());
|
|
55
|
+
structure.tree; // same shape a full scan produces; file items absent
|
|
56
|
+
structure.itemCount; // identical to a full scan (the budget counts entries)
|
|
57
|
+
structure.diagnostics; // walk findings; per-file diagnostics ride describe()
|
|
58
|
+
const perFile = await structure.describe(new Set(['README.md', 'guides/README.md']));
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Reads fetch a bounded head that provably covers the description chain's
|
|
62
|
+
input (frontmatter plus the body-head window, extended adaptively when a
|
|
63
|
+
head cannot prove stability) — extraction results are identical to a full
|
|
64
|
+
read, at a fraction of the bytes.
|
|
65
|
+
|
|
66
|
+
## Scan semantics
|
|
67
|
+
|
|
68
|
+
- **Additive exclusions** — `excludeDirs` / `excludeFiles` append to an
|
|
69
|
+
always-on built-in floor (e.g. `AGENTS.md`, `CLAUDE.md`, `node_modules`,
|
|
70
|
+
`dist`, and — with the engine delegation — `vendor` and `coverage`); stating
|
|
71
|
+
one never drops the defaults. Dot entries are skipped by default. Empty
|
|
72
|
+
directories are pruned from the tree.
|
|
73
|
+
- **gitignore inheritance** — `respectGitignore` (default on): traversal
|
|
74
|
+
honors the `.gitignore` cascade, nested files and the chain above the scan
|
|
75
|
+
root alike (git-native; the exit key for a floor entry is naming it in a
|
|
76
|
+
`.gitignore` negation, e.g. `!vendor/`).
|
|
77
|
+
- **Registered subtrees** — `boundaryExempt`: gitignore-style patterns
|
|
78
|
+
anchored at the scan root (e.g. `deepseek-harness/`) naming subtrees whose
|
|
79
|
+
own nested `.git` no longer cuts them from this view — git's
|
|
80
|
+
submodule-registration face (an unregistered embedded repo is foreign
|
|
81
|
+
territory; a registered one participates). Registration is not rescue:
|
|
82
|
+
the subtree still passes every exclusion floor, and deeper embedded
|
|
83
|
+
repositories inside it still cut. Default: none.
|
|
84
|
+
- **A loud budget** — `maxFiles` (default 2000) caps markdown entries per
|
|
85
|
+
source walk; hitting the cap fires diagnostics, never a silent truncation:
|
|
86
|
+
folders whose descent was cut carry floor counts (`countIncompletePaths`
|
|
87
|
+
on the output; views render ≥N with a per-row `count-truncated-at-limit`
|
|
88
|
+
diagnostic) and later folders are absent. The budget counts tree entries
|
|
89
|
+
(identical cut for full and structure walks).
|
|
90
|
+
- **Description source chain** — per file, in priority order: frontmatter
|
|
91
|
+
`description:`, else an explicit `description:` line in the document head,
|
|
92
|
+
else the first substantive body line; none matching falls to a placeholder
|
|
93
|
+
plus a `no-description` diagnostic (full scans emit one per file; structure
|
|
94
|
+
walks emit them per hydrated file, so a lazy view warns about the rows it
|
|
95
|
+
renders).
|
|
96
|
+
- **Folder descriptions** — a directory describes itself via its
|
|
97
|
+
standardized carriers in order: its `package.json` `description`, else
|
|
98
|
+
its `manifest.yml` `metadata.description`, else the same extraction over
|
|
99
|
+
its own `README.md`; an unreadable or invalid carrier fires a
|
|
100
|
+
`description-carrier-unreadable` diagnostic and falls through.
|
|
101
|
+
- **Representative README locator** — dir nodes carry `markdown`, the
|
|
102
|
+
POSIX-relative path of the folder's representative `README.md`; present
|
|
103
|
+
iff that README is a node in the tree (file nodes never carry it).
|
|
104
|
+
- **Read-only** — scanning never modifies source files. The map is not the
|
|
105
|
+
territory.
|
|
106
|
+
|
|
107
|
+
## Boundaries this package keeps
|
|
108
|
+
|
|
109
|
+
- One runtime dependency by ruling — `@catheadowl/pathscope`, the shared
|
|
110
|
+
file-scope engine the walk delegates to; nothing else may join it.
|
|
111
|
+
- `ResolvedScanOptions` is terminal: callers hand the scanner final values;
|
|
112
|
+
`resolveScanOptions` is an additive default floor, not an obligation.
|
|
113
|
+
- `DescriptionProvider` is a port — the built-in chain is the default; a
|
|
114
|
+
custom provider (e.g. an LLM-backed one) plugs into the same interface.
|
|
115
|
+
- API shell policy: the public surface exports what real
|
|
116
|
+
consumption forms need — someone importing today, a local fork being the
|
|
117
|
+
unwired equivalent — and never speculative breadth; each future export
|
|
118
|
+
passes a light ruling first. The former traversal-boundary family
|
|
119
|
+
(`GitignoreRule`, `readInheritedGitignoreRules`, `readGitignoreRules`,
|
|
120
|
+
`isGitignored`) was removed with the in-package engine it belonged to: it
|
|
121
|
+
had no importing consumer, and reading boundary rules without a scan is
|
|
122
|
+
engine territory — the right home is a future `@catheadowl/pathscope`
|
|
123
|
+
API, not this package.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/budget.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget cut — the nav-denominated maxFiles replay layer: the budget's
|
|
3
|
+
* unit is a TREE ENTRY and its
|
|
4
|
+
* arithmetic stays synchronous inside the replay (that is what keeps the cut
|
|
5
|
+
* point identical to the serial walk), so this module owns the shared state
|
|
6
|
+
* shape and the diagnostic vocabulary of the fact-safe cut — a partial count
|
|
7
|
+
* must never present as the total.
|
|
8
|
+
*/
|
|
9
|
+
import type { ScanDiagnostic } from './types.ts';
|
|
10
|
+
/**
|
|
11
|
+
* Shared mutable budget state (routes FileLimitState, ported): set when the
|
|
12
|
+
* maxFiles budget stops iteration while entries remain unexamined, so the
|
|
13
|
+
* caller reports the truncation loudly instead of silently dropping entries.
|
|
14
|
+
*/
|
|
15
|
+
export interface FileBudget {
|
|
16
|
+
files: number;
|
|
17
|
+
limitHit: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function fileLimitDiagnostic(maxFiles: number): ScanDiagnostic;
|
|
20
|
+
/**
|
|
21
|
+
* Per-row disclosure for one floor-count folder: the budget
|
|
22
|
+
* cut this folder's descent mid-way, so the count a view renders for it is
|
|
23
|
+
* a floor. Consumer audience — it is view fidelity, same class as
|
|
24
|
+
* file-limit-reached.
|
|
25
|
+
*/
|
|
26
|
+
export declare function countTruncatedDiagnostic(rel: string): ScanDiagnostic;
|
package/dist/budget.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function fileLimitDiagnostic(maxFiles) {
|
|
2
|
+
return {
|
|
3
|
+
code: 'file-limit-reached',
|
|
4
|
+
severity: 'warning',
|
|
5
|
+
message: `Markdown file limit (${maxFiles}) reached while scanning; entries past the limit were not examined — folders whose descent was cut carry ≥ counts (a floor, not the total) and later folders are absent. Raise scan.maxFiles for the full tree.`,
|
|
6
|
+
path: '.',
|
|
7
|
+
audience: 'consumer',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Per-row disclosure for one floor-count folder: the budget
|
|
12
|
+
* cut this folder's descent mid-way, so the count a view renders for it is
|
|
13
|
+
* a floor. Consumer audience — it is view fidelity, same class as
|
|
14
|
+
* file-limit-reached.
|
|
15
|
+
*/
|
|
16
|
+
export function countTruncatedDiagnostic(rel) {
|
|
17
|
+
return {
|
|
18
|
+
code: 'count-truncated-at-limit',
|
|
19
|
+
severity: 'warning',
|
|
20
|
+
message: `The maxFiles budget cut this folder's descent short; its shown count is a floor (at least that many), and entries past the cut point are absent.`,
|
|
21
|
+
path: rel,
|
|
22
|
+
audience: 'consumer',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../src/budget.ts"],"names":[],"mappings":"AAoBA,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO;QACL,IAAI,EAAE,oBAAoB;QAC1B,QAAQ,EAAE,SAAS;QACnB,OAAO,EAAE,wBAAwB,QAAQ,iNAAiN;QAC1P,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,UAAU;KACrB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,OAAO;QACL,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,SAAS;QACnB,OAAO,EAAE,kJAAkJ;QAC3J,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,UAAU;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-line description extraction, ported verbatim from the dsh-extras
|
|
3
|
+
* routes module (absorption is porting — every boundary handling here is
|
|
4
|
+
* tuition already paid on production corpora; do not "simplify" it).
|
|
5
|
+
*
|
|
6
|
+
* Priority: ① frontmatter `description:`, ② an explicit `description:` line
|
|
7
|
+
* in the document head, ③ the first substantive body line as a fallback.
|
|
8
|
+
* Returns null when none match.
|
|
9
|
+
*/
|
|
10
|
+
/** Body-line description lookup only trusts the document head, so a `description:` inside a code block or deep in the prose cannot hijack the route. Exported for the scan-side head-read sufficiency check (scan.ts), which must stay in lockstep with this window. */
|
|
11
|
+
export declare const BODY_HEAD_LINE_LIMIT = 15;
|
|
12
|
+
/**
|
|
13
|
+
* Extracts a one-line route description from Markdown, in priority order:
|
|
14
|
+
* ① frontmatter `description:`, ② an explicit `description:` line in the document
|
|
15
|
+
* head, ③ the first substantive body line as a fallback. Returns `null` when none match.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractDescription(content: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Directory description carriers: the folder chain sources from standardized carriers in
|
|
20
|
+
* descending order of standardization — ① the directory's own npm
|
|
21
|
+
* `package.json` `description` (nearly always present by ecosystem
|
|
22
|
+
* discipline), ② its `manifest.yml` `metadata.description` (the
|
|
23
|
+
* manifest-authoring directory-contract gloss — optional by that contract,
|
|
24
|
+
* present-only). Present-but-empty or absent falls through to the next
|
|
25
|
+
* stage; neither fabricates a description. The README stage lives in
|
|
26
|
+
* tree-replay (the chain's tail, not a carrier).
|
|
27
|
+
*/
|
|
28
|
+
export type CarrierRead = {
|
|
29
|
+
description: string | null;
|
|
30
|
+
} | {
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
/** Carrier ①: proper JSON parse; a non-object payload is as broken as invalid JSON (npm itself cannot read it — maintainer-visible). */
|
|
34
|
+
export declare function extractPackageJsonDescription(content: string): CarrierRead;
|
|
35
|
+
/**
|
|
36
|
+
* Carrier ②: the `metadata.description` gloss under the `metadata:` block —
|
|
37
|
+
* the only description key the closed manifest v1 schema defines, so the
|
|
38
|
+
* scan is anchored to that parent (a stray top-level or sibling-block
|
|
39
|
+
* `description:` is not read). YAML reuses the frontmatter ① key reader over
|
|
40
|
+
* the block (its `decription` typo tolerance rides along; manifest faces
|
|
41
|
+
* have their own validator). Lenient by nature — never errors, a shape
|
|
42
|
+
* without the key reads as `{ description: null }`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractManifestDescription(content: string): CarrierRead;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-line description extraction, ported verbatim from the dsh-extras
|
|
3
|
+
* routes module (absorption is porting — every boundary handling here is
|
|
4
|
+
* tuition already paid on production corpora; do not "simplify" it).
|
|
5
|
+
*
|
|
6
|
+
* Priority: ① frontmatter `description:`, ② an explicit `description:` line
|
|
7
|
+
* in the document head, ③ the first substantive body line as a fallback.
|
|
8
|
+
* Returns null when none match.
|
|
9
|
+
*/
|
|
10
|
+
/** Body-line description lookup only trusts the document head, so a `description:` inside a code block or deep in the prose cannot hijack the route. Exported for the scan-side head-read sufficiency check (scan.ts), which must stay in lockstep with this window. */
|
|
11
|
+
export const BODY_HEAD_LINE_LIMIT = 15;
|
|
12
|
+
/** Minimum code-point length for a body line to count as a substantive first sentence (fallback ③). Word count is avoided because CJK text has no space tokenization. */
|
|
13
|
+
const MIN_SUBSTANTIVE_LENGTH = 20;
|
|
14
|
+
/** Approximate cap (code points) for a fallback description; longer lines are cut at the last sentence boundary inside the cap. */
|
|
15
|
+
const MAX_DESCRIPTION_CHARS = 120;
|
|
16
|
+
const SENTENCE_ENDERS = ['.', '。', '!', '!', '?', '?', ';', ';'];
|
|
17
|
+
/** Link or image, tolerating one level of nesting so a badge (`[](href)`) counts as a single token. */
|
|
18
|
+
const LINK_OR_IMAGE = /!?\[(?:[^[\]]|\[[^\]]*\])*\]\([^)]*\)/gu;
|
|
19
|
+
/**
|
|
20
|
+
* Extracts a one-line route description from Markdown, in priority order:
|
|
21
|
+
* ① frontmatter `description:`, ② an explicit `description:` line in the document
|
|
22
|
+
* head, ③ the first substantive body line as a fallback. Returns `null` when none match.
|
|
23
|
+
*/
|
|
24
|
+
export function extractDescription(content) {
|
|
25
|
+
const frontmatter = parseYamlLikeFrontmatter(content);
|
|
26
|
+
const frontmatterDescription = frontmatter
|
|
27
|
+
? readDescriptionKey(frontmatter.split(/\r?\n/u))
|
|
28
|
+
: null;
|
|
29
|
+
if (frontmatterDescription)
|
|
30
|
+
return frontmatterDescription;
|
|
31
|
+
const body = frontmatter ? contentAfterFrontmatter(content) : content;
|
|
32
|
+
const openBodyLines = stripHtmlComments(stripCodeFences(body.split(/\r?\n/u))).slice(0, BODY_HEAD_LINE_LIMIT);
|
|
33
|
+
const bodyDescription = readDescriptionKey(openBodyLines);
|
|
34
|
+
if (bodyDescription)
|
|
35
|
+
return bodyDescription;
|
|
36
|
+
return firstSubstantiveLine(openBodyLines);
|
|
37
|
+
}
|
|
38
|
+
/** Fallback ③: the first body line that is prose (not Markdown structure) and long enough, cleaned of inline Markdown and capped. */
|
|
39
|
+
function firstSubstantiveLine(lines) {
|
|
40
|
+
for (let index = 0; index < lines.length; index++) {
|
|
41
|
+
const line = lines[index];
|
|
42
|
+
if (isStructuralLine(line) || isSetextTitle(lines, index))
|
|
43
|
+
continue;
|
|
44
|
+
const cleaned = cleanInlineMarkdown(line);
|
|
45
|
+
if (codePointLength(cleaned) < MIN_SUBSTANTIVE_LENGTH)
|
|
46
|
+
continue;
|
|
47
|
+
return truncateDescription(cleaned);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/** A setext heading's title line sits directly above its `===`/`---` underline; skip the title, not just the underline. */
|
|
52
|
+
function isSetextTitle(lines, index) {
|
|
53
|
+
if (index + 1 >= lines.length)
|
|
54
|
+
return false;
|
|
55
|
+
const trimmed = lines[index + 1].trim();
|
|
56
|
+
return trimmed.length > 0 && /^(={3,}|-{3,})\s*$/u.test(trimmed);
|
|
57
|
+
}
|
|
58
|
+
/** Structural Markdown lines never carry a description; they are skipped before the length test. */
|
|
59
|
+
function isStructuralLine(line) {
|
|
60
|
+
const text = line.trim();
|
|
61
|
+
if (text.length === 0)
|
|
62
|
+
return true;
|
|
63
|
+
if (/^#{1,6}(\s|$)/u.test(text))
|
|
64
|
+
return true; // ATX heading
|
|
65
|
+
if (/^(={3,}|-{3,}|\*{3,}|_{3,})$/u.test(text))
|
|
66
|
+
return true; // setext underline / horizontal rule
|
|
67
|
+
if (/^>/u.test(text))
|
|
68
|
+
return true;
|
|
69
|
+
if (/^[-+*](\s|$)/u.test(text))
|
|
70
|
+
return true; // unordered list
|
|
71
|
+
if (/^\d{1,3}\.\s/u.test(text))
|
|
72
|
+
return true; // ordered list
|
|
73
|
+
// table row / language-switch ("English | [中文]"); inline code spans are
|
|
74
|
+
// masked first — marked-up code is never table structure
|
|
75
|
+
const outsideCodeSpans = text.replace(/`[^`]*`/gu, '');
|
|
76
|
+
if (outsideCodeSpans.startsWith('|') || /\s\|\s/u.test(outsideCodeSpans))
|
|
77
|
+
return true;
|
|
78
|
+
if (/^</u.test(text))
|
|
79
|
+
return true;
|
|
80
|
+
if (isLinkOnlyLine(text))
|
|
81
|
+
return true; // badge row / nav chrome
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/** A line whose visible text is only links/images (badges, nav) carries no prose description. */
|
|
85
|
+
function isLinkOnlyLine(text) {
|
|
86
|
+
return text.length > 0 && text.replace(LINK_OR_IMAGE, '').trim().length === 0;
|
|
87
|
+
}
|
|
88
|
+
/** Light inline-Markdown cleanup so a fallback line reads as plain prose. */
|
|
89
|
+
function cleanInlineMarkdown(line) {
|
|
90
|
+
return line
|
|
91
|
+
.trim()
|
|
92
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/gu, '$1')
|
|
93
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/gu, '$1')
|
|
94
|
+
.replace(/`([^`]*)`/gu, '$1')
|
|
95
|
+
.replace(/\*\*([^*]+)\*\*/gu, '$1')
|
|
96
|
+
.replace(/__([^_]+)__/gu, '$1')
|
|
97
|
+
.replace(/\s+/g, ' ')
|
|
98
|
+
.replace(/[::]\s*$/u, '')
|
|
99
|
+
.trim();
|
|
100
|
+
}
|
|
101
|
+
/** Cut long fallback text at the last sentence boundary inside the cap; hard-cut (with a trailing `...`) when no boundary is deep enough. */
|
|
102
|
+
function truncateDescription(text) {
|
|
103
|
+
if (codePointLength(text) <= MAX_DESCRIPTION_CHARS)
|
|
104
|
+
return text;
|
|
105
|
+
const head = [...text].slice(0, MAX_DESCRIPTION_CHARS).join('');
|
|
106
|
+
const boundary = lastSentenceBoundary(head);
|
|
107
|
+
if (boundary >= 0) {
|
|
108
|
+
const cut = head.slice(0, boundary + 1).trimEnd();
|
|
109
|
+
if (codePointLength(cut) >= MIN_SUBSTANTIVE_LENGTH)
|
|
110
|
+
return cut;
|
|
111
|
+
}
|
|
112
|
+
return head.trimEnd() + '...';
|
|
113
|
+
}
|
|
114
|
+
/** Last sentence ender inside `text`, skipping abbreviation dots (`ctx.`, `e.g.`). */
|
|
115
|
+
function lastSentenceBoundary(text) {
|
|
116
|
+
for (let index = text.length - 1; index >= 0; index--) {
|
|
117
|
+
const char = text[index];
|
|
118
|
+
if (char === '.') {
|
|
119
|
+
if (isAbbreviationDot(text, index))
|
|
120
|
+
continue;
|
|
121
|
+
return index;
|
|
122
|
+
}
|
|
123
|
+
if (SENTENCE_ENDERS.includes(char))
|
|
124
|
+
return index;
|
|
125
|
+
}
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
128
|
+
function isAbbreviationDot(text, index) {
|
|
129
|
+
let letters = 0;
|
|
130
|
+
for (let prev = index - 1; prev >= 0 && prev >= index - 3; prev--) {
|
|
131
|
+
if (/[a-z]/u.test(text[prev]))
|
|
132
|
+
letters++;
|
|
133
|
+
else
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (letters === 0)
|
|
137
|
+
return false;
|
|
138
|
+
const next = index + 1 < text.length ? text[index + 1] : '';
|
|
139
|
+
return /[a-z,;):]/u.test(next);
|
|
140
|
+
}
|
|
141
|
+
function codePointLength(text) {
|
|
142
|
+
return [...text].length;
|
|
143
|
+
}
|
|
144
|
+
/** Drops fenced code blocks (```…``` and ~~~…~~~) so tool/program examples cannot leak `description:` matches. */
|
|
145
|
+
function stripCodeFences(lines) {
|
|
146
|
+
const openLines = [];
|
|
147
|
+
let insideFence = false;
|
|
148
|
+
for (const line of lines) {
|
|
149
|
+
if (isFenceLine(line)) {
|
|
150
|
+
insideFence = !insideFence;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!insideFence)
|
|
154
|
+
openLines.push(line);
|
|
155
|
+
}
|
|
156
|
+
return openLines;
|
|
157
|
+
}
|
|
158
|
+
function isFenceLine(line) {
|
|
159
|
+
return /^\s*(`{3,}|~{3,})/u.test(line);
|
|
160
|
+
}
|
|
161
|
+
/** Drops HTML comments (`<!--`…`-->`, possibly multi-line) so generated/boilerplate comment text cannot become the description. */
|
|
162
|
+
function stripHtmlComments(lines) {
|
|
163
|
+
const openLines = [];
|
|
164
|
+
let insideComment = false;
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
let rest = line;
|
|
167
|
+
let openPart = '';
|
|
168
|
+
while (rest.length > 0) {
|
|
169
|
+
if (!insideComment) {
|
|
170
|
+
const start = rest.indexOf('<!--');
|
|
171
|
+
if (start === -1) {
|
|
172
|
+
openPart += rest;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
openPart += rest.slice(0, start);
|
|
176
|
+
rest = rest.slice(start + 4);
|
|
177
|
+
insideComment = true;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const end = rest.indexOf('-->');
|
|
181
|
+
if (end === -1) {
|
|
182
|
+
rest = '';
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
rest = rest.slice(end + 3);
|
|
186
|
+
insideComment = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
openLines.push(openPart);
|
|
190
|
+
}
|
|
191
|
+
return openLines;
|
|
192
|
+
}
|
|
193
|
+
/** Carrier ①: proper JSON parse; a non-object payload is as broken as invalid JSON (npm itself cannot read it — maintainer-visible). */
|
|
194
|
+
export function extractPackageJsonDescription(content) {
|
|
195
|
+
let parsed;
|
|
196
|
+
try {
|
|
197
|
+
parsed = JSON.parse(content);
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
return { error: `invalid JSON: ${err.message}` };
|
|
201
|
+
}
|
|
202
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
203
|
+
return { error: 'package.json must be a JSON object' };
|
|
204
|
+
}
|
|
205
|
+
const description = parsed.description;
|
|
206
|
+
return {
|
|
207
|
+
description: typeof description === 'string' && description.trim() !== '' ? description.trim() : null,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Carrier ②: the `metadata.description` gloss under the `metadata:` block —
|
|
212
|
+
* the only description key the closed manifest v1 schema defines, so the
|
|
213
|
+
* scan is anchored to that parent (a stray top-level or sibling-block
|
|
214
|
+
* `description:` is not read). YAML reuses the frontmatter ① key reader over
|
|
215
|
+
* the block (its `decription` typo tolerance rides along; manifest faces
|
|
216
|
+
* have their own validator). Lenient by nature — never errors, a shape
|
|
217
|
+
* without the key reads as `{ description: null }`.
|
|
218
|
+
*/
|
|
219
|
+
export function extractManifestDescription(content) {
|
|
220
|
+
const lines = content.split(/\r?\n/u);
|
|
221
|
+
const metadataIndex = lines.findIndex((line) => /^metadata:\s*$/u.test(line));
|
|
222
|
+
if (metadataIndex === -1)
|
|
223
|
+
return { description: null };
|
|
224
|
+
const block = [];
|
|
225
|
+
for (let index = metadataIndex + 1; index < lines.length; index++) {
|
|
226
|
+
const line = lines[index];
|
|
227
|
+
if (line.trim().length === 0) {
|
|
228
|
+
if (block.length > 0)
|
|
229
|
+
break;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (!/^\s+/u.test(line))
|
|
233
|
+
break; // dedent — the metadata block ended
|
|
234
|
+
block.push(line);
|
|
235
|
+
}
|
|
236
|
+
return { description: block.length > 0 ? readDescriptionKey(block) : null };
|
|
237
|
+
}
|
|
238
|
+
function parseYamlLikeFrontmatter(content) {
|
|
239
|
+
const lines = content.split(/\r?\n/u);
|
|
240
|
+
if (lines[0]?.trim() !== '---')
|
|
241
|
+
return null;
|
|
242
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
|
|
243
|
+
if (endIndex === -1)
|
|
244
|
+
return null;
|
|
245
|
+
return lines.slice(1, endIndex).join('\n');
|
|
246
|
+
}
|
|
247
|
+
function contentAfterFrontmatter(content) {
|
|
248
|
+
const lines = content.split(/\r?\n/u);
|
|
249
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
|
|
250
|
+
return endIndex === -1 ? content : lines.slice(endIndex + 1).join('\n');
|
|
251
|
+
}
|
|
252
|
+
function readDescriptionKey(lines) {
|
|
253
|
+
for (let index = 0; index < lines.length; index++) {
|
|
254
|
+
const line = lines[index];
|
|
255
|
+
const match = line.match(/^\s*(description|decription)\s*:\s*(.*)$/iu);
|
|
256
|
+
if (!match)
|
|
257
|
+
continue;
|
|
258
|
+
const inline = stripYamlString(match[2].trim());
|
|
259
|
+
if (inline)
|
|
260
|
+
return inline;
|
|
261
|
+
const block = readIndentedBlock(lines, index + 1);
|
|
262
|
+
if (block)
|
|
263
|
+
return block;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
function readIndentedBlock(lines, start) {
|
|
268
|
+
const collected = [];
|
|
269
|
+
for (let index = start; index < lines.length; index++) {
|
|
270
|
+
const line = lines[index];
|
|
271
|
+
if (line.trim().length === 0) {
|
|
272
|
+
if (collected.length > 0)
|
|
273
|
+
break;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (!/^\s+/.test(line))
|
|
277
|
+
break;
|
|
278
|
+
collected.push(line.trim());
|
|
279
|
+
}
|
|
280
|
+
return collected.length > 0 ? collected.join(' ') : null;
|
|
281
|
+
}
|
|
282
|
+
function stripYamlString(value) {
|
|
283
|
+
const stripped = value
|
|
284
|
+
.replace(/^\|[+-]?$/u, '')
|
|
285
|
+
.replace(/^>[+-]?$/u, '')
|
|
286
|
+
.trim();
|
|
287
|
+
if (!stripped)
|
|
288
|
+
return null;
|
|
289
|
+
const quoted = stripped.match(/^(['"])(.*)\1$/u);
|
|
290
|
+
return (quoted ? quoted[2] : stripped).trim() || null;
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=description.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"description.js","sourceRoot":"","sources":["../src/description.ts"],"names":[],"mappings":"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"}
|