@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
@@ -14,46 +14,336 @@
14
14
  * - Gracefully degrades when fields missing (older artifact schema).
15
15
  * - Extend later for variance/regression annotation summaries.
16
16
  */
17
- import fs from 'fs';
18
- import path from 'path';
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
19
 
20
+ /** Canonical benchmark artifact consumed by the table generator. */
21
+ const BENCHMARK_ARTIFACT_PATH = path.resolve(
22
+ 'test/benchmarks/benchmark.results.json',
23
+ );
24
+
25
+ /** Metrics shown in the variant delta table. */
26
+ const DELTA_METRICS = ['buildMs', 'fwdAvgMs', 'bytesPerConn'] as const;
27
+
28
+ /** Metrics shown in the heap and resident-set table. */
29
+ const HEAP_METRICS = ['heapUsed', 'rss'] as const;
30
+
31
+ /** Full metric set recognized across both current and legacy artifact schemas. */
32
+ const AGGREGATED_METRICS = [...DELTA_METRICS, ...HEAP_METRICS] as const;
33
+
34
+ /** Benchmark variant identifiers emitted by the build-variant suite. */
35
+ type BenchmarkMode = 'src' | 'dist';
36
+
37
+ /** Metric names that participate in src-vs-dist regression comparison. */
38
+ type DeltaMetricName = (typeof DELTA_METRICS)[number];
39
+
40
+ /** Metric names that participate in the heap summary table. */
41
+ type HeapMetricName = (typeof HEAP_METRICS)[number];
42
+
43
+ /** All metric names recognized by the script. */
44
+ type BenchmarkMetricName = (typeof AGGREGATED_METRICS)[number];
45
+
46
+ /** Legacy flat-artifact key names such as `buildMsMean`. */
47
+ type LegacyMeanMetricKey = `${BenchmarkMetricName}Mean`;
48
+
49
+ /**
50
+ * Aggregate statistics for one benchmark metric.
51
+ *
52
+ * The current table generator only consumes `mean`, but the wider schema can
53
+ * also carry percentile and spread values that may be used by later reports.
54
+ */
20
55
  interface AggregatedMetric {
21
- mean: number;
22
- p50: number;
23
- p95: number;
24
- std: number;
56
+ mean?: number;
57
+ p50?: number;
58
+ p95?: number;
59
+ std?: number;
25
60
  }
26
61
 
27
- /** Pad / truncate value to fixed width for monospace block. */
28
- function cell(v: any, w: number): string {
62
+ /**
63
+ * Scenario-level metrics under one size bucket.
64
+ *
65
+ * The script currently reads the `all` scenario, but the normalized shape keeps
66
+ * the scenario layer so future reporting can distinguish specialized runs.
67
+ */
68
+ type AggregatedScenarioBucket = Partial<
69
+ Record<BenchmarkMetricName, AggregatedMetric>
70
+ >;
71
+
72
+ /**
73
+ * Size bucket keyed by scenario name.
74
+ *
75
+ * Example: `1024 -> all -> buildMs -> { mean: ... }`.
76
+ */
77
+ type AggregatedSizeBucket = Record<
78
+ string,
79
+ AggregatedScenarioBucket | undefined
80
+ > & {
81
+ all?: AggregatedScenarioBucket;
82
+ };
83
+
84
+ /** Aggregated metrics keyed by benchmark size. */
85
+ type AggregatedModeBucket = Record<string, AggregatedSizeBucket>;
86
+
87
+ /** Fully normalized aggregated results keyed by build variant. */
88
+ interface NormalizedAggregatedResults {
89
+ src: AggregatedModeBucket;
90
+ dist: AggregatedModeBucket;
91
+ }
92
+
93
+ /** Minimal artifact contract needed by this script. */
94
+ interface BenchmarkArtifact {
95
+ aggregated?: unknown;
96
+ }
97
+
98
+ /**
99
+ * Legacy array-based aggregated entry shape.
100
+ *
101
+ * Older benchmark artifacts stored one flat record per `(mode, size)` pair
102
+ * using `fooMean` properties instead of the nested scenario bucket structure.
103
+ */
104
+ interface LegacyAggregatedEntry extends Partial<
105
+ Record<LegacyMeanMetricKey, number>
106
+ > {
107
+ mode: BenchmarkMode;
108
+ size: number | string;
109
+ }
110
+
111
+ /**
112
+ * Pads or truncates a value to fixed width for the fenced monospace tables.
113
+ *
114
+ * @param v - Cell value to render.
115
+ * @param w - Target cell width.
116
+ * @returns Fixed-width cell content.
117
+ */
118
+ function cell(v: string | number | undefined | null, w: number): string {
29
119
  const s = String(v ?? '');
30
120
  return s.length >= w ? s.slice(0, w) : s + ' '.repeat(w - s.length);
31
121
  }
32
122
 
33
- /** Format number with trimmed trailing zeros. */
34
- function fmtNum(n: any, digits = 4): string {
35
- if (typeof n !== 'number' || !isFinite(n)) return '';
123
+ /**
124
+ * Formats a numeric value with trimmed trailing zeros.
125
+ *
126
+ * This keeps markdown tables compact without losing the ability to request
127
+ * higher precision for smaller benchmark values.
128
+ *
129
+ * @param n - Candidate numeric value.
130
+ * @param digits - Maximum fixed decimal precision.
131
+ * @returns Formatted number or an empty string when the input is not finite.
132
+ */
133
+ function fmtNum(n: number | undefined, digits = 4): string {
134
+ if (typeof n !== 'number' || !Number.isFinite(n)) return '';
36
135
  return n.toFixed(digits).replace(/0+$/, '').replace(/\.$/, '');
37
136
  }
38
137
 
39
- function loadArtifact(): any | null {
40
- const file = path.resolve('test/benchmarks/benchmark.results.json');
41
- if (!fs.existsSync(file)) {
42
- console.error('[bench:tables] Artifact not found:', file);
138
+ /**
139
+ * Loads the benchmark artifact from disk.
140
+ *
141
+ * @returns Parsed artifact when present and readable, otherwise `null`.
142
+ */
143
+ function loadArtifact(): BenchmarkArtifact | null {
144
+ if (!fs.existsSync(BENCHMARK_ARTIFACT_PATH)) {
145
+ console.error(
146
+ '[bench:tables] Artifact not found:',
147
+ BENCHMARK_ARTIFACT_PATH,
148
+ );
43
149
  return null;
44
150
  }
151
+
45
152
  try {
46
- return JSON.parse(fs.readFileSync(file, 'utf8'));
47
- } catch (e) {
48
- console.error('[bench:tables] Parse error', e);
153
+ return JSON.parse(
154
+ fs.readFileSync(BENCHMARK_ARTIFACT_PATH, 'utf8'),
155
+ ) as BenchmarkArtifact;
156
+ } catch (error) {
157
+ console.error('[bench:tables] Parse error', error);
49
158
  return null;
50
159
  }
51
160
  }
52
161
 
53
- function buildVariantDeltaTable(artifact: any): string {
54
- const agg = artifact.aggregated || {};
55
- const sizes = new Set<string>(Object.keys(agg.src || {}));
56
- const ordered = Array.from(sizes).sort((a, b) => parseInt(a) - parseInt(b));
162
+ /**
163
+ * Normalizes the artifact's aggregated payload into one stable nested shape.
164
+ *
165
+ * The benchmark pipeline has emitted both nested and legacy flat array schemas.
166
+ * Folding both into the same mode/size/scenario/metric structure keeps the
167
+ * table builders simple and resilient.
168
+ *
169
+ * @param artifact - Parsed benchmark artifact.
170
+ * @returns Normalized aggregated result buckets.
171
+ */
172
+ function normalizeAggregated(
173
+ artifact: BenchmarkArtifact,
174
+ ): NormalizedAggregatedResults {
175
+ if (Array.isArray(artifact.aggregated)) {
176
+ return normalizeLegacyAggregated(artifact.aggregated);
177
+ }
178
+
179
+ if (!isRecord(artifact.aggregated)) {
180
+ return createEmptyAggregatedResults();
181
+ }
182
+
183
+ return {
184
+ src: normalizeModeBucket(artifact.aggregated.src),
185
+ dist: normalizeModeBucket(artifact.aggregated.dist),
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Converts the older flat array schema into the normalized nested shape.
191
+ *
192
+ * @param rawEntries - Legacy aggregated entries.
193
+ * @returns Normalized aggregated result buckets.
194
+ */
195
+ function normalizeLegacyAggregated(
196
+ rawEntries: readonly unknown[],
197
+ ): NormalizedAggregatedResults {
198
+ const aggregatedResults = createEmptyAggregatedResults();
199
+
200
+ for (const rawEntry of rawEntries) {
201
+ if (!isLegacyAggregatedEntry(rawEntry)) {
202
+ continue;
203
+ }
204
+
205
+ const sizeKey = String(rawEntry.size);
206
+ const scenarioBucket = ensureAllScenarioBucket(
207
+ aggregatedResults[rawEntry.mode],
208
+ sizeKey,
209
+ );
210
+
211
+ for (const metricName of AGGREGATED_METRICS) {
212
+ const meanKey = `${metricName}Mean` as const;
213
+ const meanValue = rawEntry[meanKey];
214
+ if (typeof meanValue === 'number' && Number.isFinite(meanValue)) {
215
+ scenarioBucket[metricName] = { mean: meanValue };
216
+ }
217
+ }
218
+ }
219
+
220
+ return aggregatedResults;
221
+ }
222
+
223
+ /**
224
+ * Normalizes one top-level mode bucket such as `src` or `dist`.
225
+ *
226
+ * @param rawModeBucket - Unknown raw mode payload.
227
+ * @returns Normalized size buckets.
228
+ */
229
+ function normalizeModeBucket(rawModeBucket: unknown): AggregatedModeBucket {
230
+ if (!isRecord(rawModeBucket)) {
231
+ return {};
232
+ }
233
+
234
+ return Object.fromEntries(
235
+ Object.entries(rawModeBucket).map(([sizeKey, rawSizeBucket]) => [
236
+ sizeKey,
237
+ normalizeSizeBucket(rawSizeBucket),
238
+ ]),
239
+ );
240
+ }
241
+
242
+ /**
243
+ * Normalizes one size bucket keyed by scenario names.
244
+ *
245
+ * @param rawSizeBucket - Unknown raw size payload.
246
+ * @returns Normalized scenario buckets.
247
+ */
248
+ function normalizeSizeBucket(rawSizeBucket: unknown): AggregatedSizeBucket {
249
+ if (!isRecord(rawSizeBucket)) {
250
+ return {};
251
+ }
252
+
253
+ return Object.fromEntries(
254
+ Object.entries(rawSizeBucket).map(([scenarioName, rawScenarioBucket]) => [
255
+ scenarioName,
256
+ normalizeScenarioBucket(rawScenarioBucket),
257
+ ]),
258
+ ) as AggregatedSizeBucket;
259
+ }
260
+
261
+ /**
262
+ * Normalizes one scenario bucket keyed by metric name.
263
+ *
264
+ * Invalid or incomplete metric objects are dropped rather than guessed.
265
+ *
266
+ * @param rawScenarioBucket - Unknown raw scenario payload.
267
+ * @returns Normalized metric bucket.
268
+ */
269
+ function normalizeScenarioBucket(
270
+ rawScenarioBucket: unknown,
271
+ ): AggregatedScenarioBucket {
272
+ if (!isRecord(rawScenarioBucket)) {
273
+ return {};
274
+ }
275
+
276
+ const normalizedEntries = AGGREGATED_METRICS.flatMap((metricName) => {
277
+ const rawMetric = rawScenarioBucket[metricName];
278
+ if (!isRecord(rawMetric)) {
279
+ return [];
280
+ }
281
+
282
+ const meanValue = rawMetric.mean;
283
+ if (typeof meanValue !== 'number' || !Number.isFinite(meanValue)) {
284
+ return [];
285
+ }
286
+
287
+ return [[metricName, { mean: meanValue }] as const];
288
+ });
289
+
290
+ return Object.fromEntries(normalizedEntries) as AggregatedScenarioBucket;
291
+ }
292
+
293
+ /**
294
+ * Ensures the `all` scenario exists for one `(mode, size)` pair.
295
+ *
296
+ * @param modeBucket - Normalized mode bucket.
297
+ * @param sizeKey - Size identifier.
298
+ * @returns Mutable `all` scenario bucket.
299
+ */
300
+ function ensureAllScenarioBucket(
301
+ modeBucket: AggregatedModeBucket,
302
+ sizeKey: string,
303
+ ): AggregatedScenarioBucket {
304
+ modeBucket[sizeKey] ??= {};
305
+ modeBucket[sizeKey].all ??= {};
306
+ return modeBucket[sizeKey].all!;
307
+ }
308
+
309
+ /**
310
+ * Creates an empty normalized results shell.
311
+ *
312
+ * @returns Empty `src` and `dist` mode buckets.
313
+ */
314
+ function createEmptyAggregatedResults(): NormalizedAggregatedResults {
315
+ return {
316
+ src: {},
317
+ dist: {},
318
+ };
319
+ }
320
+
321
+ /**
322
+ * Resolves size keys in ascending numeric order.
323
+ *
324
+ * @param aggregatedResults - Normalized aggregated result buckets.
325
+ * @returns Ordered size keys.
326
+ */
327
+ function resolveOrderedSizes(
328
+ aggregatedResults: NormalizedAggregatedResults,
329
+ ): string[] {
330
+ return [...new Set(Object.keys(aggregatedResults.src))].toSorted(
331
+ compareNumericSizeKeys,
332
+ );
333
+ }
334
+
335
+ /**
336
+ * Builds the variant delta table comparing `src` and `dist` means.
337
+ *
338
+ * The result is tuned for markdown fenced blocks rather than GFM pipe tables,
339
+ * which keeps wide benchmark rows easier to scan in docs and pull requests.
340
+ *
341
+ * @param artifact - Parsed benchmark artifact.
342
+ * @returns Monospace markdown table body.
343
+ */
344
+ function buildVariantDeltaTable(artifact: BenchmarkArtifact): string {
345
+ const aggregatedResults = normalizeAggregated(artifact);
346
+ const orderedSizes = resolveOrderedSizes(aggregatedResults);
57
347
  const rows: string[] = [];
58
348
  rows.push(
59
349
  'Size Metric Src Mean Dist Mean Δ Abs Δ % Flag Result',
@@ -61,13 +351,10 @@ function buildVariantDeltaTable(artifact: any): string {
61
351
  rows.push(
62
352
  '------ ----------------- ------------- ------------- ------------- ---------- ------------- ------------------------------------------------------------',
63
353
  );
64
- const metrics = ['buildMs', 'fwdAvgMs', 'bytesPerConn'];
65
- for (const size of ordered) {
66
- for (const metric of metrics) {
67
- const srcAgg: AggregatedMetric | undefined =
68
- agg.src?.[size]?.all?.[metric];
69
- const distAgg: AggregatedMetric | undefined =
70
- agg.dist?.[size]?.all?.[metric];
354
+ for (const size of orderedSizes) {
355
+ for (const metric of DELTA_METRICS) {
356
+ const srcAgg = aggregatedResults.src[size]?.all?.[metric];
357
+ const distAgg = aggregatedResults.dist[size]?.all?.[metric];
71
358
  const sMean = srcAgg?.mean;
72
359
  const dMean = distAgg?.mean;
73
360
  let dAbs: number | undefined;
@@ -123,10 +410,15 @@ function buildVariantDeltaTable(artifact: any): string {
123
410
  return rows.join('\n');
124
411
  }
125
412
 
126
- function buildHeapTable(artifact: any): string {
127
- const agg = artifact.aggregated || {};
128
- const sizes = new Set<string>(Object.keys(agg.src || {}));
129
- const ordered = Array.from(sizes).sort((a, b) => parseInt(a) - parseInt(b));
413
+ /**
414
+ * Builds the heap and RSS comparison table.
415
+ *
416
+ * @param artifact - Parsed benchmark artifact.
417
+ * @returns Monospace markdown table body.
418
+ */
419
+ function buildHeapTable(artifact: BenchmarkArtifact): string {
420
+ const aggregatedResults = normalizeAggregated(artifact);
421
+ const orderedSizes = resolveOrderedSizes(aggregatedResults);
130
422
  const rows: string[] = [];
131
423
  rows.push(
132
424
  'Size Metric Src Bytes Dist Bytes Src MB Dist MB Δ Bytes Δ % Note',
@@ -134,10 +426,10 @@ function buildHeapTable(artifact: any): string {
134
426
  rows.push(
135
427
  '------ -------------- ------------ ------------ --------- --------- ----------- ------- -------------------------------------------------',
136
428
  );
137
- for (const size of ordered) {
138
- for (const metric of ['heapUsed', 'rss']) {
139
- const s = agg.src?.[size]?.all?.[metric]?.mean;
140
- const d = agg.dist?.[size]?.all?.[metric]?.mean;
429
+ for (const size of orderedSizes) {
430
+ for (const metric of HEAP_METRICS) {
431
+ const s = aggregatedResults.src[size]?.all?.[metric]?.mean;
432
+ const d = aggregatedResults.dist[size]?.all?.[metric]?.mean;
141
433
  if (typeof s !== 'number' || typeof d !== 'number') continue;
142
434
  const dBytes = d - s;
143
435
  const dPct = s === 0 ? 0 : (dBytes / s) * 100;
@@ -165,6 +457,55 @@ function buildHeapTable(artifact: any): string {
165
457
  return rows.join('\n');
166
458
  }
167
459
 
460
+ /**
461
+ * Compares size keys numerically, falling back to lexical comparison.
462
+ *
463
+ * @param left - Left size key.
464
+ * @param right - Right size key.
465
+ * @returns Standard comparator delta.
466
+ */
467
+ function compareNumericSizeKeys(left: string, right: string): number {
468
+ const leftValue = Number.parseInt(left, 10);
469
+ const rightValue = Number.parseInt(right, 10);
470
+
471
+ if (Number.isNaN(leftValue) || Number.isNaN(rightValue)) {
472
+ return left.localeCompare(right);
473
+ }
474
+
475
+ return leftValue - rightValue || left.localeCompare(right);
476
+ }
477
+
478
+ /**
479
+ * Narrows an unknown value to the legacy flat aggregated entry shape.
480
+ *
481
+ * @param value - Candidate legacy entry.
482
+ * @returns `true` when the value matches the legacy schema.
483
+ */
484
+ function isLegacyAggregatedEntry(
485
+ value: unknown,
486
+ ): value is LegacyAggregatedEntry {
487
+ return (
488
+ isRecord(value) &&
489
+ (value.mode === 'src' || value.mode === 'dist') &&
490
+ (typeof value.size === 'number' || typeof value.size === 'string')
491
+ );
492
+ }
493
+
494
+ /**
495
+ * Narrows an unknown value to a plain object record.
496
+ *
497
+ * @param value - Candidate value.
498
+ * @returns `true` when the value is a non-array object.
499
+ */
500
+ function isRecord(value: unknown): value is Record<string, unknown> {
501
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
502
+ }
503
+
504
+ /**
505
+ * Runs the artifact load and emits both markdown tables to standard output.
506
+ *
507
+ * @returns Nothing.
508
+ */
168
509
  function main() {
169
510
  const artifact = loadArtifact();
170
511
  if (!artifact) process.exit(1);
@@ -0,0 +1,107 @@
1
+ /*
2
+ * Centralizes stable docs-generator constants and target definitions.
3
+ *
4
+ * Keeping these values in one file makes it easier to audit what the generator
5
+ * considers a target, a published output root, or an ordering-control file
6
+ * without mixing that policy into traversal or rendering code.
7
+ */
8
+
9
+ import * as path from 'path';
10
+
11
+ import type { DocsTargetConfig } from './generate-docs.types.js';
12
+
13
+ /** Root docs output directory mirrored into the published docs site. */
14
+ export const DOCS_DIR = path.resolve('docs');
15
+
16
+ /** Default docs target when no CLI or environment override is supplied. */
17
+ export const DEFAULT_DOCS_TARGET = 'src';
18
+
19
+ /** Synthetic symbol name used to store file-level summary content. */
20
+ export const FILE_SUMMARY_SYMBOL_NAME = '__file_summary__';
21
+
22
+ /** Source-file glob used to discover docs-generator inputs. */
23
+ export const SOURCE_FILE_GLOBS = ['**/*.ts'];
24
+
25
+ /** Ignore rules for files that should never become generated docs sources. */
26
+ export const SOURCE_FILE_IGNORE_GLOBS = ['**/*.d.ts'];
27
+
28
+ /** Generated docs landing page file name for the folder index. */
29
+ export const FOLDER_INDEX_FILE_NAME = 'FOLDERS.md';
30
+
31
+ /** Per-directory ordering config file name. */
32
+ export const DOCS_ORDER_CONFIG_FILE_NAME = 'docs.order.json';
33
+
34
+ /** Supported keys accepted inside `docs.order.json`. */
35
+ export const DOCS_ORDER_SUPPORTED_KEYS = [
36
+ 'introFile',
37
+ 'fileOrder',
38
+ 'symbolOrder',
39
+ 'folderOrder',
40
+ 'hiddenFiles',
41
+ 'hiddenSymbols',
42
+ ] as const;
43
+
44
+ /** Workspace root used to rewrite absolute ts-morph type paths into repo paths. */
45
+ export const WORKSPACE_ROOT_DIR = path.resolve('.');
46
+
47
+ /**
48
+ * Docs targets supported by the folder README generator.
49
+ *
50
+ * Each target maps one source tree into one published docs tree while keeping
51
+ * any target-specific root README mirroring rules local to the target.
52
+ */
53
+ export const DOCS_TARGETS: Record<string, DocsTargetConfig> = {
54
+ src: {
55
+ name: 'src',
56
+ sourceDir: path.resolve('src'),
57
+ docsDir: DOCS_DIR,
58
+ rootDocsDir: path.join(DOCS_DIR, 'src'),
59
+ rootReadmeSource: path.resolve('README.md'),
60
+ rootReadmeDestination: path.join(DOCS_DIR, 'README.md'),
61
+ includeFolderIndex: true,
62
+ },
63
+ asciiMaze: {
64
+ name: 'asciiMaze',
65
+ sourceDir: path.resolve('test', 'examples', 'asciiMaze'),
66
+ docsDir: path.join(DOCS_DIR, 'examples', 'asciiMaze', 'docs'),
67
+ rootDocsDir: path.join(DOCS_DIR, 'examples', 'asciiMaze', 'docs'),
68
+ rootReadmeSource: path.resolve(
69
+ 'test',
70
+ 'examples',
71
+ 'asciiMaze',
72
+ 'README.md',
73
+ ),
74
+ rootReadmeDestination: path.join(
75
+ DOCS_DIR,
76
+ 'examples',
77
+ 'asciiMaze',
78
+ 'docs',
79
+ 'README.md',
80
+ ),
81
+ excludeRootSourceFiles: true,
82
+ publishedRootDir: path.join(DOCS_DIR, 'examples', 'asciiMaze'),
83
+ preservePublishedEntries: ['index.html'],
84
+ },
85
+ 'flappy-bird': {
86
+ name: 'flappy-bird',
87
+ sourceDir: path.resolve('test', 'examples', 'flappy_bird'),
88
+ docsDir: path.join(DOCS_DIR, 'examples', 'flappy_bird', 'docs'),
89
+ rootDocsDir: path.join(DOCS_DIR, 'examples', 'flappy_bird', 'docs'),
90
+ rootReadmeSource: path.resolve(
91
+ 'test',
92
+ 'examples',
93
+ 'flappy_bird',
94
+ 'README.md',
95
+ ),
96
+ rootReadmeDestination: path.join(
97
+ DOCS_DIR,
98
+ 'examples',
99
+ 'flappy_bird',
100
+ 'docs',
101
+ 'README.md',
102
+ ),
103
+ excludeRootSourceFiles: true,
104
+ publishedRootDir: path.join(DOCS_DIR, 'examples', 'flappy_bird'),
105
+ preservePublishedEntries: ['index.html'],
106
+ },
107
+ };