@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
@@ -9,13 +9,16 @@ The source of truth is the generated documentation fed by [src/neat.ts](../src/n
9
9
  which renders into [src/README.md](../src/README.md) after `npm run docs`.
10
10
 
11
11
  Primary reader:
12
+
12
13
  - first-time NeatapticTS readers who want to understand how the top-level NEAT controller works before diving into chaptered internals.
13
14
 
14
15
  Primary surfaces:
16
+
15
17
  - [src/neat.ts](../src/neat.ts)
16
18
  - [src/README.md](../src/README.md)
17
19
 
18
20
  Out of scope for this pass:
21
+
19
22
  - hand-editing generated READMEs under `src/`
20
23
  - adding external images or citations unless a concrete teaching gap requires them
21
24
  - broad rewrites of every `src/neat/**` chapter README
@@ -29,21 +32,26 @@ Out of scope for this pass:
29
32
  ## Session Log
30
33
 
31
34
  ### Foundation
35
+
32
36
  - Strengthened [src/neat.ts](../src/neat.ts) so the generated root story in [src/README.md](../src/README.md) reads as a clearer controller overview.
33
37
 
34
38
  ### Generator follow-up
35
- - The docs generator work is complete enough for now: [scripts/generate-docs.ts](../scripts/generate-docs.ts) supports per-folder ordering controls through `docs.order.json`, and the shared-speciation pilot is already live in [src/neat/speciation/shared/docs.order.json](../src/neat/speciation/shared/docs.order.json).
39
+
40
+ - The docs generator work is complete enough for now: [scripts/generate-docs/generate-docs.ts](../scripts/generate-docs/generate-docs.ts) supports per-folder ordering controls through `docs.order.json`, and the shared-speciation pilot is already live in [src/neat/speciation/shared/docs.order.json](../src/neat/speciation/shared/docs.order.json).
36
41
 
37
42
  ### Latest completed chapter work
43
+
38
44
  - Recent source-first passes strengthened [src/neat/shared/neat.shared.types.ts](../src/neat/shared/neat.shared.types.ts), [src/neat/neat.constants.ts](../src/neat/neat.constants.ts), [src/neat/cache/cache.ts](../src/neat/cache/cache.ts), [src/neat/compat/compat.ts](../src/neat/compat/compat.ts), [src/neat/harness/neat.harness.types.ts](../src/neat/harness/neat.harness.types.ts), [src/neat/init/neat.init.ts](../src/neat/init/neat.init.ts), [src/neat/helpers/neat.helpers.ts](../src/neat/helpers/neat.helpers.ts), and [src/neat/rng/rng.ts](../src/neat/rng/rng.ts); the RNG root now distinguishes caller-owned randomness from controller-owned replay state more explicitly, and the root [src/README.md](../src/README.md) plus [src/neat.ts](../src/neat.ts) story still holds up against the current pedagogical standard. Docs regeneration and TypeScript validation should be kept on every new pass.
39
45
  - A bounded root-surface polish split then moved the root compatibility alias shelf into [src/neat/neat.types.ts](../src/neat/neat.types.ts), moved the public defaults shelf into [src/neat/neat.defaults.constants.ts](../src/neat/neat.defaults.constants.ts), and centralized the empty diversity fallback in [src/neat/diversity/diversity.ts](../src/neat/diversity/diversity.ts); [src/README.md](../src/README.md) still presents the stable public root story while [src/neat/README.md](../src/neat/README.md) now carries the deeper root-types and root-defaults chapter docs.
40
46
 
41
47
  Remaining gaps:
48
+
42
49
  - Most remaining weaknesses are now small chapter-quality revisits, not untouched surfaces.
43
50
  - Generator limits still show up in some larger or re-export-heavy chapters, so only use `docs.order.json` when ordering is the real blocker.
44
51
  - Keep prioritizing compact runtime, facade, or policy boundaries that still read more like shelves than chapters.
45
52
 
46
53
  Next step:
54
+
47
55
  - Re-read [src/neat/lineage/README.md](../src/neat/lineage/README.md) and [src/neat/lineage/lineage.ts](../src/neat/lineage/lineage.ts) to decide whether the lineage root is now the next weakest compact controller-facing boundary after the RNG pass.
48
56
 
49
57
  ## Handoff Prompt
@@ -35,4 +35,4 @@ features and root helper facades compile against the existing test suite.
35
35
  ```text
36
36
  Continue from the current repo state only. Do not rely on prior chat history.
37
37
  The NEAT public-surface repair tracked in plans/neat-test-surface-repair.plans.md is complete. Do not make further NEAT-surface changes unless new evidence appears. If follow-up work is needed, start a separate pass for the remaining TypeScript diagnostics under test/examples/asciiMaze/**. Preserve unrelated user changes.
38
- ```
38
+ ```
@@ -0,0 +1,68 @@
1
+ # Render Docs HTML SOLID Split
2
+
3
+ **Status:** [DONE]
4
+
5
+ ## Scope
6
+
7
+ - Consolidate the HTML docs renderer into a folder-owned boundary rooted at `scripts/render-docs-html/`.
8
+ - Keep `scripts/render-docs-html.ts` as the stable orchestration entrypoint.
9
+ - Remove the flat `scripts/render-docs-html.sidebar.ts` file once the new direct-path chapter imports are in place.
10
+ - Improve link correctness, sidebar clarity, and docs UX where those issues are exposed by the split.
11
+
12
+ ## Current state
13
+
14
+ - Split root: `scripts/render-docs-html/` with `scripts/render-docs-html.ts` as the stable orchestration entrypoint
15
+ - README inventory for `scripts/`: none under `scripts/**/README.md`
16
+ - Relevant plan: [plans/Interactive_Examples_and_Learning_Path.md](../plans/Interactive_Examples_and_Learning_Path.md)
17
+ - Relevant repo memory: `/memories/repo/scripts_docs_sidebar_boundary.md`
18
+ - Stable entrypoint: `scripts/render-docs-html.ts`
19
+ - Landed chapter files:
20
+ - `render-docs-html.assets.ts`
21
+ - `render-docs-html.mermaid.ts`
22
+ - `render-docs-html.navigation.ts`
23
+ - `render-docs-html.pages.ts`
24
+ - `render-docs-html.shared.ts`
25
+ - `render-docs-html.types.ts`
26
+ - Closed issues in this step:
27
+ - nested docs content links now resolve through compiled docs targets or repository URLs instead of remaining raw repo-relative hrefs,
28
+ - deep sidebar labels now use clearer formatted names instead of collapsing to ambiguous leaf-only segments,
29
+ - examples-first showcase now collapses to a lighter compact mode away from onboarding/example surfaces.
30
+
31
+ ## Coverage backlog
32
+
33
+ ### [DONE] Consolidated renderer folder split
34
+
35
+ - Created `scripts/render-docs-html/` with focused chapters for shared helpers, navigation, Mermaid handling, page rendering, static assets, and shared contracts.
36
+ - Kept `scripts/render-docs-html.ts` orchestration-first and moved the heavy logic behind direct chapter imports.
37
+ - Deleted `scripts/render-docs-html.sidebar.ts` after the direct-path migration landed.
38
+ - Folded link-rewrite fixes and targeted sidebar UX improvements into the same step.
39
+
40
+ ### [DONE] Educational docs follow-up
41
+
42
+ - Completed the required `educational-docs` follow-up on the touched renderer boundary.
43
+ - Strengthened chapter-level documentation and shared type descriptions so the renderer reads as an intentional docs-tooling subsystem rather than a loose helper shelf.
44
+
45
+ ## Validation
46
+
47
+ - File diagnostics: clean for `scripts/render-docs-html.ts` and all files under `scripts/render-docs-html/`.
48
+ - `npm run docs`: passed.
49
+
50
+ ## Immediate next steps
51
+
52
+ - No further split work is queued for this boundary right now.
53
+ - Future work should start from `scripts/render-docs-html/` and preserve `scripts/render-docs-html.ts` as the stable entrypoint.
54
+
55
+ ## Handoff query
56
+
57
+ ```text
58
+ Continue from the current repo state only. Do not rely on prior chat history.
59
+
60
+ Use solid-split for #file:render-docs-html.ts and #file:render-docs-html.sidebar.ts.
61
+ Plan: plans/render-docs-html-solid-split.plans.md.
62
+ Current boundary: scripts/render-docs-html.
63
+ Completed context: scripts has no local README surface, the stable entrypoint remains scripts/render-docs-html.ts, the old flat sidebar file is deleted, and the folder boundary now owns assets, Mermaid handling, navigation, shared helpers, page rendering, and shared contracts.
64
+ If this boundary reopens: continue from the folderized renderer, preserving the improved content-link rewriting and the lighter examples/sidebar behavior instead of recreating flat helper files.
65
+ Repo standard: direct-path migration with no compatibility shims by default, and the small-chapter README pattern applies to scripts/tooling boundaries too.
66
+ Required validations: file diagnostics for touched files, then npm run docs.
67
+ Worktree caution: npm run docs rewrites generated docs outputs and there may already be unrelated generated-file drift in the worktree.
68
+ ```
@@ -172,4 +172,4 @@ Current repo state:
172
172
  Next target:
173
173
  - No active target in this lane. Reopen only if future edits reintroduce explicit-any debt or new broad lint drift into `src/`.
174
174
  - If reopened, begin with a fresh raw scan, `npx eslint src`, and `npx tsc --noEmit -p tsconfig.json` before selecting a new target.
175
- ```
175
+ ```
@@ -8,10 +8,12 @@ This plan tracks educational-docs passes for the small shared utility surfaces
8
8
  under [src/utils/README.md](../src/utils/README.md).
9
9
 
10
10
  Primary reader:
11
+
11
12
  - readers who want to understand support utilities as teaching tools rather
12
13
  than as unframed implementation leftovers.
13
14
 
14
15
  Primary surfaces:
16
+
15
17
  - [src/utils/memory.ts](../src/utils/memory.ts)
16
18
  - [src/utils/memory.utils.ts](../src/utils/memory.utils.ts)
17
19
  - [src/utils/README.md](../src/utils/README.md)
@@ -19,6 +21,7 @@ Primary surfaces:
19
21
  ## Session Log
20
22
 
21
23
  ### Latest completed chapter work
24
+
22
25
  - Strengthened the root [src/utils/memory.ts](../src/utils/memory.ts) opening so
23
26
  the generated [src/utils/README.md](../src/utils/README.md) now explains why
24
27
  heuristic memory instrumentation exists, which questions it answers, and how
@@ -33,12 +36,14 @@ Primary surfaces:
33
36
  blocking global docs generation.
34
37
 
35
38
  Remaining gaps:
39
+
36
40
  - The `src/utils` folder is now small and reasonably aligned; future work here
37
41
  is more likely to be refinement than rescue.
38
42
  - If additional utility modules are added later, keep this plan focused on
39
43
  chapter quality instead of growing a long historical log.
40
44
 
41
45
  Next step:
46
+
42
47
  - Move to the next small cross-cutting folder that still reads more like a
43
48
  helper shelf than a chapter, using [src/utils/README.md](../src/utils/README.md)
44
- as the quality bar for support modules.
49
+ as the quality bar for support modules.
@@ -0,0 +1,479 @@
1
+ import {
2
+ BEGIN_FRAME_EVENT_NAME,
3
+ DROPPED_FRAME_EVENT_NAME,
4
+ FIRE_ANIMATION_FRAME_EVENT_NAME,
5
+ FUNCTION_CALL_EVENT_NAME,
6
+ LONG_TASK_THRESHOLD_MS,
7
+ MICROSECONDS_PER_MILLISECOND,
8
+ RUN_TASK_EVENT_NAME,
9
+ UNKNOWN_PROCESS_LABEL,
10
+ UNKNOWN_SCRIPT_LABEL,
11
+ UNKNOWN_THREAD_LABEL,
12
+ VERY_LONG_TASK_THRESHOLD_MS,
13
+ } from './analyze-trace.constants.js';
14
+ import {
15
+ createProcessKey,
16
+ createThreadKey,
17
+ readNestedPrimitive,
18
+ } from './analyze-trace.shared.js';
19
+ import type {
20
+ AggregatedDuration,
21
+ LongEventSummary,
22
+ ThreadSummary,
23
+ TraceAnalysis,
24
+ TraceEvent,
25
+ TraceTimeRange,
26
+ } from './analyze-trace.types.js';
27
+
28
+ type TraceMetadataName = 'process_name' | 'thread_name';
29
+
30
+ interface TraceAnalysisInput {
31
+ traceFilePath: string;
32
+ traceEvents: readonly TraceEvent[];
33
+ topCount: number;
34
+ }
35
+
36
+ interface TraceLabelContext {
37
+ processNames: ReadonlyMap<string, string>;
38
+ threadNames: ReadonlyMap<string, string>;
39
+ workerThreadLabels: ReadonlyMap<string, string>;
40
+ }
41
+
42
+ /**
43
+ * Analyzes one trace event collection into deterministic report data.
44
+ *
45
+ * @param input - Trace analysis input packet.
46
+ * @returns Fully analyzed trace payload used by the reporting layer.
47
+ */
48
+ export function analyzeTraceEvents(input: TraceAnalysisInput): TraceAnalysis {
49
+ const processNames = collectMetadataNames(input.traceEvents, 'process_name');
50
+ const threadNames = collectMetadataNames(input.traceEvents, 'thread_name');
51
+ const workerThreadLabels = collectWorkerThreadLabels(input.traceEvents);
52
+ const labelContext: TraceLabelContext = {
53
+ processNames,
54
+ threadNames,
55
+ workerThreadLabels,
56
+ };
57
+
58
+ return {
59
+ traceFilePath: input.traceFilePath,
60
+ traceEventCount: input.traceEvents.length,
61
+ timeRange: resolveTimeRange(input.traceEvents),
62
+ droppedFrameCount: countNamedEvents(
63
+ input.traceEvents,
64
+ DROPPED_FRAME_EVENT_NAME,
65
+ ),
66
+ beginFrameCount: countNamedEvents(
67
+ input.traceEvents,
68
+ BEGIN_FRAME_EVENT_NAME,
69
+ ),
70
+ threadSummaries: buildThreadSummaries(input.traceEvents, labelContext),
71
+ hottestEvents: aggregateDurationsByName(input.traceEvents, input.topCount),
72
+ hottestFunctionCalls: aggregateFunctionCalls(
73
+ input.traceEvents,
74
+ input.topCount,
75
+ ),
76
+ longestEvents: collectLongestEvents(
77
+ input.traceEvents,
78
+ labelContext,
79
+ input.topCount,
80
+ ),
81
+ fireAnimationFrameDurations: collectEventDurationsMs(
82
+ input.traceEvents,
83
+ FIRE_ANIMATION_FRAME_EVENT_NAME,
84
+ ),
85
+ functionCallDurations: collectEventDurationsMs(
86
+ input.traceEvents,
87
+ FUNCTION_CALL_EVENT_NAME,
88
+ ),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Collects metadata names keyed by process or thread id.
94
+ *
95
+ * @param traceEvents - Trace events to scan.
96
+ * @param metadataName - Metadata event name to collect.
97
+ * @returns Metadata map keyed by stable process or thread keys.
98
+ */
99
+ function collectMetadataNames(
100
+ traceEvents: readonly TraceEvent[],
101
+ metadataName: TraceMetadataName,
102
+ ): Map<string, string> {
103
+ const metadataNames = new Map<string, string>();
104
+
105
+ for (const traceEvent of traceEvents) {
106
+ if (traceEvent.ph !== 'M' || traceEvent.name !== metadataName) {
107
+ continue;
108
+ }
109
+
110
+ const resolvedName = readNestedPrimitive(traceEvent.args, ['name']);
111
+ if (typeof resolvedName !== 'string') {
112
+ continue;
113
+ }
114
+
115
+ const metadataKey =
116
+ metadataName === 'process_name'
117
+ ? createProcessKey(traceEvent.pid)
118
+ : createThreadKey(traceEvent.pid, traceEvent.tid);
119
+ if (!metadataKey) {
120
+ continue;
121
+ }
122
+
123
+ metadataNames.set(metadataKey, resolvedName);
124
+ }
125
+
126
+ return metadataNames;
127
+ }
128
+
129
+ /**
130
+ * Collects worker-thread labels from worker attachment events.
131
+ *
132
+ * @param traceEvents - Trace events to scan.
133
+ * @returns Worker thread label map keyed by stable thread keys.
134
+ */
135
+ function collectWorkerThreadLabels(
136
+ traceEvents: readonly TraceEvent[],
137
+ ): Map<string, string> {
138
+ const workerThreadLabels = new Map<string, string>();
139
+
140
+ for (const traceEvent of traceEvents) {
141
+ if (traceEvent.name !== 'TracingSessionIdForWorker') {
142
+ continue;
143
+ }
144
+
145
+ const workerThreadId = readNestedPrimitive(traceEvent.args, [
146
+ 'data',
147
+ 'workerThreadId',
148
+ ]);
149
+ const workerUrl = readNestedPrimitive(traceEvent.args, ['data', 'url']);
150
+ if (typeof workerThreadId !== 'number' || typeof workerUrl !== 'string') {
151
+ continue;
152
+ }
153
+
154
+ const threadKey = createThreadKey(traceEvent.pid, workerThreadId);
155
+ if (!threadKey) {
156
+ continue;
157
+ }
158
+
159
+ workerThreadLabels.set(threadKey, workerUrl);
160
+ }
161
+
162
+ return workerThreadLabels;
163
+ }
164
+
165
+ /**
166
+ * Computes the visible time window spanned by the trace.
167
+ *
168
+ * @param traceEvents - Trace events to scan.
169
+ * @returns Visible trace window metrics.
170
+ */
171
+ function resolveTimeRange(traceEvents: readonly TraceEvent[]): TraceTimeRange {
172
+ let minimumTimestamp = Number.POSITIVE_INFINITY;
173
+ let maximumTimestamp = 0;
174
+
175
+ for (const traceEvent of traceEvents) {
176
+ const eventTimestamp =
177
+ typeof traceEvent.ts === 'number' ? traceEvent.ts : 0;
178
+ const eventDuration =
179
+ typeof traceEvent.dur === 'number' ? traceEvent.dur : 0;
180
+
181
+ if (eventTimestamp > 0) {
182
+ minimumTimestamp = Math.min(minimumTimestamp, eventTimestamp);
183
+ }
184
+
185
+ maximumTimestamp = Math.max(
186
+ maximumTimestamp,
187
+ eventTimestamp + eventDuration,
188
+ );
189
+ }
190
+
191
+ if (!Number.isFinite(minimumTimestamp)) {
192
+ minimumTimestamp = 0;
193
+ }
194
+
195
+ return {
196
+ windowMs:
197
+ (maximumTimestamp - minimumTimestamp) / MICROSECONDS_PER_MILLISECOND,
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Builds per-thread duration and long-task summaries.
203
+ *
204
+ * @param traceEvents - Trace events to scan.
205
+ * @param labelContext - Thread label context.
206
+ * @returns Thread summaries sorted by total duration descending.
207
+ */
208
+ function buildThreadSummaries(
209
+ traceEvents: readonly TraceEvent[],
210
+ labelContext: TraceLabelContext,
211
+ ): ThreadSummary[] {
212
+ const durationByThreadLabel = new Map<string, ThreadSummary>();
213
+
214
+ for (const traceEvent of traceEvents) {
215
+ if (traceEvent.ph !== 'X' || typeof traceEvent.dur !== 'number') {
216
+ continue;
217
+ }
218
+
219
+ const threadKey = createThreadKey(traceEvent.pid, traceEvent.tid);
220
+ if (!threadKey) {
221
+ continue;
222
+ }
223
+
224
+ const threadLabel = resolveThreadLabel(traceEvent, labelContext);
225
+ const threadSummary = durationByThreadLabel.get(threadLabel) ?? {
226
+ label: threadLabel,
227
+ totalDurationMs: 0,
228
+ runTaskDurationMs: 0,
229
+ longTaskCount16ms: 0,
230
+ longTaskCount50ms: 0,
231
+ maxTaskMs: 0,
232
+ };
233
+ const eventDurationMs = traceEvent.dur / MICROSECONDS_PER_MILLISECOND;
234
+
235
+ threadSummary.totalDurationMs += eventDurationMs;
236
+ if (traceEvent.name === RUN_TASK_EVENT_NAME) {
237
+ threadSummary.runTaskDurationMs += eventDurationMs;
238
+ if (eventDurationMs >= LONG_TASK_THRESHOLD_MS) {
239
+ threadSummary.longTaskCount16ms += 1;
240
+ }
241
+ if (eventDurationMs >= VERY_LONG_TASK_THRESHOLD_MS) {
242
+ threadSummary.longTaskCount50ms += 1;
243
+ }
244
+ threadSummary.maxTaskMs = Math.max(
245
+ threadSummary.maxTaskMs,
246
+ eventDurationMs,
247
+ );
248
+ }
249
+
250
+ durationByThreadLabel.set(threadLabel, threadSummary);
251
+ }
252
+
253
+ return Array.from(durationByThreadLabel.values()).toSorted(
254
+ (leftSummary, rightSummary) =>
255
+ rightSummary.totalDurationMs - leftSummary.totalDurationMs ||
256
+ leftSummary.label.localeCompare(rightSummary.label),
257
+ );
258
+ }
259
+
260
+ /**
261
+ * Resolves the longest complete events in the trace.
262
+ *
263
+ * @param traceEvents - Trace events to scan.
264
+ * @param labelContext - Thread label context.
265
+ * @param topCount - Maximum number of events to return.
266
+ * @returns Longest complete events sorted by duration.
267
+ */
268
+ function collectLongestEvents(
269
+ traceEvents: readonly TraceEvent[],
270
+ labelContext: TraceLabelContext,
271
+ topCount: number,
272
+ ): LongEventSummary[] {
273
+ const longEvents: LongEventSummary[] = [];
274
+
275
+ for (const traceEvent of traceEvents) {
276
+ if (traceEvent.ph !== 'X' || typeof traceEvent.dur !== 'number') {
277
+ continue;
278
+ }
279
+
280
+ const eventName = traceEvent.name;
281
+ if (typeof eventName !== 'string') {
282
+ continue;
283
+ }
284
+
285
+ const scriptUrlValue = readNestedPrimitive(traceEvent.args, [
286
+ 'data',
287
+ 'url',
288
+ ]);
289
+ longEvents.push({
290
+ durationMs: traceEvent.dur / MICROSECONDS_PER_MILLISECOND,
291
+ threadLabel: resolveThreadLabel(traceEvent, labelContext),
292
+ eventName,
293
+ scriptUrl:
294
+ typeof scriptUrlValue === 'string' ? scriptUrlValue : undefined,
295
+ });
296
+ }
297
+
298
+ return longEvents
299
+ .toSorted(
300
+ (leftEvent, rightEvent) =>
301
+ rightEvent.durationMs - leftEvent.durationMs ||
302
+ leftEvent.eventName.localeCompare(rightEvent.eventName) ||
303
+ leftEvent.threadLabel.localeCompare(rightEvent.threadLabel),
304
+ )
305
+ .slice(0, topCount);
306
+ }
307
+
308
+ /**
309
+ * Aggregates complete-event durations by event name.
310
+ *
311
+ * @param traceEvents - Trace events to scan.
312
+ * @param topCount - Maximum number of entries to return.
313
+ * @returns Event duration rollup.
314
+ */
315
+ function aggregateDurationsByName(
316
+ traceEvents: readonly TraceEvent[],
317
+ topCount: number,
318
+ ): AggregatedDuration[] {
319
+ const durationsByName = new Map<string, AggregatedDuration>();
320
+
321
+ for (const traceEvent of traceEvents) {
322
+ if (traceEvent.ph !== 'X' || typeof traceEvent.dur !== 'number') {
323
+ continue;
324
+ }
325
+
326
+ const eventName = traceEvent.name;
327
+ if (!eventName) {
328
+ continue;
329
+ }
330
+
331
+ const eventDurationMs = traceEvent.dur / MICROSECONDS_PER_MILLISECOND;
332
+ const eventSummary = durationsByName.get(eventName) ?? {
333
+ name: eventName,
334
+ count: 0,
335
+ totalMs: 0,
336
+ maxMs: 0,
337
+ };
338
+
339
+ eventSummary.count += 1;
340
+ eventSummary.totalMs += eventDurationMs;
341
+ eventSummary.maxMs = Math.max(eventSummary.maxMs, eventDurationMs);
342
+ durationsByName.set(eventName, eventSummary);
343
+ }
344
+
345
+ return Array.from(durationsByName.values())
346
+ .toSorted(
347
+ (leftSummary, rightSummary) =>
348
+ rightSummary.totalMs - leftSummary.totalMs ||
349
+ leftSummary.name.localeCompare(rightSummary.name),
350
+ )
351
+ .slice(0, topCount);
352
+ }
353
+
354
+ /**
355
+ * Aggregates FunctionCall durations by script URL.
356
+ *
357
+ * @param traceEvents - Trace events to scan.
358
+ * @param topCount - Maximum number of entries to return.
359
+ * @returns Function-call duration rollup.
360
+ */
361
+ function aggregateFunctionCalls(
362
+ traceEvents: readonly TraceEvent[],
363
+ topCount: number,
364
+ ): AggregatedDuration[] {
365
+ const durationsByFunctionCall = new Map<string, AggregatedDuration>();
366
+
367
+ for (const traceEvent of traceEvents) {
368
+ if (
369
+ traceEvent.ph !== 'X' ||
370
+ traceEvent.name !== FUNCTION_CALL_EVENT_NAME ||
371
+ typeof traceEvent.dur !== 'number'
372
+ ) {
373
+ continue;
374
+ }
375
+
376
+ const functionCallNameValue = readNestedPrimitive(traceEvent.args, [
377
+ 'data',
378
+ 'url',
379
+ ]);
380
+ const functionCallName =
381
+ typeof functionCallNameValue === 'string'
382
+ ? functionCallNameValue
383
+ : UNKNOWN_SCRIPT_LABEL;
384
+ const functionCallSummary = durationsByFunctionCall.get(
385
+ functionCallName,
386
+ ) ?? {
387
+ name: functionCallName,
388
+ count: 0,
389
+ totalMs: 0,
390
+ maxMs: 0,
391
+ };
392
+ const eventDurationMs = traceEvent.dur / MICROSECONDS_PER_MILLISECOND;
393
+
394
+ functionCallSummary.count += 1;
395
+ functionCallSummary.totalMs += eventDurationMs;
396
+ functionCallSummary.maxMs = Math.max(
397
+ functionCallSummary.maxMs,
398
+ eventDurationMs,
399
+ );
400
+ durationsByFunctionCall.set(functionCallName, functionCallSummary);
401
+ }
402
+
403
+ return Array.from(durationsByFunctionCall.values())
404
+ .toSorted(
405
+ (leftSummary, rightSummary) =>
406
+ rightSummary.totalMs - leftSummary.totalMs ||
407
+ leftSummary.name.localeCompare(rightSummary.name),
408
+ )
409
+ .slice(0, topCount);
410
+ }
411
+
412
+ /**
413
+ * Collects all durations for one named complete event.
414
+ *
415
+ * @param traceEvents - Trace events to scan.
416
+ * @param eventName - Event name to collect.
417
+ * @returns Sorted duration samples in milliseconds.
418
+ */
419
+ function collectEventDurationsMs(
420
+ traceEvents: readonly TraceEvent[],
421
+ eventName: string,
422
+ ): number[] {
423
+ const durationsMs: number[] = [];
424
+
425
+ for (const traceEvent of traceEvents) {
426
+ if (
427
+ traceEvent.ph !== 'X' ||
428
+ traceEvent.name !== eventName ||
429
+ typeof traceEvent.dur !== 'number'
430
+ ) {
431
+ continue;
432
+ }
433
+
434
+ durationsMs.push(traceEvent.dur / MICROSECONDS_PER_MILLISECOND);
435
+ }
436
+
437
+ return durationsMs.toSorted(
438
+ (leftDuration, rightDuration) => leftDuration - rightDuration,
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Counts complete or metadata events by exact name.
444
+ *
445
+ * @param traceEvents - Trace events to scan.
446
+ * @param eventName - Event name to count.
447
+ * @returns Matching event count.
448
+ */
449
+ function countNamedEvents(
450
+ traceEvents: readonly TraceEvent[],
451
+ eventName: string,
452
+ ): number {
453
+ return traceEvents.filter((traceEvent) => traceEvent.name === eventName)
454
+ .length;
455
+ }
456
+
457
+ /**
458
+ * Resolves a readable process and thread label for one trace event.
459
+ *
460
+ * @param traceEvent - Trace event needing a label.
461
+ * @param labelContext - Thread label context.
462
+ * @returns Human-readable thread label.
463
+ */
464
+ function resolveThreadLabel(
465
+ traceEvent: TraceEvent,
466
+ labelContext: TraceLabelContext,
467
+ ): string {
468
+ const processKey = createProcessKey(traceEvent.pid) ?? '';
469
+ const threadKey = createThreadKey(traceEvent.pid, traceEvent.tid) ?? '';
470
+ const processLabel =
471
+ labelContext.processNames.get(processKey) ?? UNKNOWN_PROCESS_LABEL;
472
+ const threadLabel =
473
+ labelContext.threadNames.get(threadKey) ?? UNKNOWN_THREAD_LABEL;
474
+ const workerLabel = labelContext.workerThreadLabels.get(threadKey);
475
+
476
+ return workerLabel
477
+ ? `${processLabel} / ${threadLabel} / ${workerLabel}`
478
+ : `${processLabel} / ${threadLabel}`;
479
+ }
@@ -0,0 +1,35 @@
1
+ /** Number of trace microseconds contained in one millisecond. */
2
+ export const MICROSECONDS_PER_MILLISECOND = 1_000;
3
+
4
+ /** Default item count used by the top-N rollups. */
5
+ export const DEFAULT_TOP_COUNT = 12;
6
+
7
+ /** RunTask duration threshold used for frame-budget pressure reporting. */
8
+ export const LONG_TASK_THRESHOLD_MS = 16.7;
9
+
10
+ /** Severe RunTask duration threshold used for very long task reporting. */
11
+ export const VERY_LONG_TASK_THRESHOLD_MS = 50;
12
+
13
+ /** Trace event name used by Chrome when one frame is dropped. */
14
+ export const DROPPED_FRAME_EVENT_NAME = 'DroppedFrame';
15
+
16
+ /** Trace event name used by Chrome for frame production. */
17
+ export const BEGIN_FRAME_EVENT_NAME = 'BeginFrame';
18
+
19
+ /** Trace event name used for browser task execution slices. */
20
+ export const RUN_TASK_EVENT_NAME = 'RunTask';
21
+
22
+ /** Trace event name used for script execution attribution slices. */
23
+ export const FUNCTION_CALL_EVENT_NAME = 'FunctionCall';
24
+
25
+ /** Trace event name used for animation-frame callback execution. */
26
+ export const FIRE_ANIMATION_FRAME_EVENT_NAME = 'FireAnimationFrame';
27
+
28
+ /** Fallback process label when metadata is missing. */
29
+ export const UNKNOWN_PROCESS_LABEL = 'unknown-process';
30
+
31
+ /** Fallback thread label when metadata is missing. */
32
+ export const UNKNOWN_THREAD_LABEL = 'unknown-thread';
33
+
34
+ /** Fallback function-call label when a script URL is unavailable. */
35
+ export const UNKNOWN_SCRIPT_LABEL = '(unknown-script)';