@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
@@ -0,0 +1,355 @@
1
+ /*
2
+ * Owns `docs.order.json` loading, validation, and cached lookup.
3
+ *
4
+ * Rendering code only needs a validated config view; this chapter keeps JSON
5
+ * parsing, warning messages, and cache lifecycle separate from markdown logic.
6
+ */
7
+
8
+ import fs from 'fs-extra';
9
+ import * as path from 'path';
10
+
11
+ import {
12
+ DOCS_ORDER_CONFIG_FILE_NAME,
13
+ DOCS_ORDER_SUPPORTED_KEYS,
14
+ WORKSPACE_ROOT_DIR,
15
+ } from './generate-docs.constants.js';
16
+ import type {
17
+ DirectoryDocsOrderConfig,
18
+ GenerateDocsState,
19
+ LoadedDirectoryDocsOrderConfig,
20
+ } from './generate-docs.types.js';
21
+
22
+ /**
23
+ * Loads and caches the optional per-folder docs ordering config.
24
+ *
25
+ * Phase 1 only validates and stores config so later rendering steps can reuse
26
+ * the same validated data without re-reading the file.
27
+ *
28
+ * @param state - Shared docs-generator state.
29
+ * @param directoryPath - Absolute directory path that may contain docs config.
30
+ * @returns Parsed and validated config when present.
31
+ */
32
+ export async function loadDirectoryDocsOrderConfig(
33
+ state: GenerateDocsState,
34
+ directoryPath: string,
35
+ ): Promise<LoadedDirectoryDocsOrderConfig | undefined> {
36
+ const normalizedDirectoryPath = path.resolve(directoryPath);
37
+ if (state.directoryDocsOrderConfigCache.has(normalizedDirectoryPath)) {
38
+ return state.directoryDocsOrderConfigCache.get(normalizedDirectoryPath)!;
39
+ }
40
+
41
+ const pendingConfig = readDirectoryDocsOrderConfig(
42
+ normalizedDirectoryPath,
43
+ ).then((loadedConfig) => {
44
+ state.resolvedDirectoryDocsOrderConfigCache.set(
45
+ normalizedDirectoryPath,
46
+ loadedConfig,
47
+ );
48
+ return loadedConfig;
49
+ });
50
+
51
+ state.directoryDocsOrderConfigCache.set(
52
+ normalizedDirectoryPath,
53
+ pendingConfig,
54
+ );
55
+ return pendingConfig;
56
+ }
57
+
58
+ /**
59
+ * Returns the cached docs ordering config for a directory.
60
+ *
61
+ * @param state - Shared docs-generator state.
62
+ * @param directoryPath - Absolute directory path.
63
+ * @returns Cached config when already loaded.
64
+ */
65
+ export function getCachedDirectoryDocsOrderConfig(
66
+ state: GenerateDocsState,
67
+ directoryPath: string,
68
+ ): LoadedDirectoryDocsOrderConfig | undefined {
69
+ return state.resolvedDirectoryDocsOrderConfigCache.get(
70
+ path.resolve(directoryPath),
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Writes a scoped warning for one docs ordering config file.
76
+ *
77
+ * @param configPath - Absolute config path.
78
+ * @param message - Warning message.
79
+ * @returns Nothing.
80
+ */
81
+ export function warnDirectoryDocsOrderConfig(
82
+ configPath: string,
83
+ message: string,
84
+ ): void {
85
+ const relativeConfigPath = path
86
+ .relative(WORKSPACE_ROOT_DIR, configPath)
87
+ .replace(/\\/g, '/');
88
+ console.warn(`[docs] ${relativeConfigPath}: ${message}`);
89
+ }
90
+
91
+ /**
92
+ * Reads and validates one directory's `docs.order.json` file when present.
93
+ *
94
+ * @param directoryPath - Absolute directory path.
95
+ * @returns Parsed config or undefined when absent or invalid.
96
+ */
97
+ async function readDirectoryDocsOrderConfig(
98
+ directoryPath: string,
99
+ ): Promise<LoadedDirectoryDocsOrderConfig | undefined> {
100
+ const configPath = path.join(directoryPath, DOCS_ORDER_CONFIG_FILE_NAME);
101
+ if (!(await fs.pathExists(configPath))) {
102
+ return undefined;
103
+ }
104
+
105
+ try {
106
+ const rawConfigText = await fs.readFile(configPath, 'utf8');
107
+ const parsedConfig: unknown = JSON.parse(rawConfigText);
108
+ const validatedConfig = validateDirectoryDocsOrderConfig(
109
+ parsedConfig,
110
+ configPath,
111
+ );
112
+ return validatedConfig
113
+ ? { configPath, config: validatedConfig }
114
+ : undefined;
115
+ } catch (error) {
116
+ warnDirectoryDocsOrderConfig(
117
+ configPath,
118
+ `Failed to read ${DOCS_ORDER_CONFIG_FILE_NAME}: ${getErrorMessage(error)}`,
119
+ );
120
+ return undefined;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Validates the supported phase-1 docs ordering config keys.
126
+ *
127
+ * @param parsedConfig - Raw parsed JSON value.
128
+ * @param configPath - Absolute config path used in warnings.
129
+ * @returns Sanitized config when at least one supported value is valid.
130
+ */
131
+ function validateDirectoryDocsOrderConfig(
132
+ parsedConfig: unknown,
133
+ configPath: string,
134
+ ): DirectoryDocsOrderConfig | undefined {
135
+ if (!isRecord(parsedConfig)) {
136
+ warnDirectoryDocsOrderConfig(
137
+ configPath,
138
+ 'Config must be a JSON object. Ignoring file.',
139
+ );
140
+ return undefined;
141
+ }
142
+
143
+ for (const configKey of Object.keys(parsedConfig)) {
144
+ if (!DOCS_ORDER_SUPPORTED_KEYS.includes(configKey as never)) {
145
+ warnDirectoryDocsOrderConfig(
146
+ configPath,
147
+ `Unknown key "${configKey}". Supported keys: ${DOCS_ORDER_SUPPORTED_KEYS.join(', ')}`,
148
+ );
149
+ }
150
+ }
151
+
152
+ const validatedConfig: DirectoryDocsOrderConfig = {};
153
+ const introFile = normalizeOptionalStringConfigValue(
154
+ parsedConfig.introFile,
155
+ 'introFile',
156
+ configPath,
157
+ );
158
+ if (introFile) {
159
+ validatedConfig.introFile = introFile;
160
+ }
161
+
162
+ const fileOrder = normalizeStringArrayConfigValue(
163
+ parsedConfig.fileOrder,
164
+ 'fileOrder',
165
+ configPath,
166
+ );
167
+ if (fileOrder) {
168
+ validatedConfig.fileOrder = fileOrder;
169
+ }
170
+
171
+ const symbolOrder = normalizeSymbolOrderConfigValue(
172
+ parsedConfig.symbolOrder,
173
+ configPath,
174
+ );
175
+ if (symbolOrder) {
176
+ validatedConfig.symbolOrder = symbolOrder;
177
+ }
178
+
179
+ const folderOrder = normalizeStringArrayConfigValue(
180
+ parsedConfig.folderOrder,
181
+ 'folderOrder',
182
+ configPath,
183
+ );
184
+ if (folderOrder) {
185
+ validatedConfig.folderOrder = folderOrder;
186
+ }
187
+
188
+ const hiddenFiles = normalizeStringArrayConfigValue(
189
+ parsedConfig.hiddenFiles,
190
+ 'hiddenFiles',
191
+ configPath,
192
+ );
193
+ if (hiddenFiles) {
194
+ validatedConfig.hiddenFiles = hiddenFiles;
195
+ }
196
+
197
+ const hiddenSymbols = normalizeStringArrayConfigValue(
198
+ parsedConfig.hiddenSymbols,
199
+ 'hiddenSymbols',
200
+ configPath,
201
+ );
202
+ if (hiddenSymbols) {
203
+ validatedConfig.hiddenSymbols = hiddenSymbols;
204
+ }
205
+
206
+ return Object.keys(validatedConfig).length > 0 ? validatedConfig : undefined;
207
+ }
208
+
209
+ /**
210
+ * Normalizes a single optional string config field.
211
+ *
212
+ * @param value - Raw config value.
213
+ * @param fieldName - Config field name.
214
+ * @param configPath - Absolute config path used in warnings.
215
+ * @returns Trimmed string when valid.
216
+ */
217
+ function normalizeOptionalStringConfigValue(
218
+ value: unknown,
219
+ fieldName: string,
220
+ configPath: string,
221
+ ): string | undefined {
222
+ if (value === undefined) {
223
+ return undefined;
224
+ }
225
+
226
+ if (typeof value !== 'string' || value.trim().length === 0) {
227
+ warnDirectoryDocsOrderConfig(
228
+ configPath,
229
+ `Field "${fieldName}" must be a non-empty string. Ignoring value.`,
230
+ );
231
+ return undefined;
232
+ }
233
+
234
+ return value.trim();
235
+ }
236
+
237
+ /**
238
+ * Normalizes one string-array config field.
239
+ *
240
+ * @param value - Raw config value.
241
+ * @param fieldName - Config field name.
242
+ * @param configPath - Absolute config path used in warnings.
243
+ * @returns Unique non-empty strings when valid.
244
+ */
245
+ function normalizeStringArrayConfigValue(
246
+ value: unknown,
247
+ fieldName: string,
248
+ configPath: string,
249
+ ): string[] | undefined {
250
+ if (value === undefined) {
251
+ return undefined;
252
+ }
253
+
254
+ if (!Array.isArray(value)) {
255
+ warnDirectoryDocsOrderConfig(
256
+ configPath,
257
+ `Field "${fieldName}" must be an array of non-empty strings. Ignoring value.`,
258
+ );
259
+ return undefined;
260
+ }
261
+
262
+ const normalizedEntries = value
263
+ .filter((entry): entry is string => typeof entry === 'string')
264
+ .map((entry) => entry.trim())
265
+ .filter((entry) => entry.length > 0);
266
+
267
+ if (normalizedEntries.length !== value.length) {
268
+ warnDirectoryDocsOrderConfig(
269
+ configPath,
270
+ `Field "${fieldName}" must contain only non-empty strings. Dropping invalid entries.`,
271
+ );
272
+ }
273
+
274
+ return normalizedEntries.length > 0
275
+ ? [...new Set(normalizedEntries)]
276
+ : undefined;
277
+ }
278
+
279
+ /**
280
+ * Normalizes the per-file symbol ordering map.
281
+ *
282
+ * @param value - Raw config value.
283
+ * @param configPath - Absolute config path used in warnings.
284
+ * @returns Sanitized symbol-order map when valid.
285
+ */
286
+ function normalizeSymbolOrderConfigValue(
287
+ value: unknown,
288
+ configPath: string,
289
+ ): Record<string, string[]> | undefined {
290
+ if (value === undefined) {
291
+ return undefined;
292
+ }
293
+
294
+ if (!isRecord(value)) {
295
+ warnDirectoryDocsOrderConfig(
296
+ configPath,
297
+ 'Field "symbolOrder" must be an object keyed by file name. Ignoring value.',
298
+ );
299
+ return undefined;
300
+ }
301
+
302
+ const normalizedSymbolOrderEntries = Object.entries(value)
303
+ .map(([fileName, symbolNames]) => {
304
+ const normalizedFileName = fileName.trim();
305
+ if (normalizedFileName.length === 0) {
306
+ warnDirectoryDocsOrderConfig(
307
+ configPath,
308
+ 'Field "symbolOrder" contains an empty file key. Dropping entry.',
309
+ );
310
+ return undefined;
311
+ }
312
+
313
+ const normalizedSymbols = normalizeStringArrayConfigValue(
314
+ symbolNames,
315
+ `symbolOrder.${normalizedFileName}`,
316
+ configPath,
317
+ );
318
+ if (!normalizedSymbols) {
319
+ return undefined;
320
+ }
321
+
322
+ return [normalizedFileName, normalizedSymbols] as const;
323
+ })
324
+ .filter(
325
+ (entry): entry is readonly [string, string[]] => entry !== undefined,
326
+ );
327
+
328
+ return normalizedSymbolOrderEntries.length > 0
329
+ ? Object.fromEntries(normalizedSymbolOrderEntries)
330
+ : undefined;
331
+ }
332
+
333
+ /**
334
+ * Narrows unknown values to simple object records.
335
+ *
336
+ * @param value - Value to inspect.
337
+ * @returns True when the value is a plain record-like object.
338
+ */
339
+ function isRecord(value: unknown): value is Record<string, unknown> {
340
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
341
+ }
342
+
343
+ /**
344
+ * Extracts a readable message from unknown thrown values.
345
+ *
346
+ * @param error - Thrown value.
347
+ * @returns Human-readable error text.
348
+ */
349
+ function getErrorMessage(error: unknown): string {
350
+ if (error instanceof Error) {
351
+ return error.message;
352
+ }
353
+
354
+ return String(error);
355
+ }
@@ -0,0 +1,31 @@
1
+ /*
2
+ * Creates the shared runtime state for one docs-generation process.
3
+ *
4
+ * The generator intentionally reuses one ts-morph project and one docs-order
5
+ * cache set so each chapter can stay pure about its responsibility while the
6
+ * process still shares expensive state safely.
7
+ */
8
+
9
+ import { Project } from 'ts-morph';
10
+
11
+ import type { GenerateDocsState } from './generate-docs.types.js';
12
+
13
+ /**
14
+ * Creates the mutable runtime state shared across one docs-generation run.
15
+ *
16
+ * The generator intentionally keeps one ts-morph project and one pair of
17
+ * docs-order caches for the full process so each target run can reuse the same
18
+ * lookup behavior without relying on module-level globals.
19
+ *
20
+ * @returns Shared docs-generator state.
21
+ */
22
+ export function createGenerateDocsState(): GenerateDocsState {
23
+ return {
24
+ project: new Project({
25
+ tsConfigFilePath: 'tsconfig.json',
26
+ skipAddingFilesFromTsConfig: true,
27
+ }),
28
+ resolvedDirectoryDocsOrderConfigCache: new Map(),
29
+ directoryDocsOrderConfigCache: new Map(),
30
+ };
31
+ }
@@ -0,0 +1,165 @@
1
+ /*
2
+ * Owns CLI target selection and source-tree preparation for one docs run.
3
+ *
4
+ * This chapter decides what tree is being documented and which source files are
5
+ * eligible before any ts-morph symbol work or markdown rendering begins.
6
+ */
7
+
8
+ import fg from 'fast-glob';
9
+ import fs from 'fs-extra';
10
+ import * as path from 'path';
11
+ import type { SourceFile } from 'ts-morph';
12
+
13
+ import {
14
+ DEFAULT_DOCS_TARGET,
15
+ DOCS_TARGETS,
16
+ SOURCE_FILE_GLOBS,
17
+ SOURCE_FILE_IGNORE_GLOBS,
18
+ } from './generate-docs.constants.js';
19
+ import type {
20
+ DocsTargetConfig,
21
+ GenerateDocsState,
22
+ } from './generate-docs.types.js';
23
+
24
+ /**
25
+ * Resolves the active docs target from CLI or environment input.
26
+ *
27
+ * Lookup order:
28
+ * 1. `--target=...`
29
+ * 2. `DOCS_TARGET`
30
+ * 3. default target
31
+ *
32
+ * @param rawArguments - CLI arguments passed after the executable path.
33
+ * @returns Concrete docs target configuration.
34
+ */
35
+ export function resolveDocsTarget(
36
+ rawArguments: readonly string[],
37
+ ): DocsTargetConfig {
38
+ const cliTarget = rawArguments
39
+ .find((argument) => argument.startsWith('--target='))
40
+ ?.slice('--target='.length);
41
+ const requestedTarget =
42
+ cliTarget ?? process.env.DOCS_TARGET ?? DEFAULT_DOCS_TARGET;
43
+ const resolvedTarget = DOCS_TARGETS[requestedTarget];
44
+
45
+ if (!resolvedTarget) {
46
+ const supportedTargets = Object.keys(DOCS_TARGETS).toSorted().join(', ');
47
+ throw new Error(
48
+ `Unknown docs target "${requestedTarget}". Supported targets: ${supportedTargets}`,
49
+ );
50
+ }
51
+
52
+ return resolvedTarget;
53
+ }
54
+
55
+ /**
56
+ * Prepares the output tree for the selected target.
57
+ *
58
+ * @param target - Target configuration.
59
+ * @returns Nothing.
60
+ */
61
+ export async function initializeDocsTarget(
62
+ target: DocsTargetConfig,
63
+ ): Promise<void> {
64
+ await cleanupPublishedRoot(target);
65
+ await fs.ensureDir(target.docsDir);
66
+
67
+ if (
68
+ !target.rootReadmeSource ||
69
+ !target.rootReadmeDestination ||
70
+ !(await fs.pathExists(target.rootReadmeSource))
71
+ ) {
72
+ return;
73
+ }
74
+
75
+ await fs.ensureDir(path.dirname(target.rootReadmeDestination));
76
+ await fs.copyFile(target.rootReadmeSource, target.rootReadmeDestination);
77
+ }
78
+
79
+ /**
80
+ * Loads target source files into the shared ts-morph project.
81
+ *
82
+ * @param state - Shared docs-generator state.
83
+ * @param target - Target configuration.
84
+ * @returns Filtered source files that belong to the target tree.
85
+ */
86
+ export async function loadTargetSourceFiles(
87
+ state: GenerateDocsState,
88
+ target: DocsTargetConfig,
89
+ ): Promise<SourceFile[]> {
90
+ const filePaths = await fg(SOURCE_FILE_GLOBS, {
91
+ cwd: target.sourceDir,
92
+ absolute: true,
93
+ ignore: SOURCE_FILE_IGNORE_GLOBS,
94
+ });
95
+
96
+ for (const filePath of filePaths) {
97
+ if (!shouldIncludeSourceFile(filePath, target)) {
98
+ continue;
99
+ }
100
+
101
+ state.project.addSourceFileAtPath(filePath);
102
+ }
103
+
104
+ const rawSourceFiles = state.project.getSourceFiles();
105
+ const sourceFiles = rawSourceFiles.filter((sourceFile) => {
106
+ const filePath = sourceFile.getFilePath();
107
+ return !filePath.endsWith('.d.ts') && !/node_modules/.test(filePath);
108
+ });
109
+
110
+ console.log(
111
+ `[docs:${target.name}] Loaded ${sourceFiles.length} source files (raw: ${rawSourceFiles.length})`,
112
+ );
113
+
114
+ return sourceFiles;
115
+ }
116
+
117
+ /**
118
+ * Determines whether a discovered file should participate in docs generation.
119
+ *
120
+ * @param filePath - Absolute source file path.
121
+ * @param target - Target configuration.
122
+ * @returns True when the file belongs in the target set.
123
+ */
124
+ function shouldIncludeSourceFile(
125
+ filePath: string,
126
+ target: DocsTargetConfig,
127
+ ): boolean {
128
+ if (/\.test\.ts$/i.test(filePath)) {
129
+ return false;
130
+ }
131
+
132
+ if (!target.excludeRootSourceFiles) {
133
+ return true;
134
+ }
135
+
136
+ const relativeDirectory = path.dirname(
137
+ path.relative(target.sourceDir, filePath),
138
+ );
139
+ return relativeDirectory !== '' && relativeDirectory !== '.';
140
+ }
141
+
142
+ /**
143
+ * Removes previously published generated files for targets that mirror output
144
+ * into a browsable published directory.
145
+ *
146
+ * @param target - Target configuration.
147
+ * @returns Nothing.
148
+ */
149
+ async function cleanupPublishedRoot(target: DocsTargetConfig): Promise<void> {
150
+ if (!target.publishedRootDir) {
151
+ return;
152
+ }
153
+
154
+ await fs.ensureDir(target.publishedRootDir);
155
+ const preservedEntries = new Set(target.preservePublishedEntries ?? []);
156
+ const childEntries = await fs.readdir(target.publishedRootDir);
157
+
158
+ for (const childEntry of childEntries) {
159
+ if (preservedEntries.has(childEntry)) {
160
+ continue;
161
+ }
162
+
163
+ await fs.remove(path.join(target.publishedRootDir, childEntry));
164
+ }
165
+ }
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Generates per-folder README.md aggregating exported symbols' JSDoc.
3
+ * - Copies root README.md into docs/ (manual content retained)
4
+ * - Skips generating README for src root (leave top-level README manual)
5
+ * Usage: npm run docs:folders
6
+ *
7
+ * The folder split keeps this file orchestration-only: target resolution,
8
+ * source loading, symbol normalization, and markdown emission each live in a
9
+ * dedicated chapter file below.
10
+ */
11
+ import {
12
+ emitDirectoryDocs,
13
+ emitFolderIndex,
14
+ } from './output/generate-docs.output.js';
15
+ import { createGenerateDocsState } from './generate-docs.state.js';
16
+ import {
17
+ collectDirectorySymbols,
18
+ dedupeDirectorySymbols,
19
+ } from './symbols/generate-docs.symbols.js';
20
+ import {
21
+ initializeDocsTarget,
22
+ loadTargetSourceFiles,
23
+ resolveDocsTarget,
24
+ } from './generate-docs.targets.js';
25
+
26
+ /**
27
+ * Generates docs for the requested target.
28
+ *
29
+ * The script runs in four passes:
30
+ * 1. Resolve and prepare the target output tree.
31
+ * 2. Load source files into the shared ts-morph project.
32
+ * 3. Collect and normalize rendered symbols.
33
+ * 4. Emit directory README files and the optional folder index.
34
+ *
35
+ * @returns Nothing.
36
+ */
37
+ async function main(): Promise<void> {
38
+ const state = createGenerateDocsState();
39
+
40
+ // Step 1: Resolve and prepare the requested target.
41
+ const target = resolveDocsTarget(process.argv.slice(2));
42
+ await initializeDocsTarget(target);
43
+
44
+ // Step 2: Load the target source files.
45
+ const sourceFiles = await loadTargetSourceFiles(state, target);
46
+
47
+ // Step 3: Collect and normalize rendered symbols.
48
+ const directorySymbolMap = collectDirectorySymbols(sourceFiles);
49
+ dedupeDirectorySymbols(directorySymbolMap);
50
+
51
+ // Step 4: Emit README files and the optional folder index.
52
+ await emitDirectoryDocs(state, directorySymbolMap, target);
53
+ if (target.includeFolderIndex) {
54
+ await emitFolderIndex(state, directorySymbolMap, target);
55
+ }
56
+
57
+ console.log(`[docs:${target.name}] Per-folder README generation complete.`);
58
+ }
59
+
60
+ main().catch((error: unknown) => {
61
+ console.error(error);
62
+ process.exit(1);
63
+ });