geneseed 3.1.3 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/README.md +2 -2
- package/SHIPPED.md +3 -3
- package/bin/build-driver.mjs +127 -79
- package/bin/geneseed-cli.mjs +42 -84
- package/bin/geneseed-hook.mjs +5 -2
- package/harness.config.json +1 -1
- package/js/README.md +1 -10
- package/js/build/emit-claude.mjs +4 -5
- package/js/build/emit-common.mjs +63 -114
- package/js/build/emit-opencode.mjs +74 -66
- package/js/build/generate.mjs +4 -6
- package/js/build/render.mjs +13 -13
- package/js/build/source.mjs +7 -6
- package/js/build/stubs.mjs +25 -33
- package/js/build/themes.mjs +1 -1
- package/js/build/version.mjs +1 -3
- package/js/hosts/hooks.mjs +3 -3
- package/js/hosts/hosts.mjs +2 -4
- package/js/hosts/installs.mjs +35 -28
- package/js/hosts/link.mjs +1 -1
- package/js/hosts/native.mjs +37 -19
- package/js/hosts/opencode.mjs +39 -28
- package/js/hosts/settings.mjs +33 -110
- package/js/inspect/checks-authoring.mjs +5 -4
- package/js/inspect/checks-build.mjs +1 -1
- package/js/inspect/diff.mjs +1 -3
- package/js/inspect/doctor.mjs +59 -44
- package/js/inspect/excludes.mjs +4 -10
- package/js/inspect/inventory.mjs +37 -55
- package/js/inspect/registry.mjs +4 -7
- package/js/inspect/scan.mjs +8 -15
- package/js/inspect/status.mjs +1 -1
- package/js/lib/fs.mjs +34 -0
- package/js/lib/json.mjs +28 -1
- package/js/lib/paths.mjs +0 -10
- package/js/lib/udiff.mjs +11 -16
- package/js/maintain/memory.mjs +2 -4
- package/js/maintain/migrate.mjs +3 -3
- package/js/maintain/uninstall.mjs +90 -78
- package/js/maintain/update.mjs +2 -2
- package/js/ui/anim.mjs +1 -1
- package/js/ui/menu.mjs +1 -1
- package/js/ui/tui.mjs +3 -20
- package/js/web/actions.mjs +230 -217
- package/js/web/activity.mjs +71 -89
- package/js/web/api.mjs +133 -224
- package/js/web/daemon.mjs +92 -110
- package/js/web/docs.mjs +66 -111
- package/js/web/graph.mjs +0 -0
- package/js/web/handler.mjs +78 -97
- package/js/web/jobs.mjs +124 -165
- package/js/web/routes.mjs +73 -152
- package/js/web/server.mjs +47 -82
- package/package.json +1 -1
- package/src/agents/explorer.md +2 -1
- package/src/doctrines/process.md +4 -2
- package/src/laws/universal.md +55 -43
- package/src/skills/clarify.md +1 -1
- package/src/skills/forge-mcp.md +2 -1
- package/src/skills/fresh-eyes.md +1 -1
- package/src/skills/ingest.md +2 -1
- package/src/skills/parallel-agents.md +1 -1
- package/src/skills/ponytail.md +2 -2
- package/src/skills/research.md +4 -2
- package/themes/_TEMPLATE.json +2 -0
- package/themes/biker.json +2 -0
- package/themes/commentator.json +2 -0
- package/themes/cyberpunk.json +2 -0
- package/themes/gamer.json +2 -0
- package/themes/imperial.json +2 -0
- package/themes/joker.json +2 -0
- package/themes/marvin.json +2 -0
- package/themes/mean.json +2 -0
- package/themes/military.json +2 -0
- package/themes/neutral.json +2 -0
- package/themes/pirate.json +2 -0
- package/themes/sports.json +2 -0
- package/themes/verstappen.json +2 -0
- package/themes/wizard.json +2 -0
- package/web/dist/assets/{Activity-B49fE_ug.js → Activity-BTCG2gMl.js} +1 -1
- package/web/dist/assets/{ActivityDetail-abt7hzD2.js → ActivityDetail-julsmPDB.js} +1 -1
- package/web/dist/assets/CatalogRow-DqS4e5XM.js +1 -0
- package/web/dist/assets/Diff-CIrGbiqm.js +5 -0
- package/web/dist/assets/Doctor-CpI53oHY.js +1 -0
- package/web/dist/assets/{Graph-DvYMtlvl.js → Graph-CS4tA7YS.js} +1 -1
- package/web/dist/assets/Harness-vRYs11kX.js +1 -0
- package/web/dist/assets/Laws-BjbHJrJt.js +3 -0
- package/web/dist/assets/Library-CjC7BAWN.js +1 -0
- package/web/dist/assets/{marked.esm-JKvyS0Pp.js → Markdown-kgpg9EQM.js} +15 -15
- package/web/dist/assets/Profile-CHr-PPf6.js +1 -0
- package/web/dist/assets/{Rules-CfsV1EVy.js → Rules-CsC6hfqk.js} +1 -1
- package/web/dist/assets/Skills-BvatJGfe.js +1 -0
- package/web/dist/assets/{StatusBadge-C9bZOEZk.js → StatusBadge-gRrak0nO.js} +1 -1
- package/web/dist/assets/{activity-Cc0n2jwx.js → activity-B0-FXZCj.js} +1 -1
- package/web/dist/assets/index-BnRWQbZb.js +12 -0
- package/web/dist/assets/index-Bx3I7cRW.js +2 -0
- package/web/dist/assets/index-D0f9tJlA.css +1 -0
- package/web/dist/assets/index-DJOhOwwR.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +42 -71
- package/web/dist/assets/Diff-D3_bVkih.js +0 -5
- package/web/dist/assets/Doctor-hXp7srsx.js +0 -1
- package/web/dist/assets/Harness-BIWei7N5.js +0 -1
- package/web/dist/assets/Laws-CxmnRdwt.js +0 -3
- package/web/dist/assets/Library-MjbOkHIW.js +0 -1
- package/web/dist/assets/Markdown-Dx_w0faR.js +0 -1
- package/web/dist/assets/Profile-BRFvv2gr.js +0 -1
- package/web/dist/assets/Skills-DArA6IHS.js +0 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +0 -1
- package/web/dist/assets/index-DC3-GlCY.js +0 -1
- package/web/dist/assets/index-DP035wPa.js +0 -12
- package/web/dist/assets/index-dHi-7M-F.js +0 -2
package/js/web/docs.mjs
CHANGED
|
@@ -1,44 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The web console's Docs pages
|
|
3
|
-
* `_web_core.py` builds from `docs/web/`.
|
|
2
|
+
* The web console's Docs pages, and the registry built from `docs/web/`.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* a reason that is not "it is long":
|
|
4
|
+
* `cli` docs render via `cliReference()` in `js/ui/cli.mjs`, which reads
|
|
5
|
+
* `js/cli-table.json` — the CLI's own metadata, since `harness.build_argparser()`'s parser
|
|
6
|
+
* is not something a Node twin can introspect. `bin/geneseed-cli.mjs`'s `VERBS` table reads
|
|
7
|
+
* the same file, so there is exactly one description of the CLI surface, not a second
|
|
8
|
+
* transcription of it.
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
* `add_argument` calls**, every one carrying help text, a metavar, choices and a
|
|
14
|
-
* default. A Node twin cannot introspect it; reproducing it means transcribing the
|
|
15
|
-
* whole CLI surface into a second table that would describe verbs `bin/geneseed-cli.mjs`
|
|
16
|
-
* cannot even run. This port's standing rule is that a copy of a value under test stops
|
|
17
|
-
* being the value under test, and this would be the largest copy in it. **PAID IN P10c**,
|
|
18
|
-
* by the fix the deferral named: the parser's metadata became a FILE both implementations
|
|
19
|
-
* read — `js/cli-table.json` since P2. `js/ui/cli.mjs` is the reader,
|
|
20
|
-
* `tests/test_cli_reference.py` is what fails when the table and the parser part company
|
|
21
|
-
* for as long as the parser exists, and `bin/geneseed-cli.mjs`'s `VERBS` table
|
|
22
|
-
* — which was a SECOND transcription of the same 43 calls — reads it too, so the port
|
|
23
|
-
* ended with one description of the CLI where it had been heading for three.
|
|
24
|
-
* * `about` called `_update._origin_display()`, which shells out to `git remote get-url`.
|
|
25
|
-
* `rituals/_update.py` was P8's whole subject and P6d would have needed an
|
|
26
|
-
* `_ALLOWED_SPAWNS` row for `git` ahead of the phase that argues for it. **PAID IN P8c**:
|
|
27
|
-
* P8a made that argument and ported `originDisplay()`, so the deferral was a one-line
|
|
28
|
-
* removal from `NOT_PORTED_KINDS` and a row in `KIND_ROUTES`.
|
|
29
|
-
*
|
|
30
|
-
* So `NOT_PORTED_KINDS` declares what is left, `api_docs_page` answers 501 for it, and
|
|
10
|
+
* So `NOT_PORTED_KINDS` declares what is left, `apiDocsPage` answers 501 for it, and
|
|
31
11
|
* `tests/unit/web_server.test.mjs` requires `PORTED_KINDS` ∪ `NOT_PORTED_KINDS` to equal
|
|
32
|
-
* `REF_KINDS` — a FROZEN LITERAL of the five kinds
|
|
33
|
-
*
|
|
34
|
-
*
|
|
12
|
+
* `REF_KINDS` — a FROZEN LITERAL of the five kinds this daemon has ever needed to dispatch
|
|
13
|
+
* on. Same partition shape as the route table, which is why it is a table here too and not
|
|
14
|
+
* two `if`s.
|
|
35
15
|
*
|
|
36
|
-
* THE `?harness=` QUERY PARAM
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* sending both values over one page.
|
|
16
|
+
* THE `?harness=` QUERY PARAM IS the Docs selector, and it is the ONLY input to these
|
|
17
|
+
* endpoints that is not the checkout itself — the one thing a test can vary. Every
|
|
18
|
+
* filtering rule below is exercised by sending both values over one page.
|
|
40
19
|
*/
|
|
41
|
-
import { readdirSync
|
|
20
|
+
import { readdirSync } from 'node:fs';
|
|
42
21
|
import path from 'node:path';
|
|
43
22
|
|
|
44
23
|
import { ROOT, THEMES } from '../build/source.mjs';
|
|
@@ -46,18 +25,17 @@ import { cliReference } from '../ui/cli.mjs';
|
|
|
46
25
|
import { readJsonMaybe, readMaybe } from '../hosts/installs.mjs';
|
|
47
26
|
import { resolvePath } from '../hosts/hosts.mjs';
|
|
48
27
|
import { parseJson } from '../lib/json.mjs';
|
|
28
|
+
import { isFile } from '../lib/fs.mjs';
|
|
49
29
|
import { normcase } from '../lib/paths.mjs';
|
|
50
30
|
import { WHITESPACE, stripWhitespace } from '../lib/text.mjs';
|
|
51
31
|
import { statusData } from '../inspect/status.mjs';
|
|
52
32
|
import { originDisplay } from '../maintain/update.mjs';
|
|
53
33
|
import { NotFound, deployed, resolveLinks } from './api.mjs';
|
|
54
34
|
|
|
55
|
-
const isFile = (p) => { try { return statSync(p).isFile(); } catch { return false; } };
|
|
56
|
-
|
|
57
35
|
const DOC_DIR = path.join(ROOT, 'docs', 'web');
|
|
58
36
|
|
|
59
37
|
/**
|
|
60
|
-
*
|
|
38
|
+
* A docs page split into its frontmatter map and its body.
|
|
61
39
|
*
|
|
62
40
|
* Deliberately not YAML: each value is a JSON scalar (or a JSON object for `link`), which
|
|
63
41
|
* covers every key these pages use and parses with the standard library. A value that is
|
|
@@ -77,23 +55,22 @@ export function docFrontmatter(text) {
|
|
|
77
55
|
const key = (colon < 0 ? line : line.slice(0, colon)).trim();
|
|
78
56
|
const raw = (colon < 0 ? '' : line.slice(colon + 1)).trim();
|
|
79
57
|
if (!key || !raw) continue;
|
|
80
|
-
// `
|
|
81
|
-
//
|
|
58
|
+
// `parseJson`, so a frontmatter number keeps the int/float distinction the rest of
|
|
59
|
+
// this codebase protects.
|
|
82
60
|
try { meta[key] = parseJson(raw); } catch { meta[key] = raw; }
|
|
83
61
|
}
|
|
84
62
|
return [meta, body];
|
|
85
63
|
}
|
|
86
64
|
|
|
87
65
|
/**
|
|
88
|
-
*
|
|
66
|
+
* The Docs registry, built from `docs/web/`.
|
|
89
67
|
*
|
|
90
68
|
* A page whose file is missing or malformed is skipped rather than crashing the server; an
|
|
91
69
|
* unreadable registry yields an empty Docs section, which the UI renders as "no pages".
|
|
92
70
|
*
|
|
93
|
-
* NOT cached at module load
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* when the corpus first imported this file.
|
|
71
|
+
* NOT cached at module load. The difference is invisible to a request — `docs/web/` cannot
|
|
72
|
+
* change under a running daemon any more than `dist/` can — and a module-level constant
|
|
73
|
+
* would freeze whatever `ROOT` was when this file was first imported.
|
|
97
74
|
*/
|
|
98
75
|
export function docGroups() {
|
|
99
76
|
const groups = readJsonMaybe(path.join(DOC_DIR, '_groups.json'));
|
|
@@ -118,7 +95,7 @@ export function docGroups() {
|
|
|
118
95
|
const out = [];
|
|
119
96
|
for (const g of groups) {
|
|
120
97
|
const entry = byId.get(g.id);
|
|
121
|
-
//
|
|
98
|
+
// STABLE sort: equal orders keep filename order.
|
|
122
99
|
entry.pages = entry.pages
|
|
123
100
|
.map((pair, i) => [pair[0], i, pair[1]])
|
|
124
101
|
.sort((a, b) => (a[0] - b[0]) || (a[1] - b[1]))
|
|
@@ -129,8 +106,8 @@ export function docGroups() {
|
|
|
129
106
|
}
|
|
130
107
|
|
|
131
108
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
109
|
+
* The neutral term, the theme key whose value renames it, and the one-line description. A
|
|
110
|
+
* key of `null` marks a term no theme renames.
|
|
134
111
|
*/
|
|
135
112
|
const GLOSSARY_KEYS = [
|
|
136
113
|
// The three tiers, in constitutional order and before the entities — a reader who does not
|
|
@@ -170,11 +147,9 @@ const GLOSSARY_KEYS = [
|
|
|
170
147
|
// ---- counts substituted into concept bodies -------------------------------------------
|
|
171
148
|
|
|
172
149
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* page first spends one. Only one page carries `{N_LAWS}` today; a test that could only reach
|
|
177
|
-
* these through a rendered body would leave four of the seven untested until Task 6.2.
|
|
150
|
+
* Exported so the tier tokens can be gated on the day they are ADDED rather than on the day
|
|
151
|
+
* a page first spends one. Only one page carries `{N_LAWS}` today; a test that could only
|
|
152
|
+
* reach these through a rendered body would leave most of them untested.
|
|
178
153
|
*/
|
|
179
154
|
export function docCounts(state) {
|
|
180
155
|
const inv = state.inventory;
|
|
@@ -204,7 +179,6 @@ export function docCounts(state) {
|
|
|
204
179
|
};
|
|
205
180
|
}
|
|
206
181
|
|
|
207
|
-
/** `_web_docs._sub_counts`. */
|
|
208
182
|
function subCounts(state, body) {
|
|
209
183
|
if (!body.includes('{N_')) return body;
|
|
210
184
|
let out = body;
|
|
@@ -226,7 +200,6 @@ const HARNESS_CLOSE_RE = /^\s*<!--\s*\/harness\s*-->\s*$/;
|
|
|
226
200
|
*/
|
|
227
201
|
const HARNESS_HINT_RE = /<!--\s*harness:/;
|
|
228
202
|
|
|
229
|
-
/** `_web_docs._norm_harness`. */
|
|
230
203
|
export function normHarness(value, state) {
|
|
231
204
|
const v = (value || '').trim().toLowerCase();
|
|
232
205
|
if (HARNESSES.includes(v)) return v;
|
|
@@ -234,7 +207,7 @@ export function normHarness(value, state) {
|
|
|
234
207
|
}
|
|
235
208
|
|
|
236
209
|
/**
|
|
237
|
-
*
|
|
210
|
+
* Every open has a matching close, with no nesting.
|
|
238
211
|
*
|
|
239
212
|
* A marker inside a ``` fence is example text, not a marker (the same rule `sliceSection`
|
|
240
213
|
* uses). Its purpose is to FAIL OPEN: an unbalanced marker leaves the body untouched, so a
|
|
@@ -262,7 +235,6 @@ export function harnessBlocksBalanced(lines) {
|
|
|
262
235
|
return !open;
|
|
263
236
|
}
|
|
264
237
|
|
|
265
|
-
/** `_web_docs._strip_harness_blocks`. */
|
|
266
238
|
export function stripHarnessBlocks(body, harnessName) {
|
|
267
239
|
if (!HARNESS_HINT_RE.test(body)) return body;
|
|
268
240
|
const lines = splitLines(body);
|
|
@@ -287,9 +259,9 @@ export function stripHarnessBlocks(body, harnessName) {
|
|
|
287
259
|
}
|
|
288
260
|
|
|
289
261
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
262
|
+
* NOT a bare `split('\n')`: this drops a single trailing newline instead of yielding a
|
|
263
|
+
* final empty string, and that difference reaches the output here because the result is
|
|
264
|
+
* re-joined with `\n`.
|
|
293
265
|
*/
|
|
294
266
|
function splitLines(s) {
|
|
295
267
|
const parts = s.split('\n');
|
|
@@ -297,7 +269,7 @@ function splitLines(s) {
|
|
|
297
269
|
return parts;
|
|
298
270
|
}
|
|
299
271
|
|
|
300
|
-
/**
|
|
272
|
+
/** Pure; the registry is never mutated. */
|
|
301
273
|
function visibleGroups(harnessName) {
|
|
302
274
|
const groups = [];
|
|
303
275
|
for (const g of docGroups()) {
|
|
@@ -316,14 +288,11 @@ const SLUG_DASH_RE = /-+/g;
|
|
|
316
288
|
const HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
|
|
317
289
|
|
|
318
290
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
291
|
+
* The same rules the frontend's `slug()` uses, so a registry `anchor` written against a
|
|
292
|
+
* heading matches the id the renderer assigns.
|
|
321
293
|
*
|
|
322
|
-
* `WHITESPACE` and `stripWhitespace` rather than `\s` and `trim()
|
|
323
|
-
*
|
|
324
|
-
* two languages' whitespace classes differ at U+FEFF and at U+001C-U+001F, and a heading
|
|
325
|
-
* carrying one slugs differently on each side. See `WHITESPACE`'s docblock for the measured
|
|
326
|
-
* set.
|
|
294
|
+
* `WHITESPACE` and `stripWhitespace` rather than `\s` and `trim()` — see `WHITESPACE`'s own
|
|
295
|
+
* docblock for the measured set of characters where this matters.
|
|
327
296
|
*/
|
|
328
297
|
export function slugifyHeading(text) {
|
|
329
298
|
let s = stripWhitespace(text.toLowerCase()).replace(SLUG_STRIP_RE, '');
|
|
@@ -332,7 +301,7 @@ export function slugifyHeading(text) {
|
|
|
332
301
|
}
|
|
333
302
|
|
|
334
303
|
/**
|
|
335
|
-
*
|
|
304
|
+
* The body trimmed to one heading's section.
|
|
336
305
|
*
|
|
337
306
|
* Returns `[body, ok]`; `ok === false` (with the ORIGINAL body) when the anchor is
|
|
338
307
|
* missing, so the caller falls back to the whole document. An H1 slice stops at the first
|
|
@@ -372,7 +341,6 @@ export function sliceSection(body, anchor) {
|
|
|
372
341
|
|
|
373
342
|
// ---- the endpoints -----------------------------------------------------------------------
|
|
374
343
|
|
|
375
|
-
/** `_web_docs._find_doc_page`. */
|
|
376
344
|
function findDocPage(pageId) {
|
|
377
345
|
for (const g of docGroups()) {
|
|
378
346
|
for (const p of g.pages) if (p.id === pageId) return p;
|
|
@@ -381,9 +349,9 @@ function findDocPage(pageId) {
|
|
|
381
349
|
}
|
|
382
350
|
|
|
383
351
|
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
352
|
+
* A markdown file relative to ROOT, guarded against escapes the same way the catalog is. A
|
|
353
|
+
* GET carries no token, so `rel` comes out of the registry, but the guard is what makes
|
|
354
|
+
* that safe to say.
|
|
387
355
|
*/
|
|
388
356
|
function readDocSource(rel) {
|
|
389
357
|
const target = resolvePath(path.join(ROOT, rel));
|
|
@@ -397,7 +365,7 @@ function within(child, parent) {
|
|
|
397
365
|
return p.length <= c.length && p.every((seg, i) => c[i] === seg);
|
|
398
366
|
}
|
|
399
367
|
|
|
400
|
-
/**
|
|
368
|
+
/** The deployed theme's words beside the neutral ones. */
|
|
401
369
|
function glossary(state) {
|
|
402
370
|
const load = (theme) => {
|
|
403
371
|
const doc = readJsonMaybe(path.join(THEMES, `${theme}.json`));
|
|
@@ -423,12 +391,11 @@ function glossary(state) {
|
|
|
423
391
|
}
|
|
424
392
|
|
|
425
393
|
/**
|
|
426
|
-
*
|
|
394
|
+
* The About page: a version line, the deployed install, and the links.
|
|
427
395
|
*
|
|
428
396
|
* `repo` is the install's OWN git origin (where updates come from), and `repo_is_github` is
|
|
429
|
-
* what gates the github-shaped deep links in the UI. Both come from `originDisplay()`,
|
|
430
|
-
*
|
|
431
|
-
* that argued for the `git` row in `_ALLOWED_SPAWNS`.
|
|
397
|
+
* what gates the github-shaped deep links in the UI. Both come from `originDisplay()`,
|
|
398
|
+
* which shells out to `git remote get-url` (the `git` row in `ALLOWED_SPAWNS`).
|
|
432
399
|
*
|
|
433
400
|
* `version` IS NOT WHAT IT LOOKS LIKE, and it is the shape this page has always had rather than
|
|
434
401
|
* a shortcut here. It reads a `version` key off the status snapshot and THERE IS NO SUCH KEY —
|
|
@@ -436,8 +403,8 @@ function glossary(state) {
|
|
|
436
403
|
* field is `{}`, always, and `sd.version || {}` reproduces the EXPRESSION rather than the
|
|
437
404
|
* constant: if the key ever appears, the page gains it without another edit.
|
|
438
405
|
*
|
|
439
|
-
* THERE IS NO INTERPRETER FIELD, and the absence is deliberate — see `apiSetup
|
|
440
|
-
* the same absence for the same reason.
|
|
406
|
+
* THERE IS NO INTERPRETER FIELD, and the absence is deliberate — see `apiSetup` in
|
|
407
|
+
* `api.mjs`, which spells the same absence for the same reason.
|
|
441
408
|
*
|
|
442
409
|
* `statusData()` is called for one dead field, and deliberately: this page has always paid that
|
|
443
410
|
* cost, and skipping it would answer faster in a way no gate measures and no user asked for.
|
|
@@ -453,41 +420,32 @@ function about(state) {
|
|
|
453
420
|
target: String(state.target),
|
|
454
421
|
root: String(ROOT),
|
|
455
422
|
repo: od.url,
|
|
456
|
-
// `githubSlug`, not `github_slug
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
//
|
|
461
|
-
// the two payloads by hand before a cell existed; `docs/the-about-page-...` is the cell.
|
|
423
|
+
// `githubSlug`, not `github_slug` — the record itself is camelCase like every other
|
|
424
|
+
// object in this codebase; the RESPONSE key below is snake_case because that is the
|
|
425
|
+
// wire format. Reading the wrong spelling off the record gives `undefined`, which is a
|
|
426
|
+
// perfectly plausible `false`: the About page would silently drop every github deep
|
|
427
|
+
// link and nothing else would change.
|
|
462
428
|
repo_is_github: Boolean(od.githubSlug),
|
|
463
429
|
license: 'MIT',
|
|
464
430
|
};
|
|
465
431
|
}
|
|
466
432
|
|
|
467
433
|
/**
|
|
468
|
-
* EMPTY
|
|
434
|
+
* EMPTY, AND KEPT DECLARED — the third partition in this codebase to reach that state,
|
|
469
435
|
* after `NOT_PORTED_POST` and `NOT_PORTED_ACTIONS`.
|
|
470
436
|
*
|
|
471
|
-
* `tests/
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
* typo'd page id.
|
|
437
|
+
* `tests/unit/web_server.test.mjs` cross-checks this set against the kinds `apiDocsPage`
|
|
438
|
+
* dispatches on, and that check is at its STRONGEST when the set is empty: it says every
|
|
439
|
+
* kind this daemon can dispatch on is answered here. A sixth kind added later still cannot
|
|
440
|
+
* quietly fall through to the `NotFound` at the bottom, and it has a place to be declared
|
|
441
|
+
* rather than reading as a typo'd page id.
|
|
477
442
|
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
* `cli` (200), and the claim that the 501 arm still works rests on the declaration check
|
|
482
|
-
* alone — strictly weaker, discipline #7, and said out loud there as it is here.
|
|
483
|
-
*
|
|
484
|
-
* `about` left in P8c (`_origin_display` and the `git` spawn behind it, not the page).
|
|
485
|
-
* `cli` left in P10c, and its blocker was neither: it was that `harness.build_argparser()`
|
|
486
|
-
* cannot be introspected from Node at all. See `js/ui/cli.mjs`.
|
|
443
|
+
* THE 501 ARM HAS NO REACHABLE TARGET TODAY: with this set empty, no page names an
|
|
444
|
+
* unported kind. Its correctness rests on the declaration check above, not on any request
|
|
445
|
+
* that actually reaches it — worth knowing before "simplifying" it away as dead code.
|
|
487
446
|
*/
|
|
488
447
|
export const NOT_PORTED_KINDS = new Set();
|
|
489
448
|
|
|
490
|
-
/** `_web_docs.api_docs`. */
|
|
491
449
|
export function apiDocs(state, harnessName = null) {
|
|
492
450
|
const hn = normHarness(harnessName, state);
|
|
493
451
|
const groups = visibleGroups(hn).map((g) => ({
|
|
@@ -499,8 +457,6 @@ export function apiDocs(state, harnessName = null) {
|
|
|
499
457
|
}
|
|
500
458
|
|
|
501
459
|
/**
|
|
502
|
-
* `_web_docs.api_docs_page`.
|
|
503
|
-
*
|
|
504
460
|
* The lookup deliberately ignores a page's own `harness` tag: a deep link to a page the
|
|
505
461
|
* active harness hides still resolves, and the client redirects it out of view.
|
|
506
462
|
*/
|
|
@@ -521,10 +477,9 @@ export function apiDocsPage(state, pageId, harnessName = null) {
|
|
|
521
477
|
/**
|
|
522
478
|
* The three kinds that cross, as a TABLE and not three `if`s.
|
|
523
479
|
*
|
|
524
|
-
* `tests/
|
|
525
|
-
* equal the kinds `
|
|
526
|
-
*
|
|
527
|
-
* declaration cannot see a dispatcher that stopped using it.
|
|
480
|
+
* `tests/unit/web_server.test.mjs` requires `Object.keys(KIND_ROUTES) ∪ NOT_PORTED_KINDS` to
|
|
481
|
+
* equal the kinds `apiDocsPage` dispatches on — this table is what the dispatcher actually
|
|
482
|
+
* consults, so a declaration-only check cannot see a dispatcher that stopped using it.
|
|
528
483
|
*/
|
|
529
484
|
const KIND_ROUTES = {
|
|
530
485
|
markdown: (state, pageId, page, hn) => {
|
|
@@ -552,8 +507,8 @@ const KIND_ROUTES = {
|
|
|
552
507
|
about: (state, pageId, page) => ({
|
|
553
508
|
id: pageId, title: page.title, kind: 'about', ...about(state),
|
|
554
509
|
}),
|
|
555
|
-
//
|
|
556
|
-
//
|
|
510
|
+
// The row is one line because the work was not in the page: it was in making the CLI's
|
|
511
|
+
// metadata (`js/cli-table.json`) a file this reads directly.
|
|
557
512
|
cli: (state, pageId, page) => ({
|
|
558
513
|
id: pageId, title: page.title, kind: 'cli', ...cliReference(),
|
|
559
514
|
}),
|
package/js/web/graph.mjs
CHANGED
|
Binary file
|