@refrakt-md/content 0.35.0 → 0.37.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/dist/edges/exclude.d.ts +47 -0
- package/dist/edges/exclude.d.ts.map +1 -0
- package/dist/edges/exclude.js +124 -0
- package/dist/edges/exclude.js.map +1 -0
- package/dist/edges/extract.d.ts +122 -0
- package/dist/edges/extract.d.ts.map +1 -0
- package/dist/edges/extract.js +317 -0
- package/dist/edges/extract.js.map +1 -0
- package/dist/edges/git-scan.d.ts +69 -0
- package/dist/edges/git-scan.d.ts.map +1 -0
- package/dist/edges/git-scan.js +138 -0
- package/dist/edges/git-scan.js.map +1 -0
- package/dist/edges/index.d.ts +119 -0
- package/dist/edges/index.d.ts.map +1 -0
- package/dist/edges/index.js +264 -0
- package/dist/edges/index.js.map +1 -0
- package/dist/format.d.ts +22 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +7 -20
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +25 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +1 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/loader.d.ts +15 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +23 -1
- package/dist/loader.js.map +1 -1
- package/dist/refract-loader.d.ts +23 -0
- package/dist/refract-loader.d.ts.map +1 -1
- package/dist/refract-loader.js +81 -5
- package/dist/refract-loader.js.map +1 -1
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/site.d.ts +173 -5
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +250 -133
- package/dist/site.js.map +1 -1
- package/dist/validate-content.d.ts +83 -0
- package/dist/validate-content.d.ts.map +1 -0
- package/dist/validate-content.js +143 -0
- package/dist/validate-content.js.map +1 -0
- package/dist/validate.d.ts +32 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +31 -10
- package/dist/validate.js.map +1 -1
- package/frontmatter.schema.json +7 -0
- package/package.json +9 -5
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git scan (SPEC-136 D5, WORK-594).
|
|
3
|
+
*
|
|
4
|
+
* One pass over the repository's history, producing for every path the commits
|
|
5
|
+
* that touched it. Adapted from `timestamps.ts`'s scan rather than rewritten —
|
|
6
|
+
* the shallow-clone and git-root handling there are the parts most easily got
|
|
7
|
+
* wrong.
|
|
8
|
+
*
|
|
9
|
+
* **`--full-history`, and no pathspec.** This is the single easiest way to ship
|
|
10
|
+
* this feature with quietly wrong numbers, and it was found by measuring
|
|
11
|
+
* SPEC-136's own base rate:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* git log --name-only -- site/content/docs/cli/cli-overview.md → 2 commits
|
|
15
|
+
* git log --full-history --name-only (no pathspec) → 5 commits
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A pathspec makes git apply history simplification. Both outputs are correct
|
|
19
|
+
* git answering different questions — but this command does *arithmetic* on
|
|
20
|
+
* those timestamps, since the referrer's last-changed time is one half of every
|
|
21
|
+
* score. A simplified history inflates every edge out of a file and deflates
|
|
22
|
+
* every edge into it, and the failure is invisible: the report still renders,
|
|
23
|
+
* still ranks, still looks plausible.
|
|
24
|
+
*/
|
|
25
|
+
export interface Commit {
|
|
26
|
+
sha: string;
|
|
27
|
+
/** Unix seconds. */
|
|
28
|
+
at: number;
|
|
29
|
+
subject: string;
|
|
30
|
+
}
|
|
31
|
+
export interface GitHistory {
|
|
32
|
+
/** Path (relative to the git root) → commits touching it, newest first. */
|
|
33
|
+
byPath: Map<string, Commit[]>;
|
|
34
|
+
/** Prefix from the git root to the scanned directory, '' at the root. */
|
|
35
|
+
prefix: string;
|
|
36
|
+
}
|
|
37
|
+
/** Why the scan could not run. A refusal is never a zero — D5. */
|
|
38
|
+
export declare class GitScanRefusal extends Error {
|
|
39
|
+
constructor(message: string);
|
|
40
|
+
}
|
|
41
|
+
/** A shallow clone reports one commit per path, so every edge scores zero and
|
|
42
|
+
* the tool confidently reports a clean corpus. That is the silent-wrong class
|
|
43
|
+
* this feature exists to avoid, so it refuses instead. */
|
|
44
|
+
export declare function isShallowClone(cwd: string): boolean;
|
|
45
|
+
export declare function isGitRepository(cwd: string): boolean;
|
|
46
|
+
/** Path from the git root to `cwd`, '' when already at the root. */
|
|
47
|
+
export declare function gitRelativePrefix(cwd: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Scan the whole history once.
|
|
50
|
+
*
|
|
51
|
+
* @throws {GitScanRefusal} on a non-git tree or a shallow clone. Both are
|
|
52
|
+
* "could not measure", which is a different event from "measured, nothing
|
|
53
|
+
* wrong" and must not be reported as the latter.
|
|
54
|
+
*/
|
|
55
|
+
export declare function scanHistory(cwd: string): GitHistory;
|
|
56
|
+
/** The most recent commit timestamp for a path, or undefined if untracked. */
|
|
57
|
+
export declare function lastChanged(history: GitHistory, path: string): number | undefined;
|
|
58
|
+
/** Commits touching `path` strictly after `since`, newest first. */
|
|
59
|
+
export declare function commitsSince(history: GitHistory, path: string, since: number): Commit[];
|
|
60
|
+
/**
|
|
61
|
+
* Paths changed since a git ref — the `since:` query's input.
|
|
62
|
+
*
|
|
63
|
+
* A PR-scoped sweep: resolve what the branch touched, then ask the same
|
|
64
|
+
* question `touching` asks. Returns an empty list rather than throwing when the
|
|
65
|
+
* ref cannot be resolved, because a missing ref is the caller's mistake to
|
|
66
|
+
* report, not a reason to fail the whole query.
|
|
67
|
+
*/
|
|
68
|
+
export declare function changedSince(cwd: string, ref: string): string[];
|
|
69
|
+
//# sourceMappingURL=git-scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-scan.d.ts","sourceRoot":"","sources":["../../src/edges/git-scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,MAAM,WAAW,MAAM;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;CACf;AAED,kEAAkE;AAClE,qBAAa,cAAe,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI3B;AAWD;;2DAE2D;AAC3D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMnD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAyCnD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKjF;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIvF;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAM/D"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git scan (SPEC-136 D5, WORK-594).
|
|
3
|
+
*
|
|
4
|
+
* One pass over the repository's history, producing for every path the commits
|
|
5
|
+
* that touched it. Adapted from `timestamps.ts`'s scan rather than rewritten —
|
|
6
|
+
* the shallow-clone and git-root handling there are the parts most easily got
|
|
7
|
+
* wrong.
|
|
8
|
+
*
|
|
9
|
+
* **`--full-history`, and no pathspec.** This is the single easiest way to ship
|
|
10
|
+
* this feature with quietly wrong numbers, and it was found by measuring
|
|
11
|
+
* SPEC-136's own base rate:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* git log --name-only -- site/content/docs/cli/cli-overview.md → 2 commits
|
|
15
|
+
* git log --full-history --name-only (no pathspec) → 5 commits
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A pathspec makes git apply history simplification. Both outputs are correct
|
|
19
|
+
* git answering different questions — but this command does *arithmetic* on
|
|
20
|
+
* those timestamps, since the referrer's last-changed time is one half of every
|
|
21
|
+
* score. A simplified history inflates every edge out of a file and deflates
|
|
22
|
+
* every edge into it, and the failure is invisible: the report still renders,
|
|
23
|
+
* still ranks, still looks plausible.
|
|
24
|
+
*/
|
|
25
|
+
import { execSync } from 'node:child_process';
|
|
26
|
+
/** Why the scan could not run. A refusal is never a zero — D5. */
|
|
27
|
+
export class GitScanRefusal extends Error {
|
|
28
|
+
constructor(message) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'GitScanRefusal';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function git(command, cwd) {
|
|
34
|
+
return execSync(command, {
|
|
35
|
+
cwd,
|
|
36
|
+
encoding: 'utf-8',
|
|
37
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
38
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** A shallow clone reports one commit per path, so every edge scores zero and
|
|
42
|
+
* the tool confidently reports a clean corpus. That is the silent-wrong class
|
|
43
|
+
* this feature exists to avoid, so it refuses instead. */
|
|
44
|
+
export function isShallowClone(cwd) {
|
|
45
|
+
try {
|
|
46
|
+
return git('git rev-parse --is-shallow-repository', cwd).trim() === 'true';
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function isGitRepository(cwd) {
|
|
53
|
+
try {
|
|
54
|
+
return git('git rev-parse --is-inside-work-tree', cwd).trim() === 'true';
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Path from the git root to `cwd`, '' when already at the root. */
|
|
61
|
+
export function gitRelativePrefix(cwd) {
|
|
62
|
+
const root = git('git rev-parse --show-toplevel', cwd).trim();
|
|
63
|
+
const abs = git('pwd', cwd).trim();
|
|
64
|
+
if (abs === root)
|
|
65
|
+
return '';
|
|
66
|
+
const rel = abs.slice(root.length + 1);
|
|
67
|
+
return rel ? `${rel}/` : '';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Scan the whole history once.
|
|
71
|
+
*
|
|
72
|
+
* @throws {GitScanRefusal} on a non-git tree or a shallow clone. Both are
|
|
73
|
+
* "could not measure", which is a different event from "measured, nothing
|
|
74
|
+
* wrong" and must not be reported as the latter.
|
|
75
|
+
*/
|
|
76
|
+
export function scanHistory(cwd) {
|
|
77
|
+
if (!isGitRepository(cwd)) {
|
|
78
|
+
throw new GitScanRefusal('not a git repository — `refrakt stale` measures commits against a file, and has no ' +
|
|
79
|
+
'non-git fallback. Run it inside a checkout.');
|
|
80
|
+
}
|
|
81
|
+
if (isShallowClone(cwd)) {
|
|
82
|
+
throw new GitScanRefusal('shallow clone — every path would report a single commit and every edge would score ' +
|
|
83
|
+
'zero, which is indistinguishable from a clean corpus. Set `fetch-depth: 0` on the ' +
|
|
84
|
+
'checkout step (actions/checkout defaults to 1) and run again.');
|
|
85
|
+
}
|
|
86
|
+
// `%x00` separates the subject so a subject containing the field separator
|
|
87
|
+
// cannot split the record.
|
|
88
|
+
const output = git('git log --full-history --format="__C__%H%x00%at%x00%s" --name-only --diff-filter=ACMR HEAD', cwd);
|
|
89
|
+
const byPath = new Map();
|
|
90
|
+
let current;
|
|
91
|
+
for (const raw of output.split('\n')) {
|
|
92
|
+
const line = raw.replace(/\r$/, '');
|
|
93
|
+
if (line.startsWith('__C__')) {
|
|
94
|
+
const [sha, at, ...rest] = line.slice(5).split('\u0000');
|
|
95
|
+
current = { sha: sha.slice(0, 7), at: Number(at), subject: rest.join('\u0000') };
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (line.length === 0 || current === undefined)
|
|
99
|
+
continue;
|
|
100
|
+
const list = byPath.get(line);
|
|
101
|
+
if (list)
|
|
102
|
+
list.push(current);
|
|
103
|
+
else
|
|
104
|
+
byPath.set(line, [current]);
|
|
105
|
+
}
|
|
106
|
+
return { byPath, prefix: gitRelativePrefix(cwd) };
|
|
107
|
+
}
|
|
108
|
+
/** The most recent commit timestamp for a path, or undefined if untracked. */
|
|
109
|
+
export function lastChanged(history, path) {
|
|
110
|
+
const commits = history.byPath.get(path);
|
|
111
|
+
if (!commits || commits.length === 0)
|
|
112
|
+
return undefined;
|
|
113
|
+
// `git log` emits newest first, so the head is the most recent.
|
|
114
|
+
return commits[0].at;
|
|
115
|
+
}
|
|
116
|
+
/** Commits touching `path` strictly after `since`, newest first. */
|
|
117
|
+
export function commitsSince(history, path, since) {
|
|
118
|
+
const commits = history.byPath.get(path);
|
|
119
|
+
if (!commits)
|
|
120
|
+
return [];
|
|
121
|
+
return commits.filter((c) => c.at > since);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Paths changed since a git ref — the `since:` query's input.
|
|
125
|
+
*
|
|
126
|
+
* A PR-scoped sweep: resolve what the branch touched, then ask the same
|
|
127
|
+
* question `touching` asks. Returns an empty list rather than throwing when the
|
|
128
|
+
* ref cannot be resolved, because a missing ref is the caller's mistake to
|
|
129
|
+
* report, not a reason to fail the whole query.
|
|
130
|
+
*/
|
|
131
|
+
export function changedSince(cwd, ref) {
|
|
132
|
+
const out = git(`git diff --name-only ${ref}...HEAD`, cwd);
|
|
133
|
+
return out
|
|
134
|
+
.split('\n')
|
|
135
|
+
.map((line) => line.trim())
|
|
136
|
+
.filter((line) => line.length > 0);
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=git-scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-scan.js","sourceRoot":"","sources":["../../src/edges/git-scan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge index (SPEC-136 D12, WORK-594).
|
|
3
|
+
*
|
|
4
|
+
* **Built as an addressable index, with ranking as one reader of it.** The
|
|
5
|
+
* tempting shape is a function that walks content, scores as it goes, and
|
|
6
|
+
* prints. That shape cannot answer WORK-596's `touching` query, which runs in
|
|
7
|
+
* the opposite direction — from a changed file to the pages that document it —
|
|
8
|
+
* and without the git scan at all. The cost of doing it properly is an
|
|
9
|
+
* afternoon here and a rewrite avoided later.
|
|
10
|
+
*/
|
|
11
|
+
import { type DeclaredEdgeError, type Edge, type EdgeClass } from './extract.js';
|
|
12
|
+
import { type Commit, type GitHistory } from './git-scan.js';
|
|
13
|
+
export type { Edge, EdgeClass, DeclaredEdgeError } from './extract.js';
|
|
14
|
+
export type { Commit, GitHistory } from './git-scan.js';
|
|
15
|
+
export { changedSince, GitScanRefusal, scanHistory } from './git-scan.js';
|
|
16
|
+
export { globMatcher, globToRegExp, resolveStaleSettings, StaleConfigRefusal, type StaleSettings, } from './exclude.js';
|
|
17
|
+
export interface EdgeIndex {
|
|
18
|
+
edges: Edge[];
|
|
19
|
+
/** Declared edges that resolve to nothing — D14 reports these loudly, where
|
|
20
|
+
* every inferred class stays silent. */
|
|
21
|
+
errors: DeclaredEdgeError[];
|
|
22
|
+
/** target path → edges pointing at it. The `touching` direction. */
|
|
23
|
+
byTarget: Map<string, Edge[]>;
|
|
24
|
+
/** referrer page → edges it declares. */
|
|
25
|
+
byReferrer: Map<string, Edge[]>;
|
|
26
|
+
/** What the project's `stale` config removed. Reported, never silent: an
|
|
27
|
+
* exclusion list that hides its own effect is how the report converges on
|
|
28
|
+
* empty without anyone deciding that it should. */
|
|
29
|
+
excluded: ExclusionCount;
|
|
30
|
+
}
|
|
31
|
+
export interface ExclusionCount {
|
|
32
|
+
/** Pages skipped whole, matching an `archival` glob. */
|
|
33
|
+
archivalPages: number;
|
|
34
|
+
/** Edges dropped because their target matched a `generated` glob. */
|
|
35
|
+
generatedTargets: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the index from a content directory.
|
|
39
|
+
*
|
|
40
|
+
* Needs no git and no build, so `touching` can answer for a file created in the
|
|
41
|
+
* working tree and never committed.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildEdgeIndex(opts: {
|
|
44
|
+
repoRoot: string;
|
|
45
|
+
contentDirs: string[];
|
|
46
|
+
/** Referrer globs — matching pages contribute no edges at all. */
|
|
47
|
+
archival?: string[];
|
|
48
|
+
/** Target globs — edges pointing at a match are dropped. */
|
|
49
|
+
generated?: string[];
|
|
50
|
+
}): EdgeIndex;
|
|
51
|
+
/** Index a list of edges in both directions. */
|
|
52
|
+
export declare function indexEdges(edges: Edge[]): EdgeIndex;
|
|
53
|
+
/**
|
|
54
|
+
* The `touching` query — every page whose edges point at any of `paths`.
|
|
55
|
+
*
|
|
56
|
+
* Not a staleness measurement and not a filter over the ranked report: there is
|
|
57
|
+
* no commit count, because the change has not happened yet. It takes no git
|
|
58
|
+
* history, is unbounded by `--top`, and is the query that makes this feature
|
|
59
|
+
* preventive rather than retrospective.
|
|
60
|
+
*/
|
|
61
|
+
export declare function edgesTouching(index: EdgeIndex, paths: string[]): Edge[];
|
|
62
|
+
export interface RankedEdge {
|
|
63
|
+
edge: Edge;
|
|
64
|
+
/** Commits touching the target since the referrer last changed. */
|
|
65
|
+
score: number;
|
|
66
|
+
/** Unix seconds; undefined when the referrer is untracked. */
|
|
67
|
+
referrerChanged?: number;
|
|
68
|
+
targetChanged?: number;
|
|
69
|
+
commits: Commit[];
|
|
70
|
+
}
|
|
71
|
+
export interface RankOptions {
|
|
72
|
+
/** Narrow to one edge class. */
|
|
73
|
+
edgeClass?: EdgeClass;
|
|
74
|
+
/** Floor on the commit count. */
|
|
75
|
+
min?: number;
|
|
76
|
+
}
|
|
77
|
+
/** Per-class base rate — how many edges of that class scored non-zero out of
|
|
78
|
+
* how many were scanned. Printed in the footer so degradation is visible
|
|
79
|
+
* rather than inferred. */
|
|
80
|
+
export interface BaseRate {
|
|
81
|
+
class: EdgeClass;
|
|
82
|
+
nonZero: number;
|
|
83
|
+
scanned: number;
|
|
84
|
+
}
|
|
85
|
+
export interface RankResult {
|
|
86
|
+
ranked: RankedEdge[];
|
|
87
|
+
baseRates: BaseRate[];
|
|
88
|
+
/** Total non-zero edges before `--top` truncation. */
|
|
89
|
+
totalNonZero: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Rank the index against a git history.
|
|
93
|
+
*
|
|
94
|
+
* One reader of the index among several — `touching` is another, and neither
|
|
95
|
+
* owns it.
|
|
96
|
+
*
|
|
97
|
+
* An edge whose referrer is newer than every change to its target scores zero
|
|
98
|
+
* and is omitted. A zero score is **the absence of evidence of staleness, not
|
|
99
|
+
* evidence of freshness**: the referrer side resets on any edit, including a
|
|
100
|
+
* typo fix (D6, a known and accepted false negative).
|
|
101
|
+
*/
|
|
102
|
+
export declare function rankEdges(index: EdgeIndex, history: GitHistory, opts?: RankOptions): RankResult;
|
|
103
|
+
/** Build the index and rank it in one call — the report's entry point.
|
|
104
|
+
*
|
|
105
|
+
* `contentDirs`, `archival` and `generated` all come from the project's
|
|
106
|
+
* `refrakt.config.json` when not passed explicitly (tests pass fixtures). None
|
|
107
|
+
* of the three has a built-in value: a directory layout is a property of the
|
|
108
|
+
* project, not of the tool. */
|
|
109
|
+
export declare function runStale(opts: {
|
|
110
|
+
repoRoot: string;
|
|
111
|
+
contentDirs?: string[];
|
|
112
|
+
archival?: string[];
|
|
113
|
+
generated?: string[];
|
|
114
|
+
edgeClass?: EdgeClass;
|
|
115
|
+
min?: number;
|
|
116
|
+
}): RankResult & {
|
|
117
|
+
index: EdgeIndex;
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/edges/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge index (SPEC-136 D12, WORK-594).
|
|
3
|
+
*
|
|
4
|
+
* **Built as an addressable index, with ranking as one reader of it.** The
|
|
5
|
+
* tempting shape is a function that walks content, scores as it goes, and
|
|
6
|
+
* prints. That shape cannot answer WORK-596's `touching` query, which runs in
|
|
7
|
+
* the opposite direction — from a changed file to the pages that document it —
|
|
8
|
+
* and without the git scan at all. The cost of doing it properly is an
|
|
9
|
+
* afternoon here and a rewrite avoided later.
|
|
10
|
+
*/
|
|
11
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
12
|
+
import { join, relative, sep } from 'node:path';
|
|
13
|
+
import { compareMarker, parseMarker, sliceForInvocation } from '@refrakt-md/runes';
|
|
14
|
+
import { dedupeEdges, extractDeclaredEdges, extractDescribedLinkEdges, extractEmbeddedEdges, extractProseEdges, } from './extract.js';
|
|
15
|
+
import { parseFrontmatter } from '../frontmatter.js';
|
|
16
|
+
import { globMatcher, resolveStaleSettings } from './exclude.js';
|
|
17
|
+
import { commitsSince, lastChanged, scanHistory, } from './git-scan.js';
|
|
18
|
+
export { changedSince, GitScanRefusal, scanHistory } from './git-scan.js';
|
|
19
|
+
export { globMatcher, globToRegExp, resolveStaleSettings, StaleConfigRefusal, } from './exclude.js';
|
|
20
|
+
/** Collect content files under a directory, repo-root-relative POSIX. */
|
|
21
|
+
function collectPages(contentDir, repoRoot, out = []) {
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = readdirSync(contentDir);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const full = join(contentDir, entry);
|
|
31
|
+
if (statSync(full).isDirectory())
|
|
32
|
+
collectPages(full, repoRoot, out);
|
|
33
|
+
else if (entry.endsWith('.md'))
|
|
34
|
+
out.push(relative(repoRoot, full).split(sep).join('/'));
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the index from a content directory.
|
|
40
|
+
*
|
|
41
|
+
* Needs no git and no build, so `touching` can answer for a file created in the
|
|
42
|
+
* working tree and never committed.
|
|
43
|
+
*/
|
|
44
|
+
export function buildEdgeIndex(opts) {
|
|
45
|
+
const edges = [];
|
|
46
|
+
const errors = [];
|
|
47
|
+
const isArchival = globMatcher(opts.archival);
|
|
48
|
+
const isGenerated = globMatcher(opts.generated);
|
|
49
|
+
const excluded = { archivalPages: 0, generatedTargets: 0 };
|
|
50
|
+
const exists = (path) => {
|
|
51
|
+
try {
|
|
52
|
+
return statSync(join(opts.repoRoot, path)).isFile();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// Every content page, for resolving described-link targets. A link points
|
|
59
|
+
// at a route; the edge needs the file behind it.
|
|
60
|
+
const allPages = opts.contentDirs.flatMap((d) => collectPages(join(opts.repoRoot, d), opts.repoRoot));
|
|
61
|
+
const byRoute = new Map();
|
|
62
|
+
for (const page of allPages) {
|
|
63
|
+
for (const dir of opts.contentDirs) {
|
|
64
|
+
if (!page.startsWith(`${dir}/`))
|
|
65
|
+
continue;
|
|
66
|
+
const rel = page.slice(dir.length + 1);
|
|
67
|
+
const route = `/${rel.replace(/\.md$/, '').replace(/\/index$/, '')}`;
|
|
68
|
+
byRoute.set(route === '/index' ? '/' : route, page);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Both `<path>.md` and `<path>/index.md` resolve. The anchor is already
|
|
72
|
+
// stripped by the extractor.
|
|
73
|
+
const resolvePage = (route) => byRoute.get(route) ?? byRoute.get(`${route}/index`);
|
|
74
|
+
for (const page of allPages) {
|
|
75
|
+
// An archival page is skipped before extraction, not filtered after it,
|
|
76
|
+
// so it disappears from `touching` too — "what documents this file"
|
|
77
|
+
// must not answer with a record of what was once true.
|
|
78
|
+
if (isArchival(page)) {
|
|
79
|
+
excluded.archivalPages++;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
let source;
|
|
83
|
+
try {
|
|
84
|
+
source = readFileSync(join(opts.repoRoot, page), 'utf8');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const { frontmatter } = parseFrontmatter(source);
|
|
90
|
+
edges.push(...extractEmbeddedEdges(page, source));
|
|
91
|
+
// A `_layout.md` declares nothing on behalf of the pages beneath it
|
|
92
|
+
// (D13) — that claim would be attributed to every one of them.
|
|
93
|
+
if (!page.endsWith('/_layout.md')) {
|
|
94
|
+
const declared = extractDeclaredEdges(page, frontmatter, exists);
|
|
95
|
+
edges.push(...declared.edges);
|
|
96
|
+
errors.push(...declared.errors);
|
|
97
|
+
}
|
|
98
|
+
edges.push(...extractDescribedLinkEdges(page, source, resolvePage));
|
|
99
|
+
edges.push(...extractProseEdges(page, source, exists));
|
|
100
|
+
}
|
|
101
|
+
// A marker outranks a commit count, but only while it is still true. This
|
|
102
|
+
// is where the two are told apart — here rather than in `rankEdges`, which
|
|
103
|
+
// reads no files, and against the same slice `snippet review` hashes.
|
|
104
|
+
for (const edge of edges) {
|
|
105
|
+
if (!edge.reviewed || edge.attrs === undefined)
|
|
106
|
+
continue;
|
|
107
|
+
edge.markerStale = !markerIsCurrent(opts.repoRoot, edge.target, edge.attrs, edge.reviewed);
|
|
108
|
+
edge.attrs = undefined;
|
|
109
|
+
}
|
|
110
|
+
const deduped = dedupeEdges(edges);
|
|
111
|
+
const kept = deduped.filter((edge) => {
|
|
112
|
+
if (!isGenerated(edge.target))
|
|
113
|
+
return true;
|
|
114
|
+
excluded.generatedTargets++;
|
|
115
|
+
return false;
|
|
116
|
+
});
|
|
117
|
+
return { ...indexEdges(kept), errors, excluded };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Does an attached marker still certify the target's current content?
|
|
121
|
+
*
|
|
122
|
+
* A formatting-only change counts as current: SPEC-134 D3 already decided that
|
|
123
|
+
* a reformat does not cost a review, and re-deciding it here would make the two
|
|
124
|
+
* tools disagree.
|
|
125
|
+
*
|
|
126
|
+
* An anchor that refuses, or a target that cannot be read, answers `true`. The
|
|
127
|
+
* refusal is SPEC-131's to report, and treating "could not check" as "stale"
|
|
128
|
+
* would put every broken anchor at the top of a report about documentation.
|
|
129
|
+
*/
|
|
130
|
+
function markerIsCurrent(repoRoot, target, attrs, reviewed) {
|
|
131
|
+
let source;
|
|
132
|
+
try {
|
|
133
|
+
source = readFileSync(join(repoRoot, target), 'utf8');
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
const current = sliceForInvocation(attrs, target, source);
|
|
139
|
+
if (current === undefined)
|
|
140
|
+
return true;
|
|
141
|
+
const stored = parseMarker(reviewed);
|
|
142
|
+
const verdict = compareMarker(stored.strict, current, stored.loose).verdict;
|
|
143
|
+
return verdict === 'current' || verdict === 'formatting-only';
|
|
144
|
+
}
|
|
145
|
+
/** Index a list of edges in both directions. */
|
|
146
|
+
export function indexEdges(edges) {
|
|
147
|
+
const byTarget = new Map();
|
|
148
|
+
const byReferrer = new Map();
|
|
149
|
+
for (const edge of edges) {
|
|
150
|
+
const t = byTarget.get(edge.target);
|
|
151
|
+
if (t)
|
|
152
|
+
t.push(edge);
|
|
153
|
+
else
|
|
154
|
+
byTarget.set(edge.target, [edge]);
|
|
155
|
+
const r = byReferrer.get(edge.referrer);
|
|
156
|
+
if (r)
|
|
157
|
+
r.push(edge);
|
|
158
|
+
else
|
|
159
|
+
byReferrer.set(edge.referrer, [edge]);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
edges,
|
|
163
|
+
errors: [],
|
|
164
|
+
byTarget,
|
|
165
|
+
byReferrer,
|
|
166
|
+
excluded: { archivalPages: 0, generatedTargets: 0 },
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The `touching` query — every page whose edges point at any of `paths`.
|
|
171
|
+
*
|
|
172
|
+
* Not a staleness measurement and not a filter over the ranked report: there is
|
|
173
|
+
* no commit count, because the change has not happened yet. It takes no git
|
|
174
|
+
* history, is unbounded by `--top`, and is the query that makes this feature
|
|
175
|
+
* preventive rather than retrospective.
|
|
176
|
+
*/
|
|
177
|
+
export function edgesTouching(index, paths) {
|
|
178
|
+
const out = [];
|
|
179
|
+
for (const path of paths) {
|
|
180
|
+
out.push(...(index.byTarget.get(path) ?? []));
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Rank the index against a git history.
|
|
186
|
+
*
|
|
187
|
+
* One reader of the index among several — `touching` is another, and neither
|
|
188
|
+
* owns it.
|
|
189
|
+
*
|
|
190
|
+
* An edge whose referrer is newer than every change to its target scores zero
|
|
191
|
+
* and is omitted. A zero score is **the absence of evidence of staleness, not
|
|
192
|
+
* evidence of freshness**: the referrer side resets on any edit, including a
|
|
193
|
+
* typo fix (D6, a known and accepted false negative).
|
|
194
|
+
*/
|
|
195
|
+
export function rankEdges(index, history, opts = {}) {
|
|
196
|
+
const ranked = [];
|
|
197
|
+
const rates = new Map();
|
|
198
|
+
for (const edge of index.edges) {
|
|
199
|
+
if (opts.edgeClass && edge.class !== opts.edgeClass)
|
|
200
|
+
continue;
|
|
201
|
+
let rate = rates.get(edge.class);
|
|
202
|
+
if (!rate) {
|
|
203
|
+
rate = { class: edge.class, nonZero: 0, scanned: 0 };
|
|
204
|
+
rates.set(edge.class, rate);
|
|
205
|
+
}
|
|
206
|
+
rate.scanned++;
|
|
207
|
+
const referrerChanged = lastChanged(history, `${history.prefix}${edge.referrer}`);
|
|
208
|
+
const targetChanged = lastChanged(history, `${history.prefix}${edge.target}`);
|
|
209
|
+
// An untracked referrer has no "since" to measure from. Reporting it as
|
|
210
|
+
// maximally stale would put every new page at the top of the report.
|
|
211
|
+
if (referrerChanged === undefined)
|
|
212
|
+
continue;
|
|
213
|
+
// A SPEC-134 marker is a stronger statement than a commit count, so
|
|
214
|
+
// where one exists it wins (D3) — but only while it is still true. A
|
|
215
|
+
// marker the target has outgrown has expired as a statement, so the
|
|
216
|
+
// commit count stands again.
|
|
217
|
+
if (edge.reviewed && !edge.markerStale)
|
|
218
|
+
continue;
|
|
219
|
+
const commits = commitsSince(history, `${history.prefix}${edge.target}`, referrerChanged);
|
|
220
|
+
if (commits.length === 0)
|
|
221
|
+
continue;
|
|
222
|
+
if (opts.min !== undefined && commits.length < opts.min)
|
|
223
|
+
continue;
|
|
224
|
+
rate.nonZero++;
|
|
225
|
+
ranked.push({
|
|
226
|
+
edge,
|
|
227
|
+
score: commits.length,
|
|
228
|
+
referrerChanged,
|
|
229
|
+
targetChanged,
|
|
230
|
+
commits,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
ranked.sort((a, b) => b.score - a.score || a.edge.referrer.localeCompare(b.edge.referrer));
|
|
234
|
+
return {
|
|
235
|
+
ranked,
|
|
236
|
+
baseRates: [...rates.values()].sort((a, b) => a.class.localeCompare(b.class)),
|
|
237
|
+
totalNonZero: ranked.length,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** Build the index and rank it in one call — the report's entry point.
|
|
241
|
+
*
|
|
242
|
+
* `contentDirs`, `archival` and `generated` all come from the project's
|
|
243
|
+
* `refrakt.config.json` when not passed explicitly (tests pass fixtures). None
|
|
244
|
+
* of the three has a built-in value: a directory layout is a property of the
|
|
245
|
+
* project, not of the tool. */
|
|
246
|
+
export function runStale(opts) {
|
|
247
|
+
const settings = opts.contentDirs === undefined
|
|
248
|
+
? resolveStaleSettings(opts.repoRoot)
|
|
249
|
+
: {
|
|
250
|
+
contentDirs: opts.contentDirs,
|
|
251
|
+
archival: opts.archival ?? [],
|
|
252
|
+
generated: opts.generated ?? [],
|
|
253
|
+
};
|
|
254
|
+
const index = buildEdgeIndex({
|
|
255
|
+
repoRoot: opts.repoRoot,
|
|
256
|
+
contentDirs: settings.contentDirs,
|
|
257
|
+
archival: opts.archival ?? settings.archival,
|
|
258
|
+
generated: opts.generated ?? settings.generated,
|
|
259
|
+
});
|
|
260
|
+
const history = scanHistory(opts.repoRoot);
|
|
261
|
+
const result = rankEdges(index, history, { edgeClass: opts.edgeClass, min: opts.min });
|
|
262
|
+
return { ...result, index };
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/edges/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAEN,WAAW,EAGX,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAEN,YAAY,EAEZ,WAAW,EACX,WAAW,GACX,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACN,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,GAElB,MAAM,cAAc,CAAC;AAwBtB,yEAAyE;AACzE,SAAS,YAAY,CAAC,UAAkB,EAAE,QAAgB,EAAE,MAAgB,EAAE;IAC7E,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACJ,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACrC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;aAC/D,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAO9B;IACA,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAmB,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC;IAE3E,MAAM,MAAM,GAAG,CAAC,IAAY,EAAW,EAAE;QACxC,IAAI,CAAC;YACJ,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC,CAAC;IAEF,0EAA0E;IAC1E,iDAAiD;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CACnD,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC;gBAAE,SAAS;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC;YACrE,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,CAAC;IACF,CAAC;IACD,wEAAwE;IACxE,6BAA6B;IAC7B,MAAM,WAAW,GAAG,CAAC,KAAa,EAAsB,EAAE,CACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC;IAErD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7B,wEAAwE;QACxE,oEAAoE;QACpE,uDAAuD;QACvD,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,QAAQ,CAAC,aAAa,EAAE,CAAC;YACzB,SAAS;QACV,CAAC;QAED,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACJ,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;QAED,MAAM,EAAE,WAAW,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAEjD,KAAK,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QAElD,oEAAoE;QACpE,+DAA+D;QAC/D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,GAAG,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,0EAA0E;IAC1E,2EAA2E;IAC3E,sEAAsE;IACtE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,SAAS;QACzD,IAAI,CAAC,WAAW,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3F,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACpC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,eAAe,CACvB,QAAgB,EAChB,MAAc,EACd,KAAa,EACb,QAAgB;IAEhB,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACJ,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;IAC5E,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,iBAAiB,CAAC;AAC/D,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC;YAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACf,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAEvC,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC;YAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACf,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO;QACN,KAAK;QACL,MAAM,EAAE,EAAE;QACV,QAAQ;QACR,UAAU;QACV,QAAQ,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE;KACnD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,KAAe;IAC9D,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAmCD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CACxB,KAAgB,EAChB,OAAmB,EACnB,OAAoB,EAAE;IAEtB,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS;YAAE,SAAS;QAE9D,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,IAAI,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YACrD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;QAEf,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClF,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAE9E,wEAAwE;QACxE,qEAAqE;QACrE,IAAI,eAAe,KAAK,SAAS;YAAE,SAAS;QAE5C,oEAAoE;QACpE,qEAAqE;QACrE,oEAAoE;QACpE,6BAA6B;QAC7B,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,SAAS;QAEjD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;QAC1F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG;YAAE,SAAS;QAElE,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,KAAK,EAAE,OAAO,CAAC,MAAM;YACrB,eAAe;YACf,aAAa;YACb,OAAO;SACP,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE3F,OAAO;QACN,MAAM;QACN,SAAS,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7E,YAAY,EAAE,MAAM,CAAC,MAAM;KAC3B,CAAC;AACH,CAAC;AAED;;;;;gCAKgC;AAChC,MAAM,UAAU,QAAQ,CAAC,IAOxB;IACA,MAAM,QAAQ,GACb,IAAI,CAAC,WAAW,KAAK,SAAS;QAC7B,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC;YACA,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;SAC/B,CAAC;IAEL,MAAM,KAAK,GAAG,cAAc,CAAC;QAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;QAC5C,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS;KAC/C,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvF,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC"}
|
package/dist/format.d.ts
CHANGED
|
@@ -23,5 +23,27 @@ import type { PipelineStats } from './pipeline.js';
|
|
|
23
23
|
* ✓ Build complete (0 errors, 1 warning)
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* Sink for a content load's pipeline diagnostics.
|
|
28
|
+
*
|
|
29
|
+
* SPEC-135 D5 — every surface that loads content reports through one of these
|
|
30
|
+
* rather than each adapter formatting and writing its own summary. The reporter
|
|
31
|
+
* is called exactly once per *actual* load: a cached `SiteLoader.load()` that
|
|
32
|
+
* returns a memoized site does not re-report, so a dev session prints on first
|
|
33
|
+
* load and on each `invalidate()`-triggered reload, not on every navigation.
|
|
34
|
+
*
|
|
35
|
+
* Receives the raw stats and warnings rather than a rendered string, so
|
|
36
|
+
* non-terminal consumers (the CLI's `--format json`, the MCP tool) can act on
|
|
37
|
+
* individual findings instead of parsing text.
|
|
38
|
+
*/
|
|
39
|
+
export type PipelineReporter = (stats: PipelineStats, warnings: PipelineWarning[]) => void;
|
|
40
|
+
/**
|
|
41
|
+
* The default reporter: `formatPipelineSummary` to stderr.
|
|
42
|
+
*
|
|
43
|
+
* This is the behaviour the adapters each implemented by hand before SPEC-135 —
|
|
44
|
+
* kept identical, byte for byte, so moving them onto the seam changes nothing
|
|
45
|
+
* a user sees.
|
|
46
|
+
*/
|
|
47
|
+
export declare const stderrReporter: PipelineReporter;
|
|
26
48
|
export declare function formatPipelineSummary(stats: PipelineStats, warnings: PipelineWarning[]): string;
|
|
27
49
|
//# sourceMappingURL=format.d.ts.map
|
package/dist/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAUnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAyB/F"}
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAUnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;AAE3F;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,gBAE5B,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAyB/F"}
|