@nielspeter/eess-md 0.8.0 → 0.10.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.
Files changed (74) hide show
  1. package/README.md +13 -1
  2. package/dist/builders/code-blocks.d.ts +37 -0
  3. package/dist/builders/code-blocks.d.ts.map +1 -0
  4. package/dist/builders/code-blocks.js +143 -0
  5. package/dist/builders/code-blocks.js.map +1 -0
  6. package/dist/builders/declaration-findings.d.ts +37 -0
  7. package/dist/builders/declaration-findings.d.ts.map +1 -0
  8. package/dist/builders/declaration-findings.js +128 -0
  9. package/dist/builders/declaration-findings.js.map +1 -0
  10. package/dist/builders/links.d.ts +60 -1
  11. package/dist/builders/links.d.ts.map +1 -1
  12. package/dist/builders/links.js +92 -1
  13. package/dist/builders/links.js.map +1 -1
  14. package/dist/conditions/linked-back.d.ts +17 -0
  15. package/dist/conditions/linked-back.d.ts.map +1 -0
  16. package/dist/conditions/linked-back.js +122 -0
  17. package/dist/conditions/linked-back.js.map +1 -0
  18. package/dist/conditions/resolve.d.ts +8 -40
  19. package/dist/conditions/resolve.d.ts.map +1 -1
  20. package/dist/conditions/resolve.js +26 -138
  21. package/dist/conditions/resolve.js.map +1 -1
  22. package/dist/corpus.d.ts +3 -0
  23. package/dist/corpus.d.ts.map +1 -1
  24. package/dist/corpus.js +7 -10
  25. package/dist/corpus.js.map +1 -1
  26. package/dist/index.d.ts +7 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +7 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/model/code-block-parse.d.ts +118 -0
  31. package/dist/model/code-block-parse.d.ts.map +1 -0
  32. package/dist/model/code-block-parse.js +368 -0
  33. package/dist/model/code-block-parse.js.map +1 -0
  34. package/dist/model/document.d.ts +14 -3
  35. package/dist/model/document.d.ts.map +1 -1
  36. package/dist/model/document.js +30 -7
  37. package/dist/model/document.js.map +1 -1
  38. package/dist/model/label.d.ts +35 -0
  39. package/dist/model/label.d.ts.map +1 -0
  40. package/dist/model/label.js +68 -0
  41. package/dist/model/label.js.map +1 -0
  42. package/dist/model/links.d.ts +43 -0
  43. package/dist/model/links.d.ts.map +1 -1
  44. package/dist/model/links.js +112 -11
  45. package/dist/model/links.js.map +1 -1
  46. package/dist/model/prose.d.ts +11 -0
  47. package/dist/model/prose.d.ts.map +1 -1
  48. package/dist/model/prose.js +50 -5
  49. package/dist/model/prose.js.map +1 -1
  50. package/dist/model/query.d.ts +7 -0
  51. package/dist/model/query.d.ts.map +1 -1
  52. package/dist/model/query.js +10 -3
  53. package/dist/model/query.js.map +1 -1
  54. package/dist/model/resolve-link.d.ts +142 -0
  55. package/dist/model/resolve-link.d.ts.map +1 -0
  56. package/dist/model/resolve-link.js +194 -0
  57. package/dist/model/resolve-link.js.map +1 -0
  58. package/dist/predicates/declared-block.d.ts +29 -0
  59. package/dist/predicates/declared-block.d.ts.map +1 -0
  60. package/dist/predicates/declared-block.js +47 -0
  61. package/dist/predicates/declared-block.js.map +1 -0
  62. package/dist/predicates/live-target.d.ts +12 -0
  63. package/dist/predicates/live-target.d.ts.map +1 -0
  64. package/dist/predicates/live-target.js +17 -0
  65. package/dist/predicates/live-target.js.map +1 -0
  66. package/dist/rules/adr.d.ts +14 -5
  67. package/dist/rules/adr.d.ts.map +1 -1
  68. package/dist/rules/adr.js +5 -27
  69. package/dist/rules/adr.js.map +1 -1
  70. package/dist/rules/ledger.d.ts +10 -6
  71. package/dist/rules/ledger.d.ts.map +1 -1
  72. package/dist/rules/ledger.js +80 -24
  73. package/dist/rules/ledger.js.map +1 -1
  74. package/package.json +6 -3
@@ -0,0 +1,142 @@
1
+ import type { ArchFix } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import type { MdDocument } from './document.js';
4
+ import type { MdLink } from './links.js';
5
+ /**
6
+ * Options for resolving a link URL against the repo tree. Covers static-site
7
+ * conventions (VitePress, Docusaurus, MkDocs, GitBook…) generically instead of
8
+ * naming any tool: extensionless links resolve by trying extensions and/or a
9
+ * directory index file.
10
+ */
11
+ export interface LinkResolveOptions {
12
+ /**
13
+ * Extensions to try when the URL as written doesn't resolve, e.g. `['.md']`
14
+ * for sites that link `./guide` meaning `./guide.md`. Tried in order.
15
+ */
16
+ readonly tryExtensions?: readonly string[];
17
+ /**
18
+ * Directory index filename to try, e.g. `'index.md'` for sites where
19
+ * `./guide/` (or `./guide`) means `./guide/index.md`.
20
+ */
21
+ readonly tryIndex?: string;
22
+ /**
23
+ * Content root for site-absolute links (leading `/`). Static-site generators
24
+ * resolve `/page` against the site's content root, not the repo root — e.g.
25
+ * `rootDir: 'docs'` makes `/guide` mean `docs/guide(.md)`. Repo-root
26
+ * resolution is still tried first, so plain repo-absolute links keep working.
27
+ */
28
+ readonly rootDir?: string;
29
+ /**
30
+ * Resolve a link naming a real directory (`./fixed/` or `./fixed`, with or
31
+ * without the trailing slash — both forms occur in this repo's own corpus)
32
+ * even when it has no index file. Correct for a repo-hosted corpus (GitHub,
33
+ * GitLab render a directory link as its listing); wrong for a static-site
34
+ * corpus where a bare directory is not itself a page — there, reach for
35
+ * {@link tryIndex} instead. Off by default: this widens what "resolves"
36
+ * means, and a link resolving that shouldn't is a false green, not a
37
+ * convenience. Default `false`.
38
+ *
39
+ * A link naming a real directory always wins over an unrelated typo — if
40
+ * `./foo` was meant as a deleted `foo.md` but a directory `foo/` happens to
41
+ * exist at that path, it resolves as the directory. Not a new failure mode:
42
+ * GitHub renders that link as the directory listing too, so this only
43
+ * closes the gap between the gate and what the link actually does when
44
+ * followed, it doesn't introduce a new way to be surprised by it.
45
+ *
46
+ * `resolveLink` reports a directory as its own case whatever this says; the
47
+ * option decides whether `resolve()` accepts that case.
48
+ */
49
+ readonly resolveDirectories?: boolean;
50
+ }
51
+ /** The parts of a link that resolution reads. Narrower than `MdLink`, so a caller can build one. */
52
+ export interface LinkToResolve {
53
+ readonly url: string;
54
+ readonly external: boolean;
55
+ readonly doc: {
56
+ readonly relPath: string;
57
+ };
58
+ }
59
+ /** Why an existing file is not a loaded corpus document. Every reason that applies is listed. */
60
+ export type NotInCorpusReason = 'not-markdown' | 'ignored' | 'outside-roots';
61
+ /**
62
+ * Where a link points, as one named case (plan 0404). Each case is a different
63
+ * fact with a different remedy, so a rule over links can say which one it met:
64
+ *
65
+ * - `external`: a URL with a scheme or `//`; not resolved against the repo.
66
+ * - `self`: a pure `#anchor`, or a path back to the linking document itself.
67
+ * - `document`: an existing, loaded corpus document, whose links are parsed.
68
+ * - `not-in-corpus`: an existing file the corpus did not load, with every reason.
69
+ * - `directory`: no candidate is a file, but one names a real directory.
70
+ * - `missing`: nothing exists; `tried` lists every candidate, in order.
71
+ */
72
+ export type LinkTarget = {
73
+ readonly kind: 'external';
74
+ } | {
75
+ readonly kind: 'self';
76
+ } | {
77
+ readonly kind: 'document';
78
+ readonly path: string;
79
+ readonly doc: MdDocument;
80
+ } | {
81
+ readonly kind: 'not-in-corpus';
82
+ readonly path: string;
83
+ readonly reasons: readonly NotInCorpusReason[];
84
+ } | {
85
+ readonly kind: 'directory';
86
+ readonly path: string;
87
+ } | {
88
+ readonly kind: 'missing';
89
+ readonly tried: readonly string[];
90
+ };
91
+ /** How `corpus()` decided which files to load, kept so a resolver can say why one was not. */
92
+ interface CorpusMatchers {
93
+ readonly inRoots: (relPath: string) => boolean;
94
+ readonly ignored: (relPath: string) => boolean;
95
+ }
96
+ /**
97
+ * Record how `corpus()` filtered its files. Called by `corpus()` only; a
98
+ * `Corpus` built any other way has no matchers, and an unloaded `.md` file in
99
+ * it is reported as `outside-roots`.
100
+ */
101
+ export declare function registerCorpusMatchers(corpus: Corpus, matchers: CorpusMatchers): void;
102
+ /**
103
+ * A link URL's base repo-relative target(s), or `[]` for a pure fragment.
104
+ * Site-absolute links (leading `/`) yield the repo-root target, then the
105
+ * content-root target when `rootDir` is set. Throws `URIError` on malformed
106
+ * percent-encoding; `resolveLink` turns that into `missing`.
107
+ *
108
+ * Exported for `linkResolves`, which labels the two site-absolute candidates
109
+ * in its directory hint (bug 0137); not part of the package's public API.
110
+ */
111
+ export declare function linkTargets(link: LinkToResolve, options: LinkResolveOptions): string[];
112
+ /**
113
+ * Resolve a link to the one case it is (plan 0404). Total: it never throws.
114
+ *
115
+ * Candidates are tried in a fixed order — each target (repo-root, then
116
+ * content-root when `rootDir` is set), and within it the URL as written, then
117
+ * `tryExtensions`, then `tryIndex`. The first existing **file** wins. Only when
118
+ * no candidate is a file is a directory reported, the first target's first.
119
+ * When nothing exists, `missing.tried` is every candidate tried, in order.
120
+ *
121
+ * Links into the built-in ignores (`node_modules`, `.git`, `dist`, …) are
122
+ * `missing`: `corpus()` never walks those folders, so their files are not known.
123
+ *
124
+ * The directory and document indexes are built on a corpus's first resolution
125
+ * and cached. A `corpus()` result never changes, so that is exact; a hand-built
126
+ * `Corpus` whose `documents()` returns something different later is not re-read.
127
+ */
128
+ export declare function resolveLink(link: LinkToResolve, corpus: Corpus, options?: LinkResolveOptions): LinkTarget;
129
+ /** Basename → every repo file with it, for {@link movedLinkFix}. Built once per caller. */
130
+ export declare function basenameIndex(corpus: Corpus): ReadonlyMap<string, readonly string[]>;
131
+ /**
132
+ * If a broken link's basename uniquely names one file in the repo (the target
133
+ * moved, not renamed), a deterministic autofix rewriting the URL to a path
134
+ * relative to the linking document (plan 0066). Ambiguous basename, or a URL
135
+ * whose exact span isn't known, → no fix.
136
+ *
137
+ * Lives beside `resolveLink` so that path arithmetic stays in this one module
138
+ * (plan 0404's structural rule keeps `node:path` out of the conditions).
139
+ */
140
+ export declare function movedLinkFix(link: MdLink, byBasename: ReadonlyMap<string, readonly string[]>): ArchFix | undefined;
141
+ export {};
142
+ //# sourceMappingURL=resolve-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-link.d.ts","sourceRoot":"","sources":["../../src/model/resolve-link.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CACtC;AAED,oGAAoG;AACpG,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED,iGAAiG;AACjG,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,GAAG,eAAe,CAAA;AAE5E;;;;;;;;;;GAUG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CAAE,GAC9E;IACE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAC/C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAEnE,8FAA8F;AAC9F,UAAU,cAAc;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;IAC9C,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;CAC/C;AAID;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAErF;AAwCD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAYtF;AA+BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,UAAU,CA2BZ;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CASpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,GACjD,OAAO,GAAG,SAAS,CAoBrB"}
@@ -0,0 +1,194 @@
1
+ import { posix } from 'node:path';
2
+ const matchersOf = new WeakMap();
3
+ /**
4
+ * Record how `corpus()` filtered its files. Called by `corpus()` only; a
5
+ * `Corpus` built any other way has no matchers, and an unloaded `.md` file in
6
+ * it is reported as `outside-roots`.
7
+ */
8
+ export function registerCorpusMatchers(corpus, matchers) {
9
+ matchersOf.set(corpus, matchers);
10
+ }
11
+ const indexesOf = new WeakMap();
12
+ /**
13
+ * The two corpus-wide indexes resolution needs, built once per corpus. Before
14
+ * plan 0404 the directory index was rebuilt by every `resolve()` condition;
15
+ * called per link from a custom rule it would be rebuilt per link.
16
+ *
17
+ * The directory index is derived from `fileIndex` rather than new I/O: each
18
+ * indexed file's ancestors are known directories. `fileIndex` spans the whole
19
+ * physical repo walk from `cwd`, not just the configured `roots`, so a
20
+ * directory link can resolve against something outside any root. The repo
21
+ * root itself (`.`) is deliberately excluded — nobody links "the whole repo".
22
+ */
23
+ function indexes(corpus) {
24
+ const cached = indexesOf.get(corpus);
25
+ if (cached !== undefined)
26
+ return cached;
27
+ const directories = new Set();
28
+ for (const rel of corpus.fileIndex) {
29
+ let dir = posix.dirname(rel);
30
+ while (dir !== '.' && dir !== '/' && !directories.has(dir)) {
31
+ directories.add(dir);
32
+ dir = posix.dirname(dir);
33
+ }
34
+ }
35
+ const documents = new Map();
36
+ for (const doc of corpus.documents())
37
+ documents.set(doc.relPath, doc);
38
+ const built = { directories, documents };
39
+ indexesOf.set(corpus, built);
40
+ return built;
41
+ }
42
+ /**
43
+ * A link URL's base repo-relative target(s), or `[]` for a pure fragment.
44
+ * Site-absolute links (leading `/`) yield the repo-root target, then the
45
+ * content-root target when `rootDir` is set. Throws `URIError` on malformed
46
+ * percent-encoding; `resolveLink` turns that into `missing`.
47
+ *
48
+ * Exported for `linkResolves`, which labels the two site-absolute candidates
49
+ * in its directory hint (bug 0137); not part of the package's public API.
50
+ */
51
+ export function linkTargets(link, options) {
52
+ const withoutFragment = link.url.split('#')[0] ?? '';
53
+ if (withoutFragment === '')
54
+ return [];
55
+ const decoded = decodeURIComponent(withoutFragment);
56
+ if (decoded.startsWith('/')) {
57
+ const repoRooted = posix.normalize(decoded.replace(/^\/+/, ''));
58
+ return options.rootDir !== undefined
59
+ ? [repoRooted, posix.normalize(posix.join(options.rootDir, repoRooted))]
60
+ : [repoRooted];
61
+ }
62
+ const base = posix.dirname(link.doc.relPath);
63
+ return [posix.normalize(posix.join(base, decoded))];
64
+ }
65
+ /** Every repo-relative file candidate for one target, in the order they are tried. */
66
+ function candidates(target, options) {
67
+ const out = [target];
68
+ const bare = target.replace(/\/+$/, '');
69
+ for (const ext of options.tryExtensions ?? []) {
70
+ out.push(bare + ext);
71
+ }
72
+ if (options.tryIndex !== undefined) {
73
+ out.push(posix.join(bare, options.tryIndex));
74
+ }
75
+ return out;
76
+ }
77
+ function fileTarget(path, link, corpus) {
78
+ if (path === link.doc.relPath)
79
+ return { kind: 'self' };
80
+ const doc = indexes(corpus).documents.get(path);
81
+ if (doc !== undefined)
82
+ return { kind: 'document', path, doc };
83
+ const matchers = matchersOf.get(corpus);
84
+ const reasons = [];
85
+ if (!path.endsWith('.md'))
86
+ reasons.push('not-markdown');
87
+ if (matchers?.ignored(path) === true)
88
+ reasons.push('ignored');
89
+ if (matchers === undefined || !matchers.inRoots(path))
90
+ reasons.push('outside-roots');
91
+ // A loaded `corpus()` reads exactly the `.md` files in its roots that it does
92
+ // not ignore, so one of the three always applies. Should a hand-built
93
+ // `Corpus` disagree, `outside-roots` is the reason that names a remedy.
94
+ if (reasons.length === 0)
95
+ reasons.push('outside-roots');
96
+ return { kind: 'not-in-corpus', path, reasons };
97
+ }
98
+ /**
99
+ * Resolve a link to the one case it is (plan 0404). Total: it never throws.
100
+ *
101
+ * Candidates are tried in a fixed order — each target (repo-root, then
102
+ * content-root when `rootDir` is set), and within it the URL as written, then
103
+ * `tryExtensions`, then `tryIndex`. The first existing **file** wins. Only when
104
+ * no candidate is a file is a directory reported, the first target's first.
105
+ * When nothing exists, `missing.tried` is every candidate tried, in order.
106
+ *
107
+ * Links into the built-in ignores (`node_modules`, `.git`, `dist`, …) are
108
+ * `missing`: `corpus()` never walks those folders, so their files are not known.
109
+ *
110
+ * The directory and document indexes are built on a corpus's first resolution
111
+ * and cached. A `corpus()` result never changes, so that is exact; a hand-built
112
+ * `Corpus` whose `documents()` returns something different later is not re-read.
113
+ */
114
+ export function resolveLink(link, corpus, options = {}) {
115
+ if (link.external)
116
+ return { kind: 'external' };
117
+ let targets;
118
+ try {
119
+ targets = linkTargets(link, options);
120
+ }
121
+ catch (error) {
122
+ // Malformed percent-encoding (`%E0`): the URL names no file anyone can
123
+ // reach, which is exactly `missing`. Reported, never thrown, so one bad
124
+ // link cannot abort a whole run.
125
+ void error;
126
+ return { kind: 'missing', tried: [link.url] };
127
+ }
128
+ if (targets.length === 0)
129
+ return { kind: 'self' };
130
+ const tried = [];
131
+ for (const target of targets) {
132
+ for (const candidate of candidates(target, options)) {
133
+ tried.push(candidate);
134
+ if (corpus.fileIndex.has(candidate))
135
+ return fileTarget(candidate, link, corpus);
136
+ }
137
+ }
138
+ const { directories } = indexes(corpus);
139
+ for (const target of targets) {
140
+ const bare = target.replace(/\/+$/, '');
141
+ if (directories.has(bare))
142
+ return { kind: 'directory', path: bare };
143
+ }
144
+ return { kind: 'missing', tried };
145
+ }
146
+ /** Basename → every repo file with it, for {@link movedLinkFix}. Built once per caller. */
147
+ export function basenameIndex(corpus) {
148
+ const byBasename = new Map();
149
+ for (const rel of corpus.fileIndex) {
150
+ const base = rel.slice(rel.lastIndexOf('/') + 1);
151
+ const list = byBasename.get(base);
152
+ if (list)
153
+ list.push(rel);
154
+ else
155
+ byBasename.set(base, [rel]);
156
+ }
157
+ return byBasename;
158
+ }
159
+ /**
160
+ * If a broken link's basename uniquely names one file in the repo (the target
161
+ * moved, not renamed), a deterministic autofix rewriting the URL to a path
162
+ * relative to the linking document (plan 0066). Ambiguous basename, or a URL
163
+ * whose exact span isn't known, → no fix.
164
+ *
165
+ * Lives beside `resolveLink` so that path arithmetic stays in this one module
166
+ * (plan 0404's structural rule keeps `node:path` out of the conditions).
167
+ */
168
+ export function movedLinkFix(link, byBasename) {
169
+ if (link.urlStart === undefined || link.urlEnd === undefined)
170
+ return undefined;
171
+ const path = link.url.split('#')[0] ?? '';
172
+ const fragment = link.url.slice(path.length); // '' or '#anchor'
173
+ const base = path.slice(path.replace(/\/+$/, '').lastIndexOf('/') + 1);
174
+ const matches = byBasename.get(base) ?? [];
175
+ if (matches.length !== 1)
176
+ return undefined; // renamed (no match) or ambiguous → no fix
177
+ const target = matches[0];
178
+ if (target === undefined)
179
+ return undefined;
180
+ let rel = posix.relative(posix.dirname(link.doc.relPath), target);
181
+ if (!rel.startsWith('.'))
182
+ rel = './' + rel;
183
+ const replacement = rel + fragment;
184
+ if (replacement === link.url)
185
+ return undefined;
186
+ return {
187
+ file: link.doc.file,
188
+ start: link.urlStart,
189
+ end: link.urlEnd,
190
+ replacement,
191
+ describe: `rewrite link "${link.url}" → "${replacement}"`,
192
+ };
193
+ }
194
+ //# sourceMappingURL=resolve-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-link.js","sourceRoot":"","sources":["../../src/model/resolve-link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Predicate } from '@nielspeter/eess';
2
+ import type { MdLink, MdLinkBlock } from '../model/links.js';
3
+ /**
4
+ * Plan 0405 — select the links a block declares.
5
+ *
6
+ * These factories are deliberately NOT exported from the package root, the same
7
+ * as `areInternal`/`areLive`: the declaration scan lives in `LinkRuleBuilder`,
8
+ * which records the declaration when `areLabelled()`/`areInSection()` is called.
9
+ * A raw predicate composed through `satisfy()`/`not()`/`or()` would select
10
+ * without recording anything, and so without the scan.
11
+ */
12
+ /**
13
+ * The block that decides whether a link with this `path` is declared under the
14
+ * label `declared` matches: walking from the innermost block outwards, the first
15
+ * that either matches it (any form, exact case) or opens with a wrapped label of
16
+ * its own. A plain line that is not the label — `bug 0402: [b](b.md)`,
17
+ * `Metadata:`, a URL — never decides, so it neither hides a declaration nor
18
+ * captures one.
19
+ */
20
+ export declare function decidingBlock(path: readonly MdLinkBlock[], declared: RegExp): {
21
+ readonly block: MdLinkBlock;
22
+ readonly at: number;
23
+ readonly matches: boolean;
24
+ } | undefined;
25
+ /** Links declared under `label` (case-sensitive), in any label form, at any depth below it. */
26
+ export declare function areLabelledPredicate(label: string): Predicate<MdLink>;
27
+ /** Links under a heading matching `name` (a string exactly, or a `RegExp`), at any depth beneath it. */
28
+ export declare function areInSectionPredicate(name: string | RegExp): Predicate<MdLink>;
29
+ //# sourceMappingURL=declared-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-block.d.ts","sourceRoot":"","sources":["../../src/predicates/declared-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAI5D;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAQ7F;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAMrE;AAED,wGAAwG;AACxG,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAK9E"}
@@ -0,0 +1,47 @@
1
+ import { labelPattern, wrappedLabelOf } from '../model/label.js';
2
+ import { inSection } from '../model/query.js';
3
+ /**
4
+ * Plan 0405 — select the links a block declares.
5
+ *
6
+ * These factories are deliberately NOT exported from the package root, the same
7
+ * as `areInternal`/`areLive`: the declaration scan lives in `LinkRuleBuilder`,
8
+ * which records the declaration when `areLabelled()`/`areInSection()` is called.
9
+ * A raw predicate composed through `satisfy()`/`not()`/`or()` would select
10
+ * without recording anything, and so without the scan.
11
+ */
12
+ /**
13
+ * The block that decides whether a link with this `path` is declared under the
14
+ * label `declared` matches: walking from the innermost block outwards, the first
15
+ * that either matches it (any form, exact case) or opens with a wrapped label of
16
+ * its own. A plain line that is not the label — `bug 0402: [b](b.md)`,
17
+ * `Metadata:`, a URL — never decides, so it neither hides a declaration nor
18
+ * captures one.
19
+ */
20
+ export function decidingBlock(path, declared) {
21
+ for (let at = path.length - 1; at >= 0; at--) {
22
+ const block = path[at];
23
+ if (block === undefined)
24
+ continue;
25
+ if (declared.test(block.text))
26
+ return { block, at, matches: true };
27
+ if (wrappedLabelOf(block.text) !== undefined)
28
+ return { block, at, matches: false };
29
+ }
30
+ return undefined;
31
+ }
32
+ /** Links declared under `label` (case-sensitive), in any label form, at any depth below it. */
33
+ export function areLabelledPredicate(label) {
34
+ const re = new RegExp(labelPattern(label));
35
+ return {
36
+ description: `are declared under the label "${label}"`,
37
+ test: (l) => decidingBlock(l.blockPath ?? [], re)?.matches === true,
38
+ };
39
+ }
40
+ /** Links under a heading matching `name` (a string exactly, or a `RegExp`), at any depth beneath it. */
41
+ export function areInSectionPredicate(name) {
42
+ return {
43
+ description: `are in section "${String(name)}"`,
44
+ test: (l) => inSection(l, name),
45
+ };
46
+ }
47
+ //# sourceMappingURL=declared-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declared-block.js","sourceRoot":"","sources":["../../src/predicates/declared-block.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA4B,EAC5B,QAAgB;IAEhB,KAAK,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;QACtB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAClE,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IACpF,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1C,OAAO;QACL,WAAW,EAAE,iCAAiC,KAAK,GAAG;QACtD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,OAAO,KAAK,IAAI;KACpE,CAAA;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,qBAAqB,CAAC,IAAqB;IACzD,OAAO;QACL,WAAW,EAAE,mBAAmB,MAAM,CAAC,IAAI,CAAC,GAAG;QAC/C,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;KAChC,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Predicate } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import type { MdLink } from '../model/links.js';
4
+ import { type LinkResolveOptions } from '../model/resolve-link.js';
5
+ /**
6
+ * Links whose target is not a frozen corpus document (plan 0406), resolved with
7
+ * `resolveLink` and `options` — pass `beLinkedBack()` the same options, so both
8
+ * resolve a link the same way. Every other case is kept: a missing or external
9
+ * target is still examined, and still reported.
10
+ */
11
+ export declare function haveLiveTargetsPredicate(corpus: Corpus, options?: LinkResolveOptions): Predicate<MdLink>;
12
+ //# sourceMappingURL=live-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-target.d.ts","sourceRoot":"","sources":["../../src/predicates/live-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAe,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAE/E;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,kBAAuB,GAC/B,SAAS,CAAC,MAAM,CAAC,CAQnB"}
@@ -0,0 +1,17 @@
1
+ import { resolveLink } from '../model/resolve-link.js';
2
+ /**
3
+ * Links whose target is not a frozen corpus document (plan 0406), resolved with
4
+ * `resolveLink` and `options` — pass `beLinkedBack()` the same options, so both
5
+ * resolve a link the same way. Every other case is kept: a missing or external
6
+ * target is still examined, and still reported.
7
+ */
8
+ export function haveLiveTargetsPredicate(corpus, options = {}) {
9
+ return {
10
+ description: 'have live targets (not in a frozen folder)',
11
+ test: (link) => {
12
+ const target = resolveLink(link, corpus, options);
13
+ return !(target.kind === 'document' && target.doc.frozen);
14
+ },
15
+ };
16
+ }
17
+ //# sourceMappingURL=live-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-target.js","sourceRoot":"","sources":["../../src/predicates/live-target.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAA2B,MAAM,0BAA0B,CAAA;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAc,EACd,UAA8B,EAAE;IAEhC,OAAO;QACL,WAAW,EAAE,4CAA4C;QACzD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;YACjD,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC3D,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -19,19 +19,28 @@ export interface AdrEnforcementOptions extends PresetBaseOptions {
19
19
  };
20
20
  /** Valid tier numbers. Default `[1,2,3,4,5]`. */
21
21
  readonly tiers?: readonly number[];
22
- /** Verify cited file paths exist and cited `it('…')` titles resolve. Default `true`. */
22
+ /**
23
+ * Verify that the file paths a Mechanism cell cites exist. Default `true`.
24
+ *
25
+ * A cited `it('…')` **title** is not resolved here. Whether it names a real test is
26
+ * `eess-crossvalidate`'s `adrCitationsResolve`, which reads the test AST: a markdown
27
+ * dialect reading test files as text matched `it('r')` against any test beginning with
28
+ * `r`, and could not tell a commented-out test from a live one (bug 0111). Run that
29
+ * check too if your ADRs cite test titles.
30
+ */
23
31
  readonly verifyCitations?: boolean;
24
32
  }
25
33
  /**
26
34
  * OPINIONATED preset: gate a repo's ADRs on the EESS enforcement-tier model.
27
35
  * Gates on *declaration* — an ADR fails for a missing `## Enforcement` table, a
28
- * clause with no valid tier, or (when `verifyCitations`) a citation that doesn't
29
- * resolve; a soft-tier clause declared as such passes.
36
+ * clause with no valid tier, or (when `verifyCitations`) a cited file path that
37
+ * doesn't exist; a soft-tier clause declared as such passes.
30
38
  *
31
39
  * Emits per-rule ids so `overrides` can downgrade/disable individual checks.
32
40
  *
33
- * The citation check resolves two forms — a backticked file path and an
34
- * `it('…')` title. A Mechanism cell that cites something else (a rule id in
41
+ * The citation check resolves one form, a backticked file path. A cited `it('…')`
42
+ * title is `eess-crossvalidate`'s `adrCitationsResolve` to resolve, against the
43
+ * test AST (bug 0111). A Mechanism cell that cites something else (a rule id in
35
44
  * your own architecture tool, a CI job) is not resolved here, and this preset
36
45
  * takes no plugin for it: what counts as a live id is your fact, not the
37
46
  * corpus's. Compose that check from `rows()` + `correspondence()` against the
@@ -1 +1 @@
1
- {"version":3,"file":"adr.d.ts","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAW1C;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAClC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACtE,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,wFAAwF;IACxF,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CACnC;AAuDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CAgDjB"}
1
+ {"version":3,"file":"adr.d.ts","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAW1C;;;;;GAKG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAClC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACtE,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CACnC;AA8BD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,aAAa,EAAE,CAgDjB"}
package/dist/rules/adr.js CHANGED
@@ -1,5 +1,3 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
1
  import { dispatchRule, finishPreset, validateOverrides, not, mergeCollectResults, } from '@nielspeter/eess';
4
2
  import { docs } from '../builders/docs.js';
5
3
  import { haveNameMatching } from '../predicates/document.js';
@@ -11,15 +9,6 @@ import { haveTableRowsSatisfying } from '../conditions/table-rows.js';
11
9
  const notIndexDoc = not(haveNameMatching(/^(README|index)\.md$/i));
12
10
  const RULE_IDS = ['adr/enforcement-declared', 'adr/valid-tiers', 'adr/citations-resolve'];
13
11
  const PATH_RE = /`([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)`/g;
14
- const IT_CITE_RE = /it(?:\.\w+)?\(\s*['"]([^'"]+)['"]/g;
15
- /** Does `testFile` define a test titled `title`? (Text-level; 0059 upgrades to AST.) */
16
- function testDefinesIt(testAbs, title) {
17
- if (!existsSync(testAbs))
18
- return false;
19
- const content = readFileSync(testAbs, 'utf8');
20
- const esc = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21
- return new RegExp(`it(?:\\.\\w+)?\\(\\s*['"\`]${esc}`).test(content);
22
- }
23
12
  function validateTier(ctx, tiers) {
24
13
  const clause = ctx.cells[0] ?? '';
25
14
  const match = ctx.get('tier').match(/[1-5]/);
@@ -32,38 +21,27 @@ function validateCitations(ctx, corpus) {
32
21
  const clause = ctx.cells[0] ?? '';
33
22
  const mech = ctx.get('mechanism');
34
23
  const problems = [];
35
- const citedPaths = [];
36
24
  for (const m of mech.matchAll(PATH_RE)) {
37
25
  const p = m[1];
38
26
  if (p === undefined)
39
27
  continue;
40
- citedPaths.push(p);
41
28
  if (!corpus.fileIndex.has(p)) {
42
29
  problems.push(`clause "${clause}" cites missing file \`${p}\``);
43
30
  }
44
31
  }
45
- const testFiles = citedPaths.filter((p) => p.endsWith('.ts'));
46
- for (const m of mech.matchAll(IT_CITE_RE)) {
47
- const title = m[1];
48
- if (title === undefined)
49
- continue;
50
- const found = testFiles.some((p) => testDefinesIt(join(corpus.root, p), title));
51
- if (!found) {
52
- problems.push(`clause "${clause}" cites it('${title}') not found in ${testFiles.length ? testFiles.join(', ') : 'any cited test file'}`);
53
- }
54
- }
55
32
  return problems;
56
33
  }
57
34
  /**
58
35
  * OPINIONATED preset: gate a repo's ADRs on the EESS enforcement-tier model.
59
36
  * Gates on *declaration* — an ADR fails for a missing `## Enforcement` table, a
60
- * clause with no valid tier, or (when `verifyCitations`) a citation that doesn't
61
- * resolve; a soft-tier clause declared as such passes.
37
+ * clause with no valid tier, or (when `verifyCitations`) a cited file path that
38
+ * doesn't exist; a soft-tier clause declared as such passes.
62
39
  *
63
40
  * Emits per-rule ids so `overrides` can downgrade/disable individual checks.
64
41
  *
65
- * The citation check resolves two forms — a backticked file path and an
66
- * `it('…')` title. A Mechanism cell that cites something else (a rule id in
42
+ * The citation check resolves one form, a backticked file path. A cited `it('…')`
43
+ * title is `eess-crossvalidate`'s `adrCitationsResolve` to resolve, against the
44
+ * test AST (bug 0111). A Mechanism cell that cites something else (a rule id in
67
45
  * your own architecture tool, a CI job) is not resolved here, and this preset
68
46
  * takes no plugin for it: what counts as a live id is your fact, not the
69
47
  * corpus's. Compose that check from `rows()` + `correspondence()` against the
@@ -1 +1 @@
1
- {"version":3,"file":"adr.js","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adr.js","sourceRoot":"","sources":["../../src/rules/adr.ts"],"names":[],"mappings":"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"}
@@ -48,15 +48,19 @@ export interface HonestyAtCloseOptions extends PresetReportOptions {
48
48
  readonly closeInPlace?: boolean;
49
49
  /**
50
50
  * The full `State:` vocabulary this corpus uses. A token outside it is
51
- * **reported** (`ledger/unknown-state`), not ignored. Default: the plan lane's
52
- * enum. A bug-shaped lane passes its own, e.g.
53
- * `['Draft', 'Ready', 'Fixed', 'Rejected', 'Parked']`.
51
+ * **reported** (`ledger/unknown-state`), not ignored. A bug-shaped lane passes its own,
52
+ * e.g. `['Draft', 'Ready', 'Fixed', 'Rejected', 'Parked']`.
53
+ *
54
+ * **Declare it together with {@link terminalStates}, or omit both** for the plan lane's
55
+ * defaults. One alone throws an `ArchConfigError` (bug 0284).
54
56
  */
55
57
  readonly states?: readonly string[];
56
58
  /**
57
- * Which of {@link states} mean *closed*. Default `['Done', "Won't-do"]`; a
58
- * bug lane passes `['Fixed', 'Rejected']`. Members are treated as known states
59
- * whether or not they also appear in {@link states}.
59
+ * Which of {@link states} mean *closed*; a bug lane passes `['Fixed', 'Rejected']`.
60
+ * Members are treated as known states whether or not they also appear in {@link states}.
61
+ *
62
+ * **Declare it together with {@link states}, or omit both** for the defaults
63
+ * (`['Done', "Won't-do"]`). One alone throws an `ArchConfigError` (bug 0284).
60
64
  */
61
65
  readonly terminalStates?: readonly string[];
62
66
  /**