@reicek/neataptic-ts 0.1.24 → 0.1.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.
Files changed (215) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
  3. package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
  4. package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
  5. package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
  6. package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
  7. package/.github/workflows/ci.yml +3 -3
  8. package/.github/workflows/deploy-pages.yml +6 -6
  9. package/.github/workflows/manual_release_pipeline.yml +3 -3
  10. package/.github/workflows/publish.yml +18 -19
  11. package/.github/workflows/release_dispatch.yml +3 -3
  12. package/package.json +26 -20
  13. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  14. package/plans/README.md +24 -0
  15. package/plans/Roadmap.md +62 -40
  16. package/plans/analyze-trace-solid-split.plans.md +66 -0
  17. package/plans/architecture-solid-split.plans.md +9 -15
  18. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  19. package/plans/generate-docs-solid-split.plans.md +87 -0
  20. package/plans/methods-docs.plans.md +25 -1
  21. package/plans/methods-solid-split.plans.md +14 -14
  22. package/plans/neat-docs.plans.md +9 -1
  23. package/plans/neat-test-surface-repair.plans.md +1 -1
  24. package/plans/render-docs-html-solid-split.plans.md +68 -0
  25. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  26. package/plans/utils-docs.plans.md +6 -1
  27. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  28. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  29. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  30. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  31. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  32. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  33. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  34. package/scripts/assets/theme.css +80 -23
  35. package/scripts/copy-examples.ts +239 -0
  36. package/scripts/export-onnx.ts +223 -0
  37. package/scripts/generate-bench-tables.ts +378 -37
  38. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  39. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  40. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  41. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  42. package/scripts/generate-docs/generate-docs.ts +63 -0
  43. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  44. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  45. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  46. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  47. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  48. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  50. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  51. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  52. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  53. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  54. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  55. package/scripts/mermaid-cli.mjs +102 -22
  56. package/scripts/mermaid-cli.ts +736 -0
  57. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  58. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  59. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  60. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  61. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  62. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  63. package/scripts/render-docs-html.ts +23 -587
  64. package/scripts/run-docs.ts +238 -0
  65. package/scripts/write-dist-docs-pkg.ts +40 -0
  66. package/src/README.md +75 -75
  67. package/src/architecture/connection/README.md +5 -5
  68. package/src/architecture/layer/README.md +508 -508
  69. package/src/architecture/network/README.md +1458 -1458
  70. package/src/architecture/network/activate/README.md +694 -694
  71. package/src/architecture/network/bootstrap/README.md +77 -77
  72. package/src/architecture/network/connect/README.md +74 -74
  73. package/src/architecture/network/deterministic/README.md +135 -135
  74. package/src/architecture/network/evolve/README.md +364 -364
  75. package/src/architecture/network/gating/README.md +130 -130
  76. package/src/architecture/network/genetic/README.md +399 -399
  77. package/src/architecture/network/mutate/README.md +897 -897
  78. package/src/architecture/network/onnx/README.md +720 -720
  79. package/src/architecture/network/onnx/export/README.md +728 -728
  80. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  81. package/src/architecture/network/onnx/import/README.md +618 -618
  82. package/src/architecture/network/onnx/schema/README.md +32 -32
  83. package/src/architecture/network/prune/README.md +245 -245
  84. package/src/architecture/network/remove/README.md +135 -135
  85. package/src/architecture/network/runtime/README.md +106 -106
  86. package/src/architecture/network/serialize/README.md +542 -542
  87. package/src/architecture/network/slab/README.md +608 -608
  88. package/src/architecture/network/standalone/README.md +212 -212
  89. package/src/architecture/network/stats/README.md +84 -84
  90. package/src/architecture/network/topology/README.md +465 -465
  91. package/src/architecture/network/training/README.md +200 -200
  92. package/src/architecture/node/README.md +5 -5
  93. package/src/architecture/nodePool/README.md +14 -14
  94. package/src/methods/README.md +99 -99
  95. package/src/methods/activation/README.md +189 -189
  96. package/src/methods/cost/README.md +131 -131
  97. package/src/methods/rate/README.md +86 -86
  98. package/src/multithreading/README.md +77 -77
  99. package/src/multithreading/workers/browser/README.md +8 -8
  100. package/src/multithreading/workers/node/README.md +8 -8
  101. package/src/neat/README.md +148 -148
  102. package/src/neat/adaptive/README.md +120 -120
  103. package/src/neat/adaptive/acceptance/README.md +40 -40
  104. package/src/neat/adaptive/complexity/README.md +137 -137
  105. package/src/neat/adaptive/core/README.md +197 -197
  106. package/src/neat/adaptive/lineage/README.md +90 -90
  107. package/src/neat/adaptive/mutation/README.md +284 -284
  108. package/src/neat/compat/README.md +43 -43
  109. package/src/neat/compat/core/README.md +90 -90
  110. package/src/neat/diversity/README.md +35 -35
  111. package/src/neat/diversity/core/README.md +88 -88
  112. package/src/neat/evaluate/README.md +85 -85
  113. package/src/neat/evaluate/auto-distance/README.md +75 -75
  114. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  115. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  116. package/src/neat/evaluate/fitness/README.md +23 -23
  117. package/src/neat/evaluate/novelty/README.md +120 -120
  118. package/src/neat/evaluate/objectives/README.md +17 -17
  119. package/src/neat/evaluate/shared/README.md +94 -94
  120. package/src/neat/evolve/README.md +96 -96
  121. package/src/neat/evolve/adaptive/README.md +60 -60
  122. package/src/neat/evolve/objectives/README.md +63 -63
  123. package/src/neat/evolve/offspring/README.md +56 -56
  124. package/src/neat/evolve/population/README.md +171 -171
  125. package/src/neat/evolve/runtime/README.md +79 -79
  126. package/src/neat/evolve/speciation/README.md +74 -74
  127. package/src/neat/evolve/warnings/README.md +10 -10
  128. package/src/neat/export/README.md +114 -114
  129. package/src/neat/helpers/README.md +50 -50
  130. package/src/neat/init/README.md +9 -9
  131. package/src/neat/lineage/core/README.md +101 -101
  132. package/src/neat/multiobjective/category/README.md +74 -74
  133. package/src/neat/multiobjective/crowding/README.md +272 -272
  134. package/src/neat/multiobjective/dominance/README.md +171 -171
  135. package/src/neat/multiobjective/fronts/README.md +68 -68
  136. package/src/neat/multiobjective/metrics/README.md +43 -43
  137. package/src/neat/multiobjective/objectives/README.md +31 -31
  138. package/src/neat/multiobjective/shared/README.md +27 -27
  139. package/src/neat/mutation/README.md +97 -97
  140. package/src/neat/mutation/add-conn/README.md +115 -115
  141. package/src/neat/mutation/add-node/README.md +126 -126
  142. package/src/neat/mutation/flow/README.md +149 -149
  143. package/src/neat/mutation/repair/README.md +185 -185
  144. package/src/neat/mutation/select/README.md +117 -117
  145. package/src/neat/mutation/shared/README.md +32 -32
  146. package/src/neat/objectives/README.md +25 -25
  147. package/src/neat/objectives/core/README.md +67 -67
  148. package/src/neat/pruning/README.md +40 -40
  149. package/src/neat/pruning/core/README.md +171 -171
  150. package/src/neat/pruning/facade/README.md +32 -32
  151. package/src/neat/rng/README.md +104 -104
  152. package/src/neat/rng/core/README.md +137 -137
  153. package/src/neat/rng/facade/README.md +50 -50
  154. package/src/neat/selection/README.md +111 -111
  155. package/src/neat/selection/core/README.md +227 -227
  156. package/src/neat/selection/facade/README.md +61 -61
  157. package/src/neat/shared/README.md +163 -163
  158. package/src/neat/speciation/README.md +31 -31
  159. package/src/neat/speciation/threshold/README.md +35 -35
  160. package/src/neat/species/README.md +25 -25
  161. package/src/neat/species/core/README.md +20 -20
  162. package/src/neat/species/core/shared/README.md +18 -18
  163. package/src/neat/species/history/context/README.md +22 -22
  164. package/src/neat/telemetry/accessors/README.md +58 -58
  165. package/src/neat/telemetry/exports/README.md +233 -233
  166. package/src/neat/telemetry/facade/README.md +252 -252
  167. package/src/neat/telemetry/facade/archive/README.md +57 -57
  168. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  169. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  170. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  171. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  172. package/src/neat/telemetry/facade/species/README.md +27 -27
  173. package/src/neat/telemetry/metrics/README.md +696 -696
  174. package/src/neat/telemetry/recorder/README.md +57 -57
  175. package/src/neat/telemetry/types/README.md +32 -32
  176. package/src/neat/topology-intent/README.md +75 -75
  177. package/src/utils/README.md +193 -193
  178. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  179. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  180. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  181. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  182. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  183. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  184. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  185. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  186. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  187. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  188. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  189. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  190. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  191. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  192. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  193. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  194. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  195. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  196. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  197. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  198. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  199. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  200. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  201. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  202. package/test/examples/flappy_bird/environment/README.md +22 -22
  203. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  204. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  205. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  206. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  207. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  208. package/test/examples/flappy_bird/trainer/README.md +325 -325
  209. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  210. package/scripts/analyze-trace.ts +0 -590
  211. package/scripts/copy-examples.mjs +0 -114
  212. package/scripts/export-onnx.mjs +0 -86
  213. package/scripts/generate-bench-tables.mjs +0 -182
  214. package/scripts/generate-docs.ts +0 -2900
  215. package/scripts/write-dist-docs-pkg.mjs +0 -16
@@ -1,599 +1,35 @@
1
1
  /*
2
- * Converts every README.md inside docs/ (including root copy) into an index.html in the same directory.
2
+ * Converts every README.md inside docs/ into an index.html in the same directory.
3
3
  * Usage: npm run docs:html
4
+ *
5
+ * This file intentionally stays orchestration-first. The folder-owned boundary
6
+ * under `scripts/render-docs-html/` owns navigation, Markdown page rendering,
7
+ * static assets, and Mermaid validation.
4
8
  */
5
- import { spawn } from 'node:child_process';
6
- import { mkdtemp, rm } from 'node:fs/promises';
7
- import os from 'node:os';
8
- import fg from 'fast-glob';
9
- import path from 'path';
10
- import fs from 'fs-extra';
11
- import { marked } from 'marked';
12
- import {
13
- buildDocsSidebarHtml,
14
- buildExamplesTocLinksHtml,
15
- } from './render-docs-html.sidebar.js';
16
-
17
- const DOCS_DIR = path.resolve('docs');
18
- const THEME_CSS_SOURCE_PATH = path.resolve('scripts', 'assets', 'theme.css');
19
- const THEME_CSS_OUTPUT_PATH = path.join(DOCS_DIR, 'assets', 'theme.css');
20
- const MERMAID_MODULE_SOURCE_PATH = path.resolve(
21
- 'node_modules',
22
- 'mermaid',
23
- 'dist',
24
- 'mermaid.esm.min.mjs',
25
- );
26
- const MERMAID_DIST_SOURCE_DIR = path.resolve('node_modules', 'mermaid', 'dist');
27
- const MERMAID_MODULE_OUTPUT_PATH = path.join(
28
- DOCS_DIR,
29
- 'assets',
30
- 'vendor',
31
- 'mermaid.esm.min.mjs',
32
- );
33
- const MERMAID_DIST_OUTPUT_DIR = path.join(DOCS_DIR, 'assets', 'vendor');
34
- const MERMAID_CLI_SCRIPT_PATH = path.resolve('scripts', 'mermaid-cli.mjs');
35
- const NN_IMAGE_SOURCE_PATH = path.resolve('nn.jpg');
36
- const NN_IMAGE_FALLBACK_SOURCE_PATH = path.resolve(
37
- 'scripts',
38
- 'assets',
39
- 'nn.jpg',
40
- );
41
- const NN_IMAGE_OUTPUT_PATH = path.join(DOCS_DIR, 'nn.jpg');
42
- const GITHUB_REPOSITORY_BLOB_BASE_URL =
43
- 'https://github.com/reicek/NeatapticTS/blob/develop';
44
- const GITHUB_REPOSITORY_TREE_BASE_URL =
45
- 'https://github.com/reicek/NeatapticTS/tree/develop';
46
-
47
- const RETRIABLE_FILE_SYSTEM_ERROR_CODES = new Set([
48
- 'UNKNOWN',
49
- 'EPERM',
50
- 'EBUSY',
51
- 'EACCES',
52
- ]);
53
- const DOCS_WRITE_MAX_ATTEMPTS = 6;
54
- const DOCS_WRITE_INITIAL_RETRY_DELAY_MS = 120;
55
-
56
- interface PageMeta {
57
- abs: string;
58
- relDir: string;
59
- title: string;
60
- markdown: string;
61
- }
62
-
63
- interface MermaidBlockReference {
64
- readmePath: string;
65
- blockNumber: number;
66
- diagram: string;
67
- }
68
-
69
- function isRetriableFileSystemError(
70
- error: unknown,
71
- ): error is NodeJS.ErrnoException {
72
- if (!(error instanceof Error)) return false;
73
- const code = (error as NodeJS.ErrnoException).code;
74
- if (!code) return false;
75
- return RETRIABLE_FILE_SYSTEM_ERROR_CODES.has(code);
76
- }
77
-
78
- async function waitForRetryDelay(delayMilliseconds: number): Promise<void> {
79
- await new Promise((resolve) => {
80
- setTimeout(resolve, delayMilliseconds);
81
- });
82
- }
83
-
84
- async function writeFileWithRetry(
85
- filePath: string,
86
- content: string,
87
- encoding: BufferEncoding,
88
- ): Promise<void> {
89
- let retryDelayMilliseconds = DOCS_WRITE_INITIAL_RETRY_DELAY_MS;
90
-
91
- for (
92
- let attemptNumber = 1;
93
- attemptNumber <= DOCS_WRITE_MAX_ATTEMPTS;
94
- attemptNumber += 1
95
- ) {
96
- try {
97
- await fs.writeFile(filePath, content, encoding);
98
- return;
99
- } catch (error: unknown) {
100
- const canRetry =
101
- isRetriableFileSystemError(error) &&
102
- attemptNumber < DOCS_WRITE_MAX_ATTEMPTS;
103
- if (!canRetry) {
104
- throw error;
105
- }
106
-
107
- await waitForRetryDelay(retryDelayMilliseconds);
108
- retryDelayMilliseconds = Math.min(retryDelayMilliseconds * 2, 1_000);
109
- }
110
- }
111
- }
112
-
113
- async function ensureThemeCss(): Promise<void> {
114
- // Step 1: Ensure destination directory exists.
115
- await fs.ensureDir(path.dirname(THEME_CSS_OUTPUT_PATH));
116
-
117
- // Step 2: Copy static theme stylesheet used by generated docs pages.
118
- await fs.copyFile(THEME_CSS_SOURCE_PATH, THEME_CSS_OUTPUT_PATH);
119
- }
120
-
121
- async function ensureMermaidModule(): Promise<void> {
122
- // Step 1: Skip Mermaid bootstrapping when the browser bundle is unavailable.
123
- const hasMermaidModule = await fs.pathExists(MERMAID_MODULE_SOURCE_PATH);
124
- if (!hasMermaidModule) return;
125
-
126
- // Step 2: Copy the full Mermaid dist bundle so transitive chunk imports load.
127
- await fs.ensureDir(MERMAID_DIST_OUTPUT_DIR);
128
- await fs.copy(MERMAID_DIST_SOURCE_DIR, MERMAID_DIST_OUTPUT_DIR, {
129
- overwrite: true,
130
- });
131
- }
132
-
133
- function buildDocsLayoutInteractionScript(): string {
134
- return `<script>
135
- const docsPanels = Array.from(document.querySelectorAll('.docs-panel'));
136
-
137
- function setActiveDocsPanel(nextPanel) {
138
- docsPanels.forEach((panel) => {
139
- panel.classList.toggle('is-active', panel === nextPanel);
140
- });
141
- }
142
-
143
- document.addEventListener('click', (event) => {
144
- const target = event.target;
145
- if (!(target instanceof Element)) {
146
- setActiveDocsPanel(null);
147
- return;
148
- }
149
-
150
- const activePanel = target.closest('.docs-panel');
151
- setActiveDocsPanel(activePanel instanceof HTMLElement ? activePanel : null);
152
- });
153
-
154
- document.addEventListener('keydown', (event) => {
155
- if (event.key === 'Escape') {
156
- setActiveDocsPanel(null);
157
- }
158
- });
159
- </script>`;
160
- }
161
-
162
- async function ensureStaticDocsAssets(): Promise<void> {
163
- // Step 1: Ensure core theme assets are present.
164
- await ensureThemeCss();
165
- await ensureMermaidModule();
166
-
167
- // Step 2: Ensure the README hero image resolves when served from `/docs/`.
168
- // The root README is copied into `docs/README.md`, so `<img src="nn.jpg">`
169
- // becomes a request for `/docs/nn.jpg` when hosted under that base path.
170
- const hasRootImage = await fs.pathExists(NN_IMAGE_SOURCE_PATH);
171
- const hasFallbackImage = await fs.pathExists(NN_IMAGE_FALLBACK_SOURCE_PATH);
172
- const sourcePath = hasRootImage
173
- ? NN_IMAGE_SOURCE_PATH
174
- : hasFallbackImage
175
- ? NN_IMAGE_FALLBACK_SOURCE_PATH
176
- : undefined;
177
9
 
178
- if (!sourcePath) return;
179
- await fs.copyFile(sourcePath, NN_IMAGE_OUTPUT_PATH);
180
- }
181
-
182
- function slugify(s: string): string {
183
- return s
184
- .toLowerCase()
185
- .replace(/[^a-z0-9]+/g, '-')
186
- .replace(/^-+|-+$/g, '')
187
- .replace(/-{2,}/g, '-');
188
- }
189
-
190
- function escapeHtml(value: string): string {
191
- return value
192
- .replace(/&/g, '&amp;')
193
- .replace(/</g, '&lt;')
194
- .replace(/>/g, '&gt;')
195
- .replace(/"/g, '&quot;')
196
- .replace(/'/g, '&#39;');
197
- }
198
-
199
- function buildMermaidBootstrapScript(relToRoot: string): string {
200
- const mermaidModuleHref =
201
- (relToRoot ? `${relToRoot}/` : '') + 'assets/vendor/mermaid.esm.min.mjs';
202
-
203
- return `<script type="module">
204
- import mermaid from "${mermaidModuleHref}";
205
-
206
- mermaid.initialize({
207
- startOnLoad: false,
208
- securityLevel: 'loose',
209
- theme: 'base',
210
- themeVariables: {
211
- darkMode: true,
212
- background: '#121a24',
213
- primaryColor: '#102131',
214
- primaryTextColor: '#d7e4f3',
215
- primaryBorderColor: '#5ec8ff',
216
- secondaryColor: '#0f1722',
217
- secondaryTextColor: '#d7e4f3',
218
- secondaryBorderColor: '#5ec8ff',
219
- tertiaryColor: '#16283a',
220
- tertiaryTextColor: '#d7e4f3',
221
- tertiaryBorderColor: '#ffbf69',
222
- mainBkg: '#0f1722',
223
- nodeBorder: '#5ec8ff',
224
- clusterBkg: '#102131',
225
- clusterBorder: '#5ec8ff',
226
- lineColor: '#5ec8ff',
227
- edgeLabelBackground: '#0f1722',
228
- textColor: '#d7e4f3',
229
- fontFamily: 'Open Sans, sans-serif'
230
- }
231
- });
232
-
233
- try {
234
- await mermaid.run({ querySelector: '.mermaid-diagram' });
235
- } catch (error) {
236
- console.error('[docs] Mermaid render failed.', error);
237
- }
238
- </script>`;
239
- }
240
-
241
- function buildRelativeDocsHref(currentDir: string, targetDir: string): string {
242
- const relLink = path.posix.relative(currentDir || '.', targetDir || '.') || '.';
243
- return (relLink === '.' ? '.' : relLink) + '/index.html';
244
- }
245
-
246
- function isExternalOrAnchorHref(href: string): boolean {
247
- return (
248
- /^[a-z][a-z0-9+.-]*:/i.test(href) ||
249
- href.startsWith('//') ||
250
- href.startsWith('#')
251
- );
252
- }
253
-
254
- function normalizeRepoRelativeHref(href: string): string {
255
- return href
256
- .trim()
257
- .replace(/\\/g, '/')
258
- .replace(/^\.\//, '')
259
- .replace(/^\//, '')
260
- .replace(/\/{2,}/g, '/');
261
- }
262
-
263
- function hasCompiledDocsPage(relativeDirectoryPath: string): boolean {
264
- const normalizedDirectoryPath = relativeDirectoryPath.replace(/\/$/, '');
265
- if (!normalizedDirectoryPath) {
266
- return false;
267
- }
268
-
269
- return fs.existsSync(path.resolve(DOCS_DIR, normalizedDirectoryPath, 'index.html'));
270
- }
271
-
272
- function buildGitHubRepositoryHref(repoRelativePath: string): string {
273
- const normalizedPath = normalizeRepoRelativeHref(repoRelativePath).replace(
274
- /\/$/,
275
- '',
276
- );
277
- const absoluteRepositoryPath = path.resolve(normalizedPath);
278
- const pathExistsInRepository = fs.existsSync(absoluteRepositoryPath);
279
- const urlBase = pathExistsInRepository && fs.statSync(absoluteRepositoryPath).isFile()
280
- ? GITHUB_REPOSITORY_BLOB_BASE_URL
281
- : path.posix.extname(normalizedPath)
282
- ? GITHUB_REPOSITORY_BLOB_BASE_URL
283
- : GITHUB_REPOSITORY_TREE_BASE_URL;
284
-
285
- return `${urlBase}/${normalizedPath}`;
286
- }
287
-
288
- function resolveRootLandingCompiledHref(href: string): string {
289
- if (!href || isExternalOrAnchorHref(href)) {
290
- return href;
291
- }
292
-
293
- const normalizedHref = normalizeRepoRelativeHref(href);
294
- if (!normalizedHref) {
295
- return './index.html';
296
- }
297
-
298
- if (
299
- normalizedHref === 'docs' ||
300
- normalizedHref === 'docs/' ||
301
- normalizedHref === 'docs/index.html'
302
- ) {
303
- return './index.html';
304
- }
305
-
306
- if (normalizedHref.startsWith('docs/')) {
307
- return `./${normalizedHref.slice('docs/'.length)}`;
308
- }
309
-
310
- if (
311
- normalizedHref === 'src' ||
312
- normalizedHref === 'src/' ||
313
- normalizedHref === 'src/README.md'
314
- ) {
315
- return './src/index.html';
316
- }
317
-
318
- if (normalizedHref.startsWith('src/')) {
319
- const sourceSubpath = normalizedHref.slice('src/'.length);
320
- const sourceReadmeMatch = /^(.*)\/README\.md$/.exec(sourceSubpath);
321
- const compiledDocsCandidate = sourceReadmeMatch
322
- ? sourceReadmeMatch[1]
323
- : sourceSubpath.replace(/\/$/, '');
324
-
325
- if (hasCompiledDocsPage(compiledDocsCandidate)) {
326
- return `./${compiledDocsCandidate}/index.html`;
327
- }
328
- }
329
-
330
- if (normalizedHref === 'test/examples' || normalizedHref === 'test/examples/') {
331
- return './examples/index.html';
332
- }
333
-
334
- const exampleReadmeMatch = /^test\/examples\/([^/]+)\/README\.md$/.exec(
335
- normalizedHref,
336
- );
337
- if (exampleReadmeMatch) {
338
- return `./examples/${exampleReadmeMatch[1]}/docs/index.html`;
339
- }
340
-
341
- const exampleDirectoryMatch = /^test\/examples\/([^/]+)\/?$/.exec(
342
- normalizedHref,
343
- );
344
- if (exampleDirectoryMatch) {
345
- return `./examples/${exampleDirectoryMatch[1]}/index.html`;
346
- }
347
-
348
- const compiledDocsDirectoryCandidate = normalizedHref.replace(/\/$/, '');
349
- if (hasCompiledDocsPage(compiledDocsDirectoryCandidate)) {
350
- return `./${compiledDocsDirectoryCandidate}/index.html`;
351
- }
352
-
353
- return buildGitHubRepositoryHref(normalizedHref);
354
- }
355
-
356
- function rewriteCompiledDocsContentLinks(
357
- htmlBody: string,
358
- relativeDirectory: string,
359
- ): string {
360
- if (relativeDirectory !== '') {
361
- return htmlBody;
362
- }
363
-
364
- return htmlBody.replace(
365
- /\bhref=(['"])([^'"]+)\1/g,
366
- (_match, quote: string, href: string) => {
367
- return `href=${quote}${resolveRootLandingCompiledHref(href)}${quote}`;
368
- },
369
- );
370
- }
371
-
372
- function hasPublishedDocsPage(
373
- relDir: string,
374
- generatedPageDirectories: ReadonlySet<string>,
375
- ): boolean {
376
- if (generatedPageDirectories.has(relDir)) return true;
377
- return fs.existsSync(path.resolve(DOCS_DIR, relDir, 'index.html'));
378
- }
379
-
380
- function collectMermaidBlocks(
381
- markdown: string,
382
- readmePath: string,
383
- ): MermaidBlockReference[] {
384
- const mermaidBlocks: MermaidBlockReference[] = [];
385
- const mermaidFencePattern = /^```mermaid[^\n]*\r?\n([\s\S]*?)^```\s*$/gm;
386
- let mermaidMatch = mermaidFencePattern.exec(markdown);
387
-
388
- while (mermaidMatch) {
389
- mermaidBlocks.push({
390
- readmePath,
391
- blockNumber: mermaidBlocks.length + 1,
392
- diagram: mermaidMatch[1].trim(),
393
- });
394
- mermaidMatch = mermaidFencePattern.exec(markdown);
395
- }
396
-
397
- return mermaidBlocks;
398
- }
399
-
400
- async function validateMermaidBlocks(
401
- mermaidBlocks: readonly MermaidBlockReference[],
402
- ): Promise<void> {
403
- if (mermaidBlocks.length === 0) {
404
- return;
405
- }
406
-
407
- const tempDirectoryPath = await mkdtemp(
408
- path.join(os.tmpdir(), 'neatapticts-docs-mermaid-'),
409
- );
410
-
411
- try {
412
- for (const mermaidBlock of mermaidBlocks) {
413
- const tempInputPath = path.join(
414
- tempDirectoryPath,
415
- `diagram-${mermaidBlock.blockNumber}.mmd`,
416
- );
417
-
418
- // Step 1: Materialize the Mermaid block for CLI validation.
419
- await fs.writeFile(tempInputPath, mermaidBlock.diagram, 'utf8');
420
-
421
- // Step 2: Treat broken Mermaid as a docs build failure instead of a browser-only error.
422
- await runMermaidValidation(tempInputPath, mermaidBlock);
423
- }
424
- } finally {
425
- await rm(tempDirectoryPath, { recursive: true, force: true });
426
- }
427
- }
428
-
429
- async function runMermaidValidation(
430
- tempInputPath: string,
431
- mermaidBlock: MermaidBlockReference,
432
- ): Promise<void> {
433
- await new Promise<void>((resolve, reject) => {
434
- const childProcess = spawn(
435
- process.execPath,
436
- [MERMAID_CLI_SCRIPT_PATH, 'validate', '--input', tempInputPath],
437
- { stdio: 'inherit' },
438
- );
439
-
440
- childProcess.once('exit', (exitCode) => {
441
- if (exitCode === 0) {
442
- resolve();
443
- return;
444
- }
445
-
446
- const relativeReadmePath = path.relative(process.cwd(), mermaidBlock.readmePath);
447
- reject(
448
- new Error(
449
- `Invalid Mermaid diagram in ${relativeReadmePath} (block ${mermaidBlock.blockNumber}). Mermaid CLI exited with code ${exitCode ?? 'null'}.`,
450
- ),
451
- );
452
- });
453
- childProcess.once('error', reject);
454
- });
455
- }
10
+ import { ensureRenderDocsAssets } from './render-docs-html/render-docs-html.assets.js';
11
+ import {
12
+ ensureMermaidBrowserAssets,
13
+ validateMermaidBlocks,
14
+ } from './render-docs-html/render-docs-html.mermaid.js';
15
+ import {
16
+ collectDocsPages,
17
+ emitDocsPages,
18
+ } from './render-docs-html/render-docs-html.pages.js';
456
19
 
457
- async function main() {
458
- await ensureStaticDocsAssets();
459
- const readmes = await fg(['**/README.md'], { cwd: DOCS_DIR, absolute: true });
20
+ async function main(): Promise<void> {
21
+ // Step 1: Ensure static assets and Mermaid browser bundles are published.
22
+ await ensureRenderDocsAssets();
23
+ await ensureMermaidBrowserAssets();
460
24
 
461
- // Collect metadata for navigation
462
- const pages: PageMeta[] = [];
463
- const mermaidBlocks: MermaidBlockReference[] = [];
464
- for (const mdFile of readmes) {
465
- const md = await fs.readFile(mdFile, 'utf8');
466
- const title =
467
- md.match(/^#\s+(.+)$/m)?.[1] ||
468
- path.relative(DOCS_DIR, path.dirname(mdFile)) ||
469
- 'Documentation';
470
- const relDir = path
471
- .relative(DOCS_DIR, path.dirname(mdFile))
472
- .replace(/\\/g, '/');
473
- pages.push({ abs: mdFile, relDir, title, markdown: md });
474
- mermaidBlocks.push(...collectMermaidBlocks(md, mdFile));
475
- }
25
+ // Step 2: Discover markdown pages and collect Mermaid fences.
26
+ const { pages, mermaidBlocks } = await collectDocsPages();
476
27
 
477
- // Step 1: Validate Mermaid across the docs tree before emitting HTML pages.
28
+ // Step 3: Validate all Mermaid diagrams before emitting HTML.
478
29
  await validateMermaidBlocks(mermaidBlocks);
479
30
 
480
- for (const meta of pages) {
481
- const md = meta.markdown;
482
- // Extract headings for TOC (## file, ### symbol)
483
- const fileHeadings: {
484
- file: string;
485
- anchor: string;
486
- symbols: { name: string; anchor: string }[];
487
- }[] = [];
488
- const lines = md.split(/\r?\n/);
489
- let currentFile: {
490
- file: string;
491
- anchor: string;
492
- symbols: { name: string; anchor: string }[];
493
- } | null = null;
494
- for (const line of lines) {
495
- const fileMatch = /^##\s+(.+\.ts)\s*$/.exec(line);
496
- if (fileMatch) {
497
- const fileName = fileMatch[1];
498
- const anchor = slugify(fileName);
499
- currentFile = { file: fileName, anchor, symbols: [] };
500
- fileHeadings.push(currentFile);
501
- continue;
502
- }
503
- const symMatch = /^###\s+([A-Za-z0-9_]+)\s*$/.exec(line);
504
- if (symMatch && currentFile) {
505
- const sym = symMatch[1];
506
- currentFile.symbols.push({ name: sym, anchor: slugify(sym) });
507
- }
508
- }
509
- // Configure marked renderer with deterministic heading IDs so anchors match our TOC.
510
- const renderer = new marked.Renderer();
511
- const originalCode = renderer.code?.bind(renderer);
512
- let hasMermaidDiagram = false;
513
- // Marked >= v16 passes a single Heading token object { text, depth, raw, tokens }
514
- // See: https://marked.js.org/using_pro#renderer for updated signature.
515
- (renderer as any).heading = ({ text, depth, raw }: any) => {
516
- const source = (raw ?? text ?? '')
517
- .toString()
518
- .replace(/<[^>]+>/g, '')
519
- .trim();
520
- const id = slugify(source);
521
- return `<h${depth} id="${id}">${text}</h${depth}>`;
522
- };
523
- (renderer as any).code = (codeToken: any) => {
524
- const { text, lang } = codeToken;
525
- if ((lang ?? '').toString().trim().toLowerCase() === 'mermaid') {
526
- hasMermaidDiagram = true;
527
- return `<pre class="mermaid mermaid-diagram">${escapeHtml(
528
- (text ?? '').toString(),
529
- )}</pre>`;
530
- }
531
-
532
- return originalCode?.(codeToken) ?? '';
533
- };
534
- marked.use({ renderer });
535
- const rawHtmlBody = marked.parse(md, { async: false });
536
- const htmlBody = rewriteCompiledDocsContentLinks(rawHtmlBody, meta.relDir);
537
- const generatedPageDirectories = new Set(pages.map((page) => page.relDir));
538
- const rootExamplesTocHtml = buildExamplesTocLinksHtml({
539
- currentDir: meta.relDir,
540
- generatedPageDirectories,
541
- hasPublishedDocsPage,
542
- buildRelativeDocsHref,
543
- escapeHtml,
544
- });
545
- const navHtml = buildDocsSidebarHtml({
546
- currentDir: meta.relDir,
547
- pages,
548
- generatedPageDirectories,
549
- hasPublishedDocsPage,
550
- buildRelativeDocsHref,
551
- escapeHtml,
552
- });
553
- const toc = fileHeadings.length
554
- ? `<div class="page-toc"><h2>Files</h2>${fileHeadings
555
- .map(
556
- (f) =>
557
- `<div class=\"toc-file\"><a href=\"#${f.anchor}\">${f.file}</a>${
558
- f.symbols.length
559
- ? `<ul>${f.symbols
560
- .map((s) => `<li><a href=#${s.anchor}>${s.name}</a></li>`)
561
- .join('')}</ul>`
562
- : ''
563
- }</div>`,
564
- )
565
- .join('')}</div>`
566
- : meta.relDir === '' && rootExamplesTocHtml
567
- ? `<div class="page-toc"><h2>Examples</h2><div class="toc-file">${rootExamplesTocHtml}</div></div>`
568
- : '';
569
- const outFile = path.join(path.dirname(meta.abs), 'index.html');
570
- const relToRoot = path
571
- .relative(path.dirname(meta.abs), DOCS_DIR)
572
- .replace(/\\/g, '/');
573
- const cssHref = (relToRoot ? relToRoot + '/' : '') + 'assets/theme.css';
574
- const mermaidBootstrapScript = hasMermaidDiagram
575
- ? buildMermaidBootstrapScript(relToRoot)
576
- : '';
577
- const docsLayoutInteractionScript = buildDocsLayoutInteractionScript();
578
- // Add Examples top-level nav; active when current dir starts with examples
579
- const examplesHref = (relToRoot || '.') + '/examples/index.html';
580
- const onExamples = meta.relDir.startsWith('examples');
581
- const docsActive = !onExamples ? ' class="active"' : '';
582
- const examplesActive = onExamples ? ' class="active"' : '';
583
- const page = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${
584
- meta.title
585
- } – NeatapticTS Docs</title><meta name="viewport" content="width=device-width,initial-scale=1">\n<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;600;700&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">\n<link rel="stylesheet" href="${cssHref}"></head><body class="${
586
- meta.relDir === '' ? 'is-root' : ''
587
- }">\n<header class="topbar"><div class="inner topbar-inner-connected"><div class="brand"><a class="brand-link" href="${
588
- relToRoot || '.'
589
- }/index.html"><span class="brand-title-lockup" aria-hidden="true"><span class="brand-title-row brand-title-row-top"><span class="brand-title-corner">╔══</span><span class="brand-label">NeatapticTS</span><span class="brand-title-corner">══╗</span></span></span><span class="brand-title-accessible">NeatapticTS</span></a></div><nav class="main-nav"><a href="${
590
- relToRoot || '.'
591
- }/index.html">Home</a><a href="${
592
- relToRoot || '.'
593
- }/index.html"${docsActive}>Docs</a><a href="${examplesHref}"${examplesActive}>Examples</a><a href="https://github.com/reicek/NeatapticTS" target="_blank" rel="noopener">GitHub</a></nav></div></header>\n<div class="layout"><aside class="sidebar docs-panel docs-panel-left">${navHtml}</aside><main class="content docs-panel docs-panel-center">${htmlBody}<footer class="site-footer">Generated from source JSDoc • <a href="https://github.com/reicek/NeatapticTS">GitHub</a></footer></main><aside class="toc docs-panel docs-panel-right">${toc}</aside></div>${docsLayoutInteractionScript}${mermaidBootstrapScript}</body></html>`;
594
- await writeFileWithRetry(outFile, page, 'utf8');
595
- }
596
- console.log('HTML docs generated.');
31
+ // Step 4: Render the full HTML page set.
32
+ await emitDocsPages(pages);
597
33
  }
598
34
 
599
35
  main().catch((e) => {