@orkestrel/scaffold 0.0.24 → 0.0.26
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/README.md +5 -0
- package/dist/bin/main.js +45 -4
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +40 -11
- package/dist/host/claude/agents/orkestrel.md +63 -48
- package/dist/host/claude/rules/documentation.md +1 -1
- package/dist/host/guides/guide.md +211 -100
- package/dist/host/guides/scaffold.md +165 -10
- package/dist/host/manifest.json +5 -5
- package/dist/host/tests/config.test.ts +30 -7
- package/dist/src/core/index.cjs +242 -149
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +185 -98
- package/dist/src/core/index.d.ts +185 -98
- package/dist/src/core/index.js +240 -150
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +171 -66
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +140 -26
- package/dist/src/server/index.d.ts +140 -26
- package/dist/src/server/index.js +173 -69
- package/dist/src/server/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# Guide
|
|
2
2
|
|
|
3
|
-
> A
|
|
4
|
-
> surface, method groups, links, and test links; `Source` reflects
|
|
5
|
-
> consumer-supplied file inventory via pure text
|
|
6
|
-
>
|
|
3
|
+
> A pure, I/O-free guides-parity toolkit: `Guide` extracts a markdown guide's documented
|
|
4
|
+
> surface, method groups, links, and test links; `Source` reflects direct declarations and
|
|
5
|
+
> conventional barrel reachability from a consumer-supplied file inventory via pure text
|
|
6
|
+
> scanners (no filesystem or TypeScript compiler API); runtime dependencies provide markdown
|
|
7
|
+
> and contract primitives, while comparison helpers (`missingSymbols`, `findMissing`,
|
|
7
8
|
> `resolveLink`, …) reduce every guides-parity check to `expect([]).toEqual([])`
|
|
8
|
-
> (AGENTS §22). Source: [`src/core`](../../src/core).
|
|
9
|
+
> (AGENTS §22). Source: [`src/core`](../../src/core). Published through `@orkestrel/guide`.
|
|
9
10
|
|
|
10
11
|
The doctrine: a guide is a contract, not prose. `createGuide(markdown)` parses a guide's
|
|
11
12
|
source once (via `@orkestrel/markdown`) into a `GuideInterface` — its `## Surface` identifiers
|
|
12
13
|
(kind-tagged), its `## Methods` interface/method groups, every link, and its `## Tests`
|
|
13
14
|
links, each cached at construction. `createSource({ files, module })` builds a
|
|
14
|
-
`SourceInterface` that reflects
|
|
15
|
-
methods
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
`SourceInterface` that reflects intentional direct declarations, conventional barrel-reachable
|
|
16
|
+
declarations, and interface/class methods by scanning a consumer-gathered file inventory with
|
|
17
|
+
plain-text line scanners, never touching disk itself. A guides-parity test asserts direct
|
|
18
|
+
declarations equal the barrel surface and the barrel surface equals the documented surface,
|
|
19
|
+
in both directions. `parseManifest` reads a `guides/README.md`'s
|
|
19
20
|
`## By concept` table into the list of `{ concept, spec, source, tests }` entries a suite
|
|
20
21
|
iterates to run this check once per documented concept.
|
|
21
22
|
|
|
@@ -25,17 +26,18 @@ iterates to run this check once per documented concept.
|
|
|
25
26
|
|
|
26
27
|
The manifest/extraction shapes every check is built from, from [`types.ts`](../../src/core/types.ts).
|
|
27
28
|
|
|
28
|
-
| Name | Kind | Shape
|
|
29
|
-
| ----------------- | --------- |
|
|
30
|
-
| `ExportKind` | type | `'type' \| 'interface' \| 'const' \| 'function' \| 'class'` — the
|
|
31
|
-
| `SurfaceSymbol` | interface | `{ name, kind }` — one documented / exported symbol.
|
|
32
|
-
| `GuideModule` | type | `string \| readonly string[]` — one source directory, or several
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
29
|
+
| Name | Kind | Shape |
|
|
30
|
+
| ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `ExportKind` | type | `'type' \| 'interface' \| 'const' \| 'function' \| 'class'` — the five-kind reflection population. Comment/template payload and enums are outside it; general package policy does not forbid enums. |
|
|
32
|
+
| `SurfaceSymbol` | interface | `{ name, kind }` — one documented / exported symbol. |
|
|
33
|
+
| `GuideModule` | type | `string \| readonly string[]` — one source directory, or several; `'.'` is the canonical workspace root. |
|
|
34
|
+
| `SourceLine` | interface | `{ source, code, jsdoc }` — one terminator-free physical source line with exact raw text, equal-length projections, and every genuine JSDoc span at its physical column or `undefined`. |
|
|
35
|
+
| `ManifestEntry` | interface | `{ concept, spec, source, tests }` — one `## By concept` manifest row, paths normalized to workspace root. |
|
|
36
|
+
| `MethodGroup` | interface | `{ interface, methods }` — one `#### \`Interface\`` block's documented method names, in table order. |
|
|
37
|
+
| `GuideInterface` | interface | `{ sections, surface, methods, links, tests, patterns }` — the structured, pure view over one parsed guide. See [`## Methods`](#methods). |
|
|
38
|
+
| `SourceInterface` | interface | `{ exports, surface, methods, exists, hidden, examples }` — direct declarations, conventional barrel reachability, members, paths, discipline, and examples. See [`## Methods`](#methods). |
|
|
39
|
+
| `SourceOptions` | interface | `{ files, module }` — exact canonical-segment opaque workspace-relative inventory keys plus the canonicalized module scope to reflect. |
|
|
40
|
+
| `DeclarationHead` | interface | `{ text, end }` — a declaration head joined into one line (across an oxfmt-wrapped signature) plus the index of the line ending in `{`. |
|
|
39
41
|
|
|
40
42
|
### Constants
|
|
41
43
|
|
|
@@ -55,43 +57,47 @@ on, from [`constants.ts`](../../src/core/constants.ts).
|
|
|
55
57
|
Pure, total leaves from [`helpers.ts`](../../src/core/helpers.ts) — the building blocks
|
|
56
58
|
`parsers.ts`'s extractors and a consumer's parity test both reach for directly.
|
|
57
59
|
|
|
58
|
-
| Name
|
|
59
|
-
|
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
60
|
+
| Name | Kind | Signature | Behavior |
|
|
61
|
+
| ---------------------- | -------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `normalizeDirectories` | function | `(module: GuideModule) => readonly string[]` | Canonicalizes through `resolvePath`, uses `'.'` for root, and removes duplicates in first-seen order. |
|
|
63
|
+
| `selectModuleKeys` | function | `(files: Readonly<Record<string, string>>, module: GuideModule) => readonly string[]` | Selects exact canonical-segment opaque `.ts` keys under any scope and excludes every selected exact `index.ts` plus `*.test.ts`; sorted. |
|
|
64
|
+
| `hasCanonicalSegments` | function | `(key: string) => boolean` | Rejects empty, `.` and `..` slash-separated segments without normalization while retaining ordinary dotfiles. |
|
|
65
|
+
| `symbolKey` | function | `(symbol: SurfaceSymbol) => string` | The bijection key for a surface symbol — `${kind} ${name}` — so a symbol comparison diffs (name, kind) pairs, not names alone. |
|
|
66
|
+
| `findMissing` | function | `(names: readonly string[], source: readonly string[]) => readonly string[]` | The names present in `names` but absent from `source` — the set-difference behind a both-directions bijection assertion. |
|
|
67
|
+
| `missingSymbols` | function | `(symbols: readonly SurfaceSymbol[], source: readonly SurfaceSymbol[]) => readonly string[]` | The `symbolKey` set-difference between two symbol lists. |
|
|
68
|
+
| `extractSourceLines` | function | `(source: string) => readonly SourceLine[]` | Equal-length physical source, code, and JSDoc projection with bounded literal Unicode identifier slash state. |
|
|
69
|
+
| `isExternalLink` | function | `(href: string) => boolean` | Whether a link `href` should be skipped by guides-parity link checks — an external scheme (`EXTERNAL_SCHEMES`) or a bare `#` anchor. |
|
|
70
|
+
| `resolveLink` | function | `(file: string, target: string) => string` | Derives a declaring file's directory, including workspace-root files, then delegates to `resolvePath`. |
|
|
71
|
+
| `resolvePath` | function | `(directory: string, target: string) => string` | Sole dot-segment reducer; returns `'.'` when no segment remains and preserves every excess leading parent. |
|
|
72
|
+
| `firstCode` | function | `(nodes: readonly InlineNode[]) => string \| undefined` | The first code-span value found by descending an inline node list, following into `emphasis` / `link` children. |
|
|
73
|
+
| `identifierOf` | function | `(code: string) => string` | The identifier prefix of a code-span text — everything before its first `<`, trimmed (strips generic-parameter annotation). |
|
|
74
|
+
| `kindIndex` | function | `(table: TableNode) => number \| undefined` | The index of a table's `Kind` column, found by its header text so it survives column reordering. |
|
|
75
|
+
| `cellLinks` | function | `(cell: readonly InlineNode[]) => readonly string[]` | The link hrefs found within one table cell's inline content, in walk order. |
|
|
76
|
+
| `findUnexampled` | function | `(names: readonly string[], fences: readonly string[], examples: readonly string[]) => readonly string[]` | The names with no fence mention (word boundary) and no `@example` membership — the EX check's core comparison. |
|
|
77
|
+
| `fenceImports` | function | `(fence: string) => readonly { specifier: string, names: readonly string[] }[]` | Parses a fence's `import` statements into per-specifier imported identifier names — the FI check's core comparison. |
|
|
73
78
|
|
|
74
79
|
### Parsers
|
|
75
80
|
|
|
76
81
|
The guide/manifest extraction pipeline, from [`parsers.ts`](../../src/core/parsers.ts) —
|
|
77
82
|
the orchestration `Guide` composes out of `helpers.ts`'s leaves.
|
|
78
83
|
|
|
79
|
-
| Name
|
|
80
|
-
|
|
|
81
|
-
| `exportsFrom`
|
|
82
|
-
| `hiddenFrom`
|
|
83
|
-
| `joinHead`
|
|
84
|
-
| `declarationBody`
|
|
85
|
-
| `memberMethods`
|
|
86
|
-
| `sectionBlocks`
|
|
87
|
-
| `extractSurface`
|
|
88
|
-
| `extractMethods`
|
|
89
|
-
| `extractLinks`
|
|
90
|
-
| `extractTests`
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
84
|
+
| Name | Kind | Signature | Behavior |
|
|
85
|
+
| --------------------- | -------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `exportsFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | Direct five-kind exports over projected lines with an uninterrupted column-zero head; projection never widens membership. |
|
|
87
|
+
| `hiddenFrom` | function | `(source: string) => readonly SurfaceSymbol[]` | The non-exported mirror with the same projected, uninterrupted column-zero head and five-kind population. |
|
|
88
|
+
| `joinHead` | function | `(lines: readonly string[], start: number) => DeclarationHead \| undefined` | Joins a declaration head starting at `start` into one space-separated line, consuming lines until the first ending in `{`. |
|
|
89
|
+
| `declarationBody` | function | `(source: string, keyword: 'class' \| 'interface', name: string) => readonly string[]` | Selects a real head/close from projected lines and returns the aligned raw body for JSDoc evidence. |
|
|
90
|
+
| `memberMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members on one projection of the body; commented candidates, getters, setters, `static`, and `#` privates never count. |
|
|
91
|
+
| `sectionBlocks` | function | `(document: MarkdownDocument, heading: string) => readonly BlockNode[]` | The block nodes under a named `##` heading, up to the next `##`-or-higher heading (or the document's end). |
|
|
92
|
+
| `extractSurface` | function | `(document: MarkdownDocument) => readonly SurfaceSymbol[]` | Every `## Surface` identifier: each table's rows union every backticked H3 entity heading, deduped by `symbolKey`. |
|
|
93
|
+
| `extractMethods` | function | `(document: MarkdownDocument) => readonly MethodGroup[]` | One `MethodGroup` per documented behavioral interface in `## Methods` — an H4 code span sets the interface, the following table lists its methods. |
|
|
94
|
+
| `extractLinks` | function | `(document: MarkdownDocument) => readonly string[]` | Every link href in the guide document, including table cells — a full, depth-first AST walk. |
|
|
95
|
+
| `extractTests` | function | `(document: MarkdownDocument) => readonly string[]` | The relative test links declared under `## Tests`. |
|
|
96
|
+
| `extractExampleLines` | function | `(lines: readonly SourceLine[]) => readonly SourceLine[]` | The next physical candidate after the authoritative exact `@example` span in a leading chain. |
|
|
97
|
+
| `examplesFrom` | function | `(source: string) => readonly string[]` | Matches exported functions against shared eligible genuine JSDoc adjacency and aligned code. |
|
|
98
|
+
| `exampleMethods` | function | `(lines: readonly string[]) => readonly string[]` | Matches callable members against the same shared eligible genuine JSDoc adjacency and aligned code. |
|
|
99
|
+
| `extractPatterns` | function | `(document: MarkdownDocument) => readonly string[]` | Every fenced `ts` code block's body text anywhere in the guide document — a full AST walk. |
|
|
100
|
+
| `parseManifest` | function | `(markdown: string, directory: string) => readonly ManifestEntry[]` | Resolves manifest links and canonicalizes Source values through `normalizeDirectories`, preserving one-versus-many shape. |
|
|
95
101
|
|
|
96
102
|
### Shapers
|
|
97
103
|
|
|
@@ -141,13 +147,17 @@ surface.
|
|
|
141
147
|
### `Source`
|
|
142
148
|
|
|
143
149
|
The implementing class of `SourceInterface`, from [`Source.ts`](../../src/core/Source.ts). A
|
|
144
|
-
pure reflection over a consumer-supplied file inventory (root-relative path → file text) plus
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`
|
|
150
|
-
|
|
150
|
+
pure reflection over a consumer-supplied file inventory (root-relative path → file text) plus a
|
|
151
|
+
module scope. `exports()` inventories direct `type`, `interface`, `const`, `function`, and
|
|
152
|
+
`class` declarations in the selected canonical directories' exact opaque module keys over
|
|
153
|
+
comment/template-excluded projected code lines; `enum` is outside this reflection population without being forbidden by
|
|
154
|
+
general package policy;
|
|
155
|
+
`surface()` inventories declarations reachable through each selected directory's conventional
|
|
156
|
+
root barrel. Both projections are computed on first access, cached, deduplicated by name and
|
|
157
|
+
kind, and sorted by name. Member structure comes from projected lines while raw bodies preserve
|
|
158
|
+
JSDoc evidence. `Source` never uses the
|
|
159
|
+
TypeScript compiler API or filesystem; the consumer gathers `files` however its environment
|
|
160
|
+
allows. See [`## Methods`](#methods) for the public call-signature surface.
|
|
151
161
|
|
|
152
162
|
## Methods
|
|
153
163
|
|
|
@@ -167,19 +177,20 @@ backticked name (AGENTS §22).
|
|
|
167
177
|
|
|
168
178
|
#### `SourceInterface`
|
|
169
179
|
|
|
170
|
-
| Method | Returns | Behavior
|
|
171
|
-
| ---------- | -------------------------- |
|
|
172
|
-
| `exports` | `readonly SurfaceSymbol[]` |
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
180
|
+
| Method | Returns | Behavior |
|
|
181
|
+
| ---------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
182
|
+
| `exports` | `readonly SurfaceSymbol[]` | Direct `type`, `interface`, `const`, `function`, and `class` declarations in the selected module keys. |
|
|
183
|
+
| `surface` | `readonly SurfaceSymbol[]` | Every declaration reachable through the selected directories' conventional root `index.ts` barrels. |
|
|
184
|
+
| `methods` | `readonly string[]` | The call-signature members of the `class` / `interface` named `name`. |
|
|
185
|
+
| `exists` | `boolean` | Whether a workspace-root-relative path exists in the inventory. |
|
|
186
|
+
| `hidden` | `readonly SurfaceSymbol[]` | Every module-scope declaration LACKING `export` (AGENTS §5). |
|
|
187
|
+
| `examples` | `readonly string[]` | The exported functions (or, given `name`, members) whose eligible leading JSDoc chain ends in an exact block-position `@example` span. |
|
|
177
188
|
|
|
178
189
|
## The extraction model
|
|
179
190
|
|
|
180
191
|
`Guide` parses a guide's markdown once (via `@orkestrel/markdown`'s `createMarkdown`) and
|
|
181
|
-
caches
|
|
182
|
-
— so every accessor is a cheap array return, not a re-parse. `extractSurface` scopes to the
|
|
192
|
+
caches six projections at construction — `sections`, `surface`, `methods`, `links`, `tests`,
|
|
193
|
+
`patterns` — so every accessor is a cheap array return, not a re-parse. `extractSurface` scopes to the
|
|
183
194
|
`## Surface` section (`sectionBlocks`) and unions two sources of identifiers: every table's
|
|
184
195
|
column-0 code span (kind read from the column whose header text is `Kind`, located
|
|
185
196
|
positionally so it survives reordering) and every backticked H3 entity heading (a class
|
|
@@ -189,32 +200,98 @@ table becomes that interface's `MethodGroup`. Both extractors normalize every id
|
|
|
189
200
|
through `identifierOf`, stripping a generic-parameter annotation (`` `WidgetInterface<T>` ``
|
|
190
201
|
→ `WidgetInterface`) so the bijection key is always the bare name. `extractLinks` walks the
|
|
191
202
|
whole AST for every `link` node (table cells included); `extractTests` does the same walk
|
|
192
|
-
scoped to the `## Tests` section only
|
|
203
|
+
scoped to the `## Tests` section only; and `extractPatterns` walks the whole AST for every
|
|
204
|
+
fenced `ts` code block.
|
|
205
|
+
|
|
206
|
+
`parseManifest(markdown, directory)` resolves every Spec, Source, and Tests link through
|
|
207
|
+
`resolvePath(directory, target)`, so root, nested, and dotted directory names are ordinary path
|
|
208
|
+
components. Source links then canonicalize through `normalizeDirectories`: `'.'` is workspace root and
|
|
209
|
+
duplicates collapse in first-seen order. `resolvePath` owns the only forward-slash dot-segment reducer, returns `'.'` when all components cancel, and retains every
|
|
210
|
+
excess leading parent. `resolveLink(file, target)` adds the declaring-file boundary: it derives
|
|
211
|
+
the directory before the final slash, treats a slashless file as workspace-root, and delegates.
|
|
212
|
+
Neither helper consults the filesystem, infers extensions, or guesses whether a dotted component
|
|
213
|
+
is a file.
|
|
193
214
|
|
|
194
215
|
`Source` never parses markdown or touches disk — it scans a consumer-supplied file
|
|
195
|
-
inventory's
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
216
|
+
inventory's text with deliberately narrow physical-line grammars. `extractSourceLines` is the
|
|
217
|
+
sole character engine and emits one `SourceLine` per LF/CRLF physical line plus the final line:
|
|
218
|
+
`source` is exact, `code` is equal-length with real comments and complete template tokens masked,
|
|
219
|
+
and `jsdoc` is equal-length with every genuine span retained at its physical column or is
|
|
220
|
+
`undefined`. A genuine JSDoc capture starts only from ordinary reflection code, never inside an
|
|
221
|
+
open comment, raw template, or template substitution. The engine traverses escapes, nested
|
|
222
|
+
template substitutions, strings, regex character classes, comments, and division/regex contexts
|
|
223
|
+
only to identify those spans; it does not parse TypeScript. Literal ECMAScript Unicode identifiers
|
|
224
|
+
participate in bounded slash-state recognition, but escaped identifier spellings are not decoded.
|
|
225
|
+
Regex recognition is a bounded lexical goal: because the projection cannot infer whether `}`
|
|
226
|
+
closes an expression or a statement/declaration block, slash immediately after bare `}` is
|
|
227
|
+
division and a post-brace regex statement needs an explicit `;`. General semicolonless
|
|
228
|
+
declaration/ASI classification is also outside this finite projector; callers use an explicit
|
|
229
|
+
`;` before a slash-leading statement after such a declaration. Projection preserves columns;
|
|
230
|
+
each consumer still owns membership. Direct and hidden heads remain uninterrupted and
|
|
231
|
+
column-zero, while barrel rows retain their separate whitespace-tolerant whole-line grammar.
|
|
232
|
+
|
|
233
|
+
`extractExampleLines` walks only `SourceLine` records. A genuine JSDoc opener is eligible only
|
|
234
|
+
when it is the first non-whitespace source material. Within a leading whitespace-separated chain,
|
|
235
|
+
each later span replaces the earlier one and is authoritative. Only an exact block-position
|
|
236
|
+
`@example` tag qualifies; same-line title text is allowed. Source material between or after spans
|
|
237
|
+
severs association, a leading JSDoc on the next line replaces pending state, and any other next
|
|
238
|
+
physical record is returned once as the candidate. `examplesFrom` and `exampleMethods` share this
|
|
239
|
+
adjacency parser and apply their distinct exported-function and callable-member grammars only to
|
|
240
|
+
`code`.
|
|
241
|
+
|
|
242
|
+
Across the `.ts` module keys under each selected directory, excluding its root `index.ts` and
|
|
243
|
+
every `*.test.ts`, `exportsFrom` matches
|
|
244
|
+
`^export (?:async )?(function\*?|class|const|interface|type) (\w+)` per projected line, deduped by
|
|
245
|
+
(kind, name). `hiddenFrom` applies the same five-kind head grammar without `export`. Comment and
|
|
246
|
+
template payload, enums, `let`, `var`, and other TypeScript declaration forms are outside these
|
|
247
|
+
two populations; enum exclusion describes reflection scope, not a general package-policy ban.
|
|
248
|
+
`declarationBody` locates a named real `export class` / `export interface` head and exact
|
|
249
|
+
column-zero close in projected lines (joining an oxfmt-wrapped signature via `joinHead`), then
|
|
250
|
+
returns the aligned raw body. `memberMethods` projects that body once and matches
|
|
200
251
|
`^\t(?:async )?\*?(\w+)(<[^>]*>)?\??\(` against those body lines — plain / `async` /
|
|
201
252
|
generator / optional methods count; getters, setters, `static` members, and `#` privates
|
|
202
253
|
never match (their keyword or sigil breaks the `name(` shape), and `constructor` is filtered
|
|
203
|
-
out of `Source.methods`. `
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
254
|
+
out of `Source.methods`. `selectModuleKeys` scopes the inventory to one `GuideModule`'s `.ts` files,
|
|
255
|
+
excluding each scope directory's own `index.ts` and any `*.test.ts` file. `Source.hidden()`
|
|
256
|
+
mechanically asserts AGENTS §5's export-discipline rule and catches a hidden five-kind
|
|
257
|
+
declaration the surface bijection alone would never see.
|
|
258
|
+
|
|
259
|
+
`Source.surface()` starts only at exact `index.ts` for canonical `'.'`, or exact
|
|
260
|
+
`<directory>/index.ts` for each nested directory returned by `normalizeDirectories(module)`.
|
|
261
|
+
Inventory keys remain opaque and are never normalized; `hasCanonicalSegments` rejects empty,
|
|
262
|
+
`.` and `..` segments while retaining dotfiles. Both the initial index and every resolved target
|
|
263
|
+
must be canonical after relative-row reduction; a parent row that reduces to a canonical key
|
|
264
|
+
remains valid. A complete row must be equivalent to
|
|
265
|
+
`export * from './target.js'`: the target starts with `./` or `../` and ends in `.js`;
|
|
266
|
+
surrounding whitespace, either quote, an optional semicolon, and an optional trailing `//`
|
|
267
|
+
comment is accepted, while the inactive quote delimiter remains target data. The same projection
|
|
268
|
+
masks actual comment/template spans, preserving valid row code around them and markers inside a
|
|
269
|
+
quoted target. Arbitrary trailing source is rejected. Only the terminal `.js` becomes `.ts`;
|
|
270
|
+
`resolveLink(currentIndex, target)` derives the current index file's directory and delegates to
|
|
271
|
+
`resolvePath`, the only dot-segment reducer. Exact workspace-root `index.ts` and nested targets
|
|
272
|
+
ending `/index.ts` recurse as barrels, while another exact `.ts` target contributes its direct
|
|
273
|
+
`exportsFrom` declarations. One visited set terminates self-cycles, multi-index cycles, repeated
|
|
274
|
+
rows, and diamonds. `symbolKey` deduplicates same-name/same-kind rows while retaining
|
|
275
|
+
same-name/different-kind rows, and the final list uses the same name sort as `exports()`.
|
|
276
|
+
|
|
277
|
+
Missing roots and targets, empty barrels, and unsupported rows contribute nothing without
|
|
278
|
+
throwing, while valid siblings continue. Named, default, namespace, type-only, non-relative, and
|
|
279
|
+
extensionless re-exports are outside the population. So are enums, declarations written directly
|
|
280
|
+
in an `index.ts`, and re-export syntax inside a terminal non-index target. Ignoring these forms does not
|
|
281
|
+
validate them: repository barrel policy, typechecking, and builds own validity. There is no
|
|
282
|
+
filesystem, package-map, alias, config, directory-index fallback, or general TypeScript module
|
|
283
|
+
resolution. The supplied inventory is never mutated; the first computed result is cached and the
|
|
284
|
+
same readonly array instance is returned thereafter.
|
|
209
285
|
|
|
210
286
|
## The check catalog
|
|
211
287
|
|
|
212
288
|
Every guides-parity check reduces to `expect([]).toEqual([])`, paired with a non-vacuousness
|
|
213
289
|
guard so a renamed heading fails loudly instead of passing on an empty extraction:
|
|
214
290
|
|
|
215
|
-
- **SB —
|
|
216
|
-
|
|
217
|
-
|
|
291
|
+
- **SB — Direct/barrel/guide surface parity (kind folded in).** `missingSymbols` proves all four
|
|
292
|
+
directions: direct declarations → barrel surface, barrel surface → direct declarations, barrel
|
|
293
|
+
surface → guide surface, and guide surface → barrel surface. Every comparison uses `symbolKey`,
|
|
294
|
+
so a declaration may drift in neither name nor kind. Guard: `guide.surface().length > 0`.
|
|
218
295
|
- **MB — Methods bijection + class-no-extra.** Per `MethodGroup`, its `methods` vs
|
|
219
296
|
`source.methods(group.interface)`, `findMissing` both directions; then, by the
|
|
220
297
|
`XInterface → X` naming convention, `findMissing(source.methods('X'), group.methods)` must
|
|
@@ -227,15 +304,24 @@ guard so a renamed heading fails loudly instead of passing on an empty extractio
|
|
|
227
304
|
- **NV — Non-vacuousness.** `parseManifest` yields at least one entry; each guide's
|
|
228
305
|
`surface()` and every `MethodGroup` is non-empty — the guard behind every other check.
|
|
229
306
|
- **EX — Examples presence.** A documented symbol "has an example" when its bare name
|
|
230
|
-
appears (word boundary) in any of `guide.patterns()`'s fence bodies, OR its source
|
|
231
|
-
JSDoc
|
|
307
|
+
appears (word boundary) in any of `guide.patterns()`'s fence bodies, OR its source has
|
|
308
|
+
an immediately preceding eligible leading JSDoc chain whose final authoritative span carries
|
|
309
|
+
an exact block-position `@example` tag, with optional title text,
|
|
310
|
+
(`source.examples()` / `source.examples(name)`).
|
|
232
311
|
Applies to every `function`-kind `Surface` symbol and every `MethodGroup` member.
|
|
233
312
|
Presence-only — fence and JSDoc CONTENT are never checked. `findUnexampled` is the
|
|
234
313
|
comparison. Guard: the SB/MB extractions this check reuses already prove non-vacuous.
|
|
235
314
|
- **FI — Fence-import reality.** Every `import { ... } from 'specifier'` in a
|
|
236
315
|
`guide.patterns()` fence, for a SELF specifier (this repo's own package name / path
|
|
237
|
-
alias), imports only names that exist in `source.
|
|
238
|
-
statement; `findMissing` diffs the imported names against
|
|
316
|
+
alias), imports only names that exist in `source.surface()`. `fenceImports` parses the
|
|
317
|
+
statement; `findMissing` diffs the imported names against the public/barrel surface's names.
|
|
318
|
+
|
|
319
|
+
Permanent controls bind the SB population boundaries through production `Source`, `Guide`,
|
|
320
|
+
`missingSymbols`, and `symbolKey`: a stranded direct declaration must be missing from the barrel;
|
|
321
|
+
a phantom Guide row must be missing from the barrel; kind drift must fail in both barrel/Guide
|
|
322
|
+
directions; a barrel-only declaration outside `selectModuleKeys()` must be missing from direct exports;
|
|
323
|
+
a correlated commented declaration must remain absent from direct and barrel populations while
|
|
324
|
+
failing Guide-to-barrel; and a workspace-root `index.ts` hop must reach its real terminal symbol.
|
|
239
325
|
|
|
240
326
|
## The pure file-inventory model
|
|
241
327
|
|
|
@@ -245,7 +331,8 @@ CONSUMER gathers however their runtime allows: a recursive `node:fs` walk in a N
|
|
|
245
331
|
run, `import.meta.glob('/**/*.ts', { eager: true, query: '?raw', import: 'default' })` in a
|
|
246
332
|
browser/vitest run, or a static bundle in any other environment. This keeps the package
|
|
247
333
|
itself environment-agnostic while every check still runs against real, on-disk truth in the
|
|
248
|
-
consumer's own test.
|
|
334
|
+
consumer's own test. The inventory must include each selected module's root `index.ts` and every
|
|
335
|
+
reachable exact `.ts` target for `surface()` to observe them; absent keys remain empty reflection.
|
|
249
336
|
|
|
250
337
|
## Patterns
|
|
251
338
|
|
|
@@ -266,12 +353,14 @@ import { createSource } from '@orkestrel/guide'
|
|
|
266
353
|
|
|
267
354
|
const source = createSource({
|
|
268
355
|
files: {
|
|
356
|
+
'src/core/index.ts': "export * from './Guide.js'\nexport * from './types.js'\n",
|
|
269
357
|
'src/core/Guide.ts': 'export class Guide {}\n',
|
|
270
358
|
'src/core/types.ts': 'export interface GuideInterface {\n\tsections(): void\n}\n',
|
|
271
359
|
},
|
|
272
360
|
module: 'src/core',
|
|
273
361
|
})
|
|
274
362
|
source.exports() // [{ name: 'Guide', kind: 'class' }, { name: 'GuideInterface', kind: 'interface' }]
|
|
363
|
+
source.surface() // [{ name: 'Guide', kind: 'class' }, { name: 'GuideInterface', kind: 'interface' }]
|
|
275
364
|
source.methods('GuideInterface') // ['sections']
|
|
276
365
|
source.exists('src/core/Guide.ts') // true
|
|
277
366
|
```
|
|
@@ -283,30 +372,52 @@ import { createGuide, createSource, missingSymbols } from '@orkestrel/guide'
|
|
|
283
372
|
|
|
284
373
|
const guide = createGuide('## Surface\n\n| Name | Kind |\n| --- | --- |\n| `Guide` | class |')
|
|
285
374
|
const source = createSource({
|
|
286
|
-
files: {
|
|
375
|
+
files: {
|
|
376
|
+
'src/core/index.ts': "export * from './Guide.js'\n",
|
|
377
|
+
'src/core/Guide.ts': 'export class Guide {}\n',
|
|
378
|
+
},
|
|
287
379
|
module: 'src/core',
|
|
288
380
|
})
|
|
289
381
|
|
|
290
|
-
//
|
|
291
|
-
missingSymbols(source.exports(),
|
|
292
|
-
missingSymbols(
|
|
382
|
+
// Direct declarations, public barrel, and guide surface agree in all four directions.
|
|
383
|
+
missingSymbols(source.exports(), source.surface()) // []
|
|
384
|
+
missingSymbols(source.surface(), source.exports()) // []
|
|
385
|
+
missingSymbols(source.surface(), guide.surface()) // []
|
|
386
|
+
missingSymbols(guide.surface(), source.surface()) // []
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Project source into physical code lines
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import { extractSourceLines } from '@orkestrel/guide'
|
|
393
|
+
|
|
394
|
+
extractSourceLines('export const visible = true // note\n')
|
|
395
|
+
// [{ source: 'export const visible = true // note', code: 'export const visible = true ', jsdoc: undefined }, ...]
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Resolve directory and file targets
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { resolveLink, resolvePath } from '@orkestrel/guide'
|
|
402
|
+
|
|
403
|
+
resolvePath('guides/nested', './spec.md') // 'guides/nested/spec.md'
|
|
404
|
+
resolveLink('index.ts', './root.ts') // 'root.ts'
|
|
293
405
|
```
|
|
294
406
|
|
|
295
407
|
## Tests
|
|
296
408
|
|
|
297
|
-
- [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) —
|
|
298
|
-
- [`tests/src/core/parsers.test.ts`](../../tests/src/core/parsers.test.ts) —
|
|
409
|
+
- [`tests/src/core/helpers.test.ts`](../../tests/src/core/helpers.test.ts) — direct `SourceLine`, lexical, JSDoc-alignment, canonical-key, runtime-name, `resolvePath`, and `resolveLink` invariants; all remaining helper leaves.
|
|
410
|
+
- [`tests/src/core/parsers.test.ts`](../../tests/src/core/parsers.test.ts) — projected five-kind direct/hidden reflection, genuine JSDoc example adjacency, faux JSDoc exclusion, nested-directory `parseManifest`, and every guide/manifest extractor.
|
|
299
411
|
- [`tests/src/core/validators.test.ts`](../../tests/src/core/validators.test.ts) — `isExportKind` / `isSurfaceSymbol` / `isMethodGroup` / `isManifestEntry`.
|
|
300
412
|
- [`tests/src/core/shapers.test.ts`](../../tests/src/core/shapers.test.ts) — per-shape guard exactness, JSON Schema essentials, seeded generate round-trips, parse rebuilds.
|
|
301
413
|
- [`tests/src/core/factories.test.ts`](../../tests/src/core/factories.test.ts) — `createGuide` / `createSource` + the compiled symbol/group/manifest contracts.
|
|
302
|
-
- [`tests/src/core/Guide.test.ts`](../../tests/src/core/Guide.test.ts) — `Guide`'s
|
|
303
|
-
- [`tests/src/core/Source.test.ts`](../../tests/src/core/Source.test.ts) —
|
|
304
|
-
- [`tests/
|
|
414
|
+
- [`tests/src/core/Guide.test.ts`](../../tests/src/core/Guide.test.ts) — `Guide`'s six cached projections and production barrel/Guide phantom and kind-drift controls.
|
|
415
|
+
- [`tests/src/core/Source.test.ts`](../../tests/src/core/Source.test.ts) — direct/barrel projections, lexical and JSDoc regressions, canonical-key populations, root and nested indexes, exact row grammar, graph invariants, and correlated population controls.
|
|
416
|
+
- [`tests/fixtures/broken/stranded-export`](../../tests/fixtures/broken/stranded-export) — permanent negative control: its guide and direct declarations agree while its conventional barrel omits `strandedExport`.
|
|
417
|
+
- [`tests/guides.test.ts`](../../tests/guides.test.ts) — the drop-in guides-parity suite, run against THIS repo's own `guides/README.md` manifest — the self-dogfooding acceptance criterion.
|
|
305
418
|
|
|
306
419
|
## See also
|
|
307
420
|
|
|
308
421
|
- `AGENTS.md` (workspace root) — the rules; §22 documentation-as-contracts.
|
|
309
|
-
- `PROPOSAL.md` (workspace root) — the design proposal this package implements; §5 the check catalog, §6 the drop-in, §7 source-scanning fidelity.
|
|
310
422
|
- [`README.md`](../README.md) — the guides index.
|
|
311
|
-
- [`markdown.md`](markdown.md) — the dependency mirror for `@orkestrel/markdown`, the AST/parse layer `Guide`
|
|
312
|
-
</content>
|
|
423
|
+
- [`markdown.md`](markdown.md) — the dependency mirror for `@orkestrel/markdown`, the AST/parse layer `Guide` is built on.
|