@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 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
- # Temporary Holding Version
1
+ # @any-nav/scan
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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
@@ -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 (`[![alt](img)](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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"}