@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (343) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +141 -0
  3. package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
  4. package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
  5. package/assets/editor-extensions/typecad-debug/package.json +76 -0
  6. package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
  7. package/assets/editor-extensions/typecad-ui/README.md +52 -0
  8. package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
  9. package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
  10. package/assets/editor-extensions/typecad-ui/icon.png +0 -0
  11. package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
  12. package/assets/editor-extensions/typecad-ui/package.json +54 -0
  13. package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
  14. package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
  15. package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
  16. package/dist/api/config.d.ts +30 -27
  17. package/dist/api/schema/types.d.ts +20 -3
  18. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  19. package/dist/api/shared/async-runtime-static.js +5 -5
  20. package/dist/api/shared/async-types.d.ts +3 -2
  21. package/dist/api/shared/board-catalog.d.ts +2 -0
  22. package/dist/api/shared/board-catalog.js +6 -0
  23. package/dist/api/shared/board-resolver.d.ts +4 -7
  24. package/dist/api/shared/board-resolver.js +2 -1
  25. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  26. package/dist/api/shared/coop-scheduler.js +1 -1
  27. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  28. package/dist/api/shared/cpp-type-ir.js +1 -1
  29. package/dist/api/shared/display-profile.d.ts +1 -1
  30. package/dist/api/shared/display-profile.js +3 -5
  31. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  32. package/dist/api/shared/framework-manifest-registry.js +0 -1
  33. package/dist/api/shared/framework-manifest.d.ts +65 -507
  34. package/dist/api/shared/framework-manifest.js +6 -11
  35. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  36. package/dist/api/shared/hal-op-ir.js +31 -65
  37. package/dist/api/shared/index.d.ts +0 -4
  38. package/dist/api/shared/index.js +1 -7
  39. package/dist/api/shared/ir-core.d.ts +6 -0
  40. package/dist/api/shared/ir-declarations.d.ts +4 -0
  41. package/dist/api/shared/ir.d.ts +1 -3
  42. package/dist/api/shared/platform-strategy.d.ts +14 -37
  43. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  44. package/dist/api/shared/polyfill-types.js +0 -27
  45. package/dist/api/shared/promise-runtime.js +3 -3
  46. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  47. package/dist/api/shared/spdx-licenses.js +1 -1
  48. package/dist/api/shared/toolchain-types.d.ts +1 -1
  49. package/dist/api/shared/toolchain-types.js +3 -3
  50. package/dist/api/shared/types.d.ts +0 -3
  51. package/dist/api/shared/validate-framework-manifest.js +13 -33
  52. package/dist/board-catalog/dts-reader.d.ts +220 -0
  53. package/dist/board-catalog/dts-reader.js +1052 -0
  54. package/dist/board-catalog/index.d.ts +11 -0
  55. package/dist/board-catalog/index.js +12 -0
  56. package/dist/board-catalog/pinconfig.d.ts +48 -0
  57. package/dist/board-catalog/pinconfig.js +263 -0
  58. package/dist/board-catalog/sdk.d.ts +82 -0
  59. package/dist/board-catalog/sdk.js +180 -0
  60. package/dist/board-catalog/store.d.ts +196 -0
  61. package/dist/board-catalog/store.js +363 -0
  62. package/dist/board-catalog/types.d.ts +233 -0
  63. package/dist/board-catalog/types.js +7 -0
  64. package/dist/board-catalog/walker.d.ts +112 -0
  65. package/dist/board-catalog/walker.js +1101 -0
  66. package/dist/cli-utils.d.ts +2 -2
  67. package/dist/cli-utils.js +5 -5
  68. package/dist/cli.js +227 -244
  69. package/dist/config-loader.d.ts +14 -19
  70. package/dist/config-loader.js +242 -78
  71. package/dist/config-schema.d.ts +19 -227
  72. package/dist/config-schema.js +9 -8
  73. package/dist/contract/board-generator.d.ts +4 -4
  74. package/dist/contract/board-generator.js +16 -5
  75. package/dist/contract/contract-parser.d.ts +36 -127
  76. package/dist/contract/contract-parser.js +76 -0
  77. package/dist/contract/index.d.ts +1 -1
  78. package/dist/contract/index.js +70 -21
  79. package/dist/create/active-board-catalog.d.ts +9 -0
  80. package/dist/create/active-board-catalog.js +94 -0
  81. package/dist/create/board-catalog.generated.d.ts +96 -0
  82. package/dist/create/board-catalog.generated.js +128737 -0
  83. package/dist/create/board-search.d.ts +23 -0
  84. package/dist/create/board-search.js +88 -0
  85. package/dist/create/editor-integration.d.ts +32 -0
  86. package/dist/create/editor-integration.js +257 -0
  87. package/dist/create/eslint-rules-template.d.ts +2 -2
  88. package/dist/create/framework-catalog.d.ts +48 -8
  89. package/dist/create/framework-catalog.js +58 -44
  90. package/dist/create/index.d.ts +8 -13
  91. package/dist/create/index.js +5 -9
  92. package/dist/create/mcu-target.d.ts +39 -0
  93. package/dist/create/mcu-target.js +80 -0
  94. package/dist/create/pack-targets.d.ts +19 -0
  95. package/dist/create/pack-targets.js +56 -0
  96. package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
  97. package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
  98. package/dist/create/templates.d.ts +51 -0
  99. package/dist/create/{init-templates.js → templates.js} +135 -116
  100. package/dist/create/wizard.d.ts +19 -0
  101. package/dist/create/wizard.js +314 -0
  102. package/dist/debug/preprocessor.d.ts +5 -0
  103. package/dist/debug/preprocessor.js +31 -7
  104. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  105. package/dist/diagnostics/diagnostics-report.js +3 -11
  106. package/dist/diagnostics/json-schema.d.ts +1 -1
  107. package/dist/diagnostics/md-writer.js +2 -2
  108. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  109. package/dist/diagnostics/mermaid-builder.js +1 -1
  110. package/dist/emit/cpp-emitter.js +4 -1
  111. package/dist/emit/emitters/class-emitter.js +32 -4
  112. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  113. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  114. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  115. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  116. package/dist/emit/emitters/output-finalizer.js +31 -23
  117. package/dist/emit/emitters/setup.js +147 -63
  118. package/dist/emit/emitters/top-level-prep.js +1 -1
  119. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  120. package/dist/emit/emitters/ui-emitter.js +1 -1
  121. package/dist/emit/expression-renderer.d.ts +3 -3
  122. package/dist/emit/expression-renderer.js +17 -7
  123. package/dist/emit/route-hal-op.js +17 -48
  124. package/dist/emit/snprintf-helpers.d.ts +1 -3
  125. package/dist/emit/snprintf-helpers.js +1 -39
  126. package/dist/emit/statement-renderer.d.ts +0 -4
  127. package/dist/emit/statement-renderer.js +54 -45
  128. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  129. package/dist/emit/utils/async-state-machine.js +56 -54
  130. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  131. package/dist/emit/utils/comment-helpers.js +1 -1
  132. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  133. package/dist/emit/utils/cpp-helpers.js +2 -2
  134. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  135. package/dist/emit/utils/index.d.ts +3 -3
  136. package/dist/emit/utils/index.js +3 -3
  137. package/dist/emit/utils/type-inference.d.ts +1 -29
  138. package/dist/emit/utils/type-inference.js +0 -75
  139. package/dist/framework-package.d.ts +3 -0
  140. package/dist/framework-package.js +23 -6
  141. package/dist/framework-registry.d.ts +5 -5
  142. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  143. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  144. package/dist/frameworks/native/index.d.ts +3 -0
  145. package/dist/frameworks/native/index.js +15 -0
  146. package/dist/frameworks/native/native-compile.d.ts +17 -0
  147. package/dist/frameworks/native/native-compile.js +170 -0
  148. package/dist/frameworks/native/native-config.d.ts +41 -0
  149. package/dist/frameworks/native/native-config.js +7 -0
  150. package/dist/frameworks/native/strategy.d.ts +97 -0
  151. package/dist/frameworks/native/strategy.js +664 -0
  152. package/dist/ir/board-resolver.d.ts +18 -23
  153. package/dist/ir/board-resolver.js +43 -401
  154. package/dist/ir/build-ir-state.d.ts +3 -0
  155. package/dist/ir/build-ir-state.js +42 -0
  156. package/dist/ir/build-ir.d.ts +1 -1
  157. package/dist/ir/build-ir.js +64 -45
  158. package/dist/ir/declaration-builders.js +1 -0
  159. package/dist/ir/expression-to-ir.js +17 -10
  160. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  161. package/dist/ir/hal/hal-emitter.js +92 -41
  162. package/dist/ir/hal/hal-parser.d.ts +5 -5
  163. package/dist/ir/hal/hal-parser.js +128 -36
  164. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  165. package/dist/ir/hal/hal-plugins.js +425 -716
  166. package/dist/ir/heap-analysis.js +2 -41
  167. package/dist/ir/identifier-collector.js +32 -20
  168. package/dist/ir/network-validation.js +7 -11
  169. package/dist/ir/ownership-analysis.js +75 -13
  170. package/dist/ir/peripheral-usage.d.ts +8 -2
  171. package/dist/ir/peripheral-usage.js +78 -149
  172. package/dist/ir/peripheral-validation.js +23 -3
  173. package/dist/ir/pin-capability-validation.d.ts +12 -0
  174. package/dist/ir/pin-capability-validation.js +61 -97
  175. package/dist/ir/pin-mode-validation.js +5 -5
  176. package/dist/ir/program-analysis.d.ts +8 -15
  177. package/dist/ir/program-analysis.js +140 -323
  178. package/dist/ir/pulldown-validation.js +11 -4
  179. package/dist/ir/resource-analysis.js +15 -3
  180. package/dist/ir/statement-to-ir.js +0 -6
  181. package/dist/ir/timing-validation.js +1 -1
  182. package/dist/ir/transformers/array-methods.js +11 -5
  183. package/dist/ir/transformers/call-statement.js +20 -87
  184. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  185. package/dist/ir/transformers/callback-context-registry.js +8 -16
  186. package/dist/ir/transformers/control-flow.js +0 -13
  187. package/dist/ir/transformers/expressions.js +10 -9
  188. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  189. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  190. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  191. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  192. package/dist/ir/transformers/namespace-methods.js +1 -1
  193. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  194. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  195. package/dist/ir/transformers/variables.js +338 -42
  196. package/dist/ir/try-catch-validation.js +4 -3
  197. package/dist/ir/type-resolution.js +15 -4
  198. package/dist/ir/validation-orchestrator.js +0 -9
  199. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  200. package/dist/libdef/cpp-to-decl.js +0 -72
  201. package/dist/libdef/registry.d.ts +0 -1
  202. package/dist/libdef/registry.js +1 -24
  203. package/dist/library/catalog.d.ts +35 -0
  204. package/dist/library/catalog.js +67 -0
  205. package/dist/library/cli.d.ts +2 -0
  206. package/dist/library/cli.js +168 -0
  207. package/dist/library/init.d.ts +25 -0
  208. package/dist/library/init.js +397 -0
  209. package/dist/library/install.d.ts +9 -0
  210. package/dist/library/install.js +80 -0
  211. package/dist/library/registry-search.d.ts +34 -0
  212. package/dist/library/registry-search.js +50 -0
  213. package/dist/library/validate.d.ts +17 -0
  214. package/dist/library/validate.js +175 -0
  215. package/dist/library-packages.d.ts +95 -0
  216. package/dist/library-packages.js +275 -0
  217. package/dist/mapping/peripheral-names.js +9 -4
  218. package/dist/mapping/source-map.d.ts +3 -4
  219. package/dist/mapping/source-map.js +12 -18
  220. package/dist/orchestrator/graph-builder.d.ts +3 -3
  221. package/dist/orchestrator/graph-builder.js +29 -12
  222. package/dist/orchestrator/type-checker.d.ts +3 -3
  223. package/dist/orchestrator/type-checker.js +3 -3
  224. package/dist/platform/async-runtime.js +1 -1
  225. package/dist/platform/coop-scheduler-runtime.js +2 -2
  226. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  227. package/dist/platform/generic-debug-codegen.js +185 -0
  228. package/dist/platform/generic-strategy.d.ts +3 -4
  229. package/dist/platform/generic-strategy.js +5 -80
  230. package/dist/platform/index.js +1 -1
  231. package/dist/platform/registry.d.ts +2 -2
  232. package/dist/platform/registry.js +2 -2
  233. package/dist/preview/client.js +17 -3
  234. package/dist/safety/asil-decorators.d.ts +32 -0
  235. package/dist/safety/asil-decorators.js +32 -0
  236. package/dist/safety/authoring.d.ts +85 -0
  237. package/dist/safety/authoring.js +61 -0
  238. package/dist/safety/engine.d.ts +3 -0
  239. package/dist/safety/engine.js +83 -0
  240. package/dist/safety/hal/ops.d.ts +48 -0
  241. package/dist/safety/hal/ops.js +46 -0
  242. package/dist/safety/iso26262/analyze.d.ts +13 -0
  243. package/dist/safety/iso26262/analyze.js +29 -0
  244. package/dist/safety/iso26262/asil.d.ts +10 -0
  245. package/dist/safety/iso26262/asil.js +22 -0
  246. package/dist/safety/iso26262/collect.d.ts +14 -0
  247. package/dist/safety/iso26262/collect.js +137 -0
  248. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  249. package/dist/safety/iso26262/goto-checker.js +72 -0
  250. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  251. package/dist/safety/iso26262/heap-checker.js +57 -0
  252. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  253. package/dist/safety/iso26262/init-checker.js +127 -0
  254. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  255. package/dist/safety/iso26262/loop-checker.js +86 -0
  256. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  257. package/dist/safety/iso26262/recursion-checker.js +134 -0
  258. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  259. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  260. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  261. package/dist/safety/passes/pinMode-intercept.js +60 -0
  262. package/dist/safety/runtime/mode-table.d.ts +5 -0
  263. package/dist/safety/runtime/mode-table.js +55 -0
  264. package/dist/safety/runtime/polyfills.d.ts +6 -0
  265. package/dist/safety/runtime/polyfills.js +12 -0
  266. package/dist/safety/runtime/result-struct.d.ts +11 -0
  267. package/dist/safety/runtime/result-struct.js +98 -0
  268. package/dist/safety/runtime/safe-int.d.ts +33 -0
  269. package/dist/safety/runtime/safe-int.js +170 -0
  270. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  271. package/dist/safety/runtime/safe-traits.js +95 -0
  272. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  273. package/dist/safety/runtime/safe-variable.js +226 -0
  274. package/dist/safety/runtime/vote.d.ts +8 -0
  275. package/dist/safety/runtime/vote.js +92 -0
  276. package/dist/safety/runtime/write-verify.d.ts +8 -0
  277. package/dist/safety/runtime/write-verify.js +71 -0
  278. package/dist/safety/safe-int-types.d.ts +45 -0
  279. package/dist/safety/safe-int-types.js +25 -0
  280. package/dist/safety/safe-variable-types.d.ts +29 -0
  281. package/dist/safety/safe-variable-types.js +19 -0
  282. package/dist/safety/safety-bridge.d.ts +5 -6
  283. package/dist/safety/safety-bridge.js +15 -28
  284. package/dist/safety/sidecar-bridge.d.ts +1 -3
  285. package/dist/safety/sidecar-bridge.js +3 -20
  286. package/dist/safety/specifiers.d.ts +6 -0
  287. package/dist/safety/specifiers.js +14 -0
  288. package/dist/safety-hook.d.ts +8 -7
  289. package/dist/safety-hook.js +12 -13
  290. package/dist/testing.d.ts +5 -6
  291. package/dist/testing.js +5 -5
  292. package/dist/transpile/resolution.d.ts +1 -1
  293. package/dist/transpile/resolution.js +18 -9
  294. package/dist/transpile/test-pins.d.ts +18 -0
  295. package/dist/transpile/test-pins.js +142 -0
  296. package/dist/transpile.d.ts +1 -2
  297. package/dist/transpile.js +90 -29
  298. package/dist/types.d.ts +45 -26
  299. package/dist/ui-hook.d.ts +1 -1
  300. package/dist/utils/cli.d.ts +2 -2
  301. package/dist/utils/cli.js +160 -157
  302. package/dist/utils/fs.d.ts +3 -4
  303. package/dist/utils/fs.js +4 -5
  304. package/dist/utils/strings.d.ts +0 -1
  305. package/dist/utils/strings.js +0 -8
  306. package/dist/utils/ui.d.ts +5 -2
  307. package/dist/utils/ui.js +13 -3
  308. package/package.json +158 -149
  309. package/dist/add-preset.d.ts +0 -4
  310. package/dist/add-preset.js +0 -74
  311. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  312. package/dist/api/shared/async-symbol-detector.js +0 -140
  313. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  314. package/dist/api/shared/native-display-op-resolver.js +0 -64
  315. package/dist/create/board-checklist.d.ts +0 -2
  316. package/dist/create/board-checklist.js +0 -52
  317. package/dist/create/board-codegen.d.ts +0 -9
  318. package/dist/create/board-codegen.js +0 -238
  319. package/dist/create/board-generators.d.ts +0 -12
  320. package/dist/create/board-generators.js +0 -651
  321. package/dist/create/board-spec.d.ts +0 -637
  322. package/dist/create/board-spec.js +0 -214
  323. package/dist/create/init-templates.d.ts +0 -28
  324. package/dist/create/init-wizard.d.ts +0 -8
  325. package/dist/create/init-wizard.js +0 -182
  326. package/dist/install/framework-catalog.d.ts +0 -53
  327. package/dist/install/framework-catalog.js +0 -107
  328. package/dist/install/handle-install.d.ts +0 -35
  329. package/dist/install/handle-install.js +0 -177
  330. package/dist/install/index.d.ts +0 -4
  331. package/dist/install/index.js +0 -3
  332. package/dist/ir/pin-state-tracking.d.ts +0 -58
  333. package/dist/ir/pin-state-tracking.js +0 -182
  334. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  335. package/dist/ir/pwm-timer-sharing.js +0 -94
  336. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  337. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  338. package/dist/libdef/component-decls.d.ts +0 -2
  339. package/dist/libdef/component-decls.js +0 -6
  340. package/dist/libdef/component-discovery.d.ts +0 -43
  341. package/dist/libdef/component-discovery.js +0 -83
  342. package/dist/theme-tokens.d.ts +0 -22
  343. package/dist/theme-tokens.js +0 -172
@@ -0,0 +1,196 @@
1
+ import type { BoardDataEntry } from './types.js';
2
+ /** Extraction-logic revision. Bump whenever walker/dts-reader change what
3
+ * they extract — an overlay written by an older revision is stale even
4
+ * when the tree itself has not moved. History lives with the walker.
5
+ * 18: boardgen maps EVERY declared ADC controller's routes (channels carry
6
+ * their owning controller; primary stays implicit).
7
+ * 19: boardgen emits the per-family channel-setup pair (zephyr.adc.gain /
8
+ * zephyr.adc.reference — STM32 requires ADC_GAIN_1 + ADC_REF_INTERNAL).
9
+ * 20: the board module re-exports the thin ADC class under its new short
10
+ * name (ADCChannel → ADC, matching GPIO/PWM/UART).
11
+ * 21: DAC joins the short-name convention (DACChannel → DAC); nRF boards
12
+ * synthesize SAADC channels from the SoC family's AIN pad map.
13
+ * 22: revision-qualified variant yamls resolve their .dts through the
14
+ * board's shared base (nrf9160dk's *_0_14_0.yaml → nrf9160dk_nrf9160.dts)
15
+ * instead of dropping — whole boards left the catalog silently.
16
+ * 23: three new silicon sources — RP2 header matrices (ADC_CH/PWM_*
17
+ * macros), Atmel pinconfigs YAMLs (adc routes), connector io-channel-map
18
+ * wiring — plus per-channel ADC pinmux macro tokens.
19
+ * 24: the remaining STM32 pinctrl spellings (H7 adc*_inp*, digitless
20
+ * single-unit adc_in*) + labeled PWM controller nodes (nRF psel matrix).
21
+ * 25: four more pinctrl grammars — NXP Kinetis (FTM/ADC16), LPC CTIMER,
22
+ * i.MX RT (flexpwm/adc node names + in-band pad joins), GigaDevice — plus
23
+ * module include-dir dts roots and board-dir overlay io-channel maps.
24
+ * 26: pinctrl name↔value cross-validation — disagreeing routes are dropped
25
+ * and the record carries pinctrlWarnings for generation to report.
26
+ * 27: pinconfigs YAML ADC (GD32/Atmel/Bouffalolab, package-aware, GD32 pinmux
27
+ * token synthesized from the signal+pin) + the SoC gpio-controller inventory
28
+ * (full port sweep source) + the siliconSources coverage ledger.
29
+ * 28: ADC/DAC device recognition widened past the adcN/dacN forms — lpadc
30
+ * (NXP LPADC), eadc (Nuvoton), sadc, and the adc_N / dac_N underscore forms
31
+ * (NXP MCX) now land in analogDevices, so the device inventory is honest.
32
+ * 29: pinconfig DAC routes (Atmel SAM dac+vout, source normalized to the
33
+ * dac0 nodelabel) join the dacPins pipeline alongside the pinctrl harvest.
34
+ * 30: STM32F1 (AFIO) grammars — the STM32F1_PINMUX value macro, the
35
+ * `timX_chY_pwm_out_pZ` PWM node, and the digitless `dac_outN_pZ` DAC node
36
+ * (source dac1).
37
+ * 31: Atmel SAM PWM routes (pinconfig tc/tcc + wo<N> — the WO pinmux macro
38
+ * token synthesized from the position+peripheral+signal triple). */
39
+ export declare const GENERATOR_REV = 31;
40
+ /** True when dir looks like a Zephyr checkout (kernel header + build file). */
41
+ export declare function isZephyrBase(dir: string): boolean;
42
+ /** The installer-written Zephyr base of the micromamba env, fs-only. */
43
+ export declare function micromambaZephyrBase(): string | undefined;
44
+ /**
45
+ * Locate a Zephyr tree WITHOUT spawning: $ZEPHYR_BASE, the well-known
46
+ * workspace layouts, then the installer's env-vars file. Returns undefined
47
+ * when none names a tree — callers decide whether that is an error
48
+ * (explicit sync) or a skip (passive lookup / no-tree machine).
49
+ */
50
+ export declare function locateZephyrBaseCheap(): string | undefined;
51
+ /** Tree provenance pinned into every overlay. */
52
+ export interface BoardCatalogProvenance {
53
+ readonly zephyrBase: string;
54
+ readonly version: string;
55
+ readonly gitHead?: string;
56
+ readonly boardsMtimeMs: number;
57
+ readonly generatedAt: string;
58
+ readonly variants: number;
59
+ }
60
+ /** A loaded overlay: the catalog + the provenance it was generated under. */
61
+ export interface BoardCatalogOverlay {
62
+ readonly path: string;
63
+ readonly provenance: BoardCatalogProvenance;
64
+ readonly data: Readonly<Record<string, BoardDataEntry>>;
65
+ /** The extraction-logic revision that wrote the file (undefined = pre-rev). */
66
+ readonly generatorRev?: number;
67
+ /** Walk health at sync time (undefined = pre-rev overlay): variants,
68
+ * withFacts, failures, droppedYamls. Persisted so drift between the
69
+ * tree and the catalog is diagnosable without re-walking. */
70
+ readonly stats?: BoardCatalogWalkStats;
71
+ }
72
+ /** Walk health counters (walker → overlay → sync report). */
73
+ export interface BoardCatalogWalkStats {
74
+ readonly variants: number;
75
+ readonly withFacts: number;
76
+ readonly failures: number;
77
+ readonly droppedYamls: number;
78
+ /** Coverage ledger (see walker's BoardCatalogWalkResult.stats.coverage). */
79
+ readonly coverage?: {
80
+ readonly adc: Readonly<Record<string, number>>;
81
+ readonly pwm: Readonly<Record<string, number>>;
82
+ readonly dac: Readonly<Record<string, number>>;
83
+ };
84
+ }
85
+ /** A completed sync's report. */
86
+ export interface BoardCatalogSyncReport {
87
+ readonly zephyrBase: string;
88
+ readonly overlayPath: string;
89
+ readonly provenance: BoardCatalogProvenance;
90
+ readonly stats: BoardCatalogWalkStats;
91
+ /** Identifiers present now but not in the previous overlay. */
92
+ readonly added: readonly string[];
93
+ /** Identifiers whose record differs from the previous overlay. */
94
+ readonly changed: readonly string[];
95
+ /** Identifiers the previous overlay had but this one does not. */
96
+ readonly removed: readonly string[];
97
+ }
98
+ /** The previous-overlay snapshot for the sync diff (undefined on first sync). */
99
+ export interface BoardCatalogSnapshot {
100
+ readonly data: Readonly<Record<string, BoardDataEntry>>;
101
+ }
102
+ /** Diff a fresh catalog against a snapshot (the previous overlay). */
103
+ export declare function diffBoardCatalogs(boards: Readonly<Record<string, BoardDataEntry>>, against: BoardCatalogSnapshot | undefined): {
104
+ added: string[];
105
+ changed: string[];
106
+ removed: string[];
107
+ };
108
+ /** Overlay file path for a Zephyr base: the workspace dir beside the tree. */
109
+ export declare function overlayPathFor(zephyrBase: string): string;
110
+ /** Read + validate one overlay file. Undefined when absent/mismatched/unreadable. */
111
+ export declare function readBoardCatalogOverlayFile(file: string): BoardCatalogOverlay | undefined;
112
+ /** Clear the memoized overlay lookup (tests). */
113
+ export declare function resetBoardCatalogOverlayCache(): void;
114
+ /**
115
+ * The active local overlay, fs-only discovery:
116
+ *
117
+ * 1. $CUTTLEFISH_BOARD_CATALOG — explicit file path (''/off disables)
118
+ * 2. The overlay beside a cheaply-located Zephyr tree
119
+ *
120
+ * Memoized per process. There is no compiled-in fallback: a machine with no
121
+ * Zephyr tree and no overlay has no catalog, and callers report that
122
+ * honestly (`cuttlefish board sync` is the fix).
123
+ */
124
+ export declare function loadBoardCatalogOverlay(): BoardCatalogOverlay | undefined;
125
+ /**
126
+ * True when the overlay no longer describes its tree: the extraction
127
+ * revision moved, the VERSION moved, the git HEAD moved, or boards/ was
128
+ * touched after the overlay was generated. Best-effort — a git-less tarball
129
+ * tree falls back to the mtime signal.
130
+ */
131
+ export declare function isOverlayStale(overlay: BoardCatalogOverlay): boolean;
132
+ /** What ensureFreshBoardCatalog ended up doing. */
133
+ export type BoardCatalogEnsureResult = {
134
+ status: 'fresh';
135
+ zephyrBase: string;
136
+ overlayPath: string;
137
+ } | {
138
+ status: 'synced';
139
+ report: BoardCatalogSyncReport;
140
+ } | {
141
+ status: 'no-tree';
142
+ } | {
143
+ status: 'no-overlay';
144
+ zephyrBase: string;
145
+ overlayPath: string;
146
+ };
147
+ /**
148
+ * Make sure the overlay is current, walking the tree ONLY when work is
149
+ * needed — no overlay yet (and a tree is cheaply locatable), or the
150
+ * overlay's provenance/revision no longer matches the tree. This runs on
151
+ * every build's board-module check; the fresh path is a handful of stat
152
+ * calls. Returns 'no-tree' on machines without a discoverable tree — the
153
+ * caller falls back to whatever catalog is already present.
154
+ */
155
+ export declare function ensureFreshBoardCatalog(): BoardCatalogEnsureResult;
156
+ /**
157
+ * Walk a tree, write the overlay beside it, report the diff vs the previous
158
+ * overlay. The diff describes what changed in YOUR tree — on the first sync
159
+ * everything is "added".
160
+ */
161
+ export declare function syncBoardCatalog(opts?: {
162
+ zephyrBase?: string;
163
+ }): BoardCatalogSyncReport;
164
+ /** Test hook — drop the memo so env/discovery changes take effect. */
165
+ export declare function resetActiveBoardCatalog(): void;
166
+ /**
167
+ * The active board catalog: the local overlay when one is discoverable,
168
+ * else NOTHING (there is no compiled-in database — a machine without a
169
+ * Zephyr tree has no boards until `cuttlefish board sync`). Memoized per
170
+ * process.
171
+ */
172
+ export declare function activeBoardCatalog(): Record<string, BoardDataEntry>;
173
+ /**
174
+ * Fingerprint of one board record as resolved from one overlay: covers the
175
+ * record content, the extraction revision, and the tree provenance. Board
176
+ * modules stamp it; `cuttlefish build` recomputes it cheaply and
177
+ * regenerates the module when it moves — a board change in the config, the
178
+ * catalog overlay, or the Zephyr tree itself recreates the project's board
179
+ * artifacts.
180
+ */
181
+ export declare function boardRecordFingerprint(entry: BoardDataEntry, overlay?: BoardCatalogOverlay): string;
182
+ /**
183
+ * The suffix user facts append to the board-module fingerprint: the SAME
184
+ * raw cuttlefish.facts.json text is hashed by the writer (boardgen, via the
185
+ * framework) and the staleness check (config-loader), so any edit to the
186
+ * file regenerates the module. Empty text → no suffix.
187
+ */
188
+ export declare function factsFingerprint(text: string): string;
189
+ /**
190
+ * Resolve a board target against a catalog: qualified identifier, bare
191
+ * board id, or board/soc prefix. Case rules mirror west: exact case first
192
+ * (revision qualifiers are case-sensitive), then lowercase, then prefix.
193
+ * The single implementation of these rules — boardgen, the config-loader's
194
+ * regen check, and the create flow all resolve through this.
195
+ */
196
+ export declare function findBoardInCatalog(data: Readonly<Record<string, BoardDataEntry>>, target: string): BoardDataEntry | undefined;
@@ -0,0 +1,363 @@
1
+ // ---------------------------------------------------------------------------
2
+ // board-catalog/store.ts — the on-disk board catalog and its lifecycle.
3
+ //
4
+ // There is NO compiled-in board database. The catalog is a machine-local
5
+ // artifact generated from the Zephyr tree the user actually builds with:
6
+ //
7
+ // <workspace>/.cuttlefish/board-catalog.json (workspace = the dir
8
+ // holding the zephyr checkout, so the overlay sits beside the tree it
9
+ // describes)
10
+ //
11
+ // Lifecycle:
12
+ // - syncBoardCatalog: walk the tree, write the overlay, report the diff
13
+ // vs the PREVIOUS overlay (what changed in your tree).
14
+ // - ensureFreshBoardCatalog: cheap fs-only staleness check (tree VERSION,
15
+ // git HEAD, boards/ mtime, generator revision); re-walks only when
16
+ // something moved. This is what makes a plain build after `west update`
17
+ // pick up the tree's boards automatically.
18
+ // - loadBoardCatalogOverlay / activeBoardCatalog: what every lookup
19
+ // (boardgen, the create wizard) resolves against.
20
+ //
21
+ // Tree discovery on these paths is deliberately fs-only (env, well-known
22
+ // layouts, the installer's env-vars file) — no process spawns. The full
23
+ // spawn-based west cascade lives in framework-zephyr (explicit sync only).
24
+ // ----------------------------------------------------------------------------
25
+ import fs from 'node:fs';
26
+ import { createHash } from 'node:crypto';
27
+ import path from 'node:path';
28
+ import { homedir } from 'node:os';
29
+ import { walkBoardCatalog, zephyrVersionOf, gitHeadOf } from './walker.js';
30
+ /** Overlay JSON schema version — bump on shape changes; loaders reject others. */
31
+ const OVERLAY_SCHEMA = 1;
32
+ /** Env var: explicit overlay file path ('' / 'off' disables overlays). */
33
+ const OVERLAY_ENV = 'CUTTLEFISH_BOARD_CATALOG';
34
+ /** Extraction-logic revision. Bump whenever walker/dts-reader change what
35
+ * they extract — an overlay written by an older revision is stale even
36
+ * when the tree itself has not moved. History lives with the walker.
37
+ * 18: boardgen maps EVERY declared ADC controller's routes (channels carry
38
+ * their owning controller; primary stays implicit).
39
+ * 19: boardgen emits the per-family channel-setup pair (zephyr.adc.gain /
40
+ * zephyr.adc.reference — STM32 requires ADC_GAIN_1 + ADC_REF_INTERNAL).
41
+ * 20: the board module re-exports the thin ADC class under its new short
42
+ * name (ADCChannel → ADC, matching GPIO/PWM/UART).
43
+ * 21: DAC joins the short-name convention (DACChannel → DAC); nRF boards
44
+ * synthesize SAADC channels from the SoC family's AIN pad map.
45
+ * 22: revision-qualified variant yamls resolve their .dts through the
46
+ * board's shared base (nrf9160dk's *_0_14_0.yaml → nrf9160dk_nrf9160.dts)
47
+ * instead of dropping — whole boards left the catalog silently.
48
+ * 23: three new silicon sources — RP2 header matrices (ADC_CH/PWM_*
49
+ * macros), Atmel pinconfigs YAMLs (adc routes), connector io-channel-map
50
+ * wiring — plus per-channel ADC pinmux macro tokens.
51
+ * 24: the remaining STM32 pinctrl spellings (H7 adc*_inp*, digitless
52
+ * single-unit adc_in*) + labeled PWM controller nodes (nRF psel matrix).
53
+ * 25: four more pinctrl grammars — NXP Kinetis (FTM/ADC16), LPC CTIMER,
54
+ * i.MX RT (flexpwm/adc node names + in-band pad joins), GigaDevice — plus
55
+ * module include-dir dts roots and board-dir overlay io-channel maps.
56
+ * 26: pinctrl name↔value cross-validation — disagreeing routes are dropped
57
+ * and the record carries pinctrlWarnings for generation to report.
58
+ * 27: pinconfigs YAML ADC (GD32/Atmel/Bouffalolab, package-aware, GD32 pinmux
59
+ * token synthesized from the signal+pin) + the SoC gpio-controller inventory
60
+ * (full port sweep source) + the siliconSources coverage ledger.
61
+ * 28: ADC/DAC device recognition widened past the adcN/dacN forms — lpadc
62
+ * (NXP LPADC), eadc (Nuvoton), sadc, and the adc_N / dac_N underscore forms
63
+ * (NXP MCX) now land in analogDevices, so the device inventory is honest.
64
+ * 29: pinconfig DAC routes (Atmel SAM dac+vout, source normalized to the
65
+ * dac0 nodelabel) join the dacPins pipeline alongside the pinctrl harvest.
66
+ * 30: STM32F1 (AFIO) grammars — the STM32F1_PINMUX value macro, the
67
+ * `timX_chY_pwm_out_pZ` PWM node, and the digitless `dac_outN_pZ` DAC node
68
+ * (source dac1).
69
+ * 31: Atmel SAM PWM routes (pinconfig tc/tcc + wo<N> — the WO pinmux macro
70
+ * token synthesized from the position+peripheral+signal triple). */
71
+ export const GENERATOR_REV = 31;
72
+ // ── fs-only Zephyr tree discovery ──────────────────────────────────────────
73
+ /** True when dir looks like a Zephyr checkout (kernel header + build file). */
74
+ export function isZephyrBase(dir) {
75
+ return (fs.existsSync(path.join(dir, 'CMakeLists.txt')) &&
76
+ fs.existsSync(path.join(dir, 'include', 'zephyr', 'kernel.h')));
77
+ }
78
+ /** Well-known workspace layouts (the dir holding the zephyr/ checkout). */
79
+ function wellKnownWorkspaces() {
80
+ const home = homedir();
81
+ return process.platform === 'win32'
82
+ ? [path.join(home, 'zephyrproject'), path.join(home, 'zephyr'), 'C:\\zephyrproject', 'C:\\zephyr']
83
+ : [path.join(home, 'zephyrproject'), path.join(home, 'zephyr'), '/opt/zephyrproject', '/opt/zephyr'];
84
+ }
85
+ /** The installer-written Zephyr base of the micromamba env, fs-only. */
86
+ export function micromambaZephyrBase() {
87
+ const root = process.env.MAMBA_ROOT_PREFIX || path.join(homedir(), 'micromamba');
88
+ const envDir = path.join(root, 'envs', process.env.TYPECAD_ZEPHYR_ENV || 'zephyr');
89
+ const candidates = process.platform === 'win32'
90
+ ? [path.join(envDir, 'etc', 'conda', 'env-vars.ps1'), path.join(envDir, 'etc', 'conda', 'env-vars.bat')]
91
+ : [path.join(envDir, 'etc', 'conda', 'env-vars.sh')];
92
+ for (const f of candidates) {
93
+ if (!fs.existsSync(f))
94
+ continue;
95
+ try {
96
+ const m = fs.readFileSync(f, 'utf8').match(/TYPECAD_ZEPHYR_BASE\s*=\s*"([^"]+)"/);
97
+ if (m && isZephyrBase(m[1]))
98
+ return m[1];
99
+ }
100
+ catch {
101
+ // unreadable — try the next candidate
102
+ }
103
+ }
104
+ return undefined;
105
+ }
106
+ /**
107
+ * Locate a Zephyr tree WITHOUT spawning: $ZEPHYR_BASE, the well-known
108
+ * workspace layouts, then the installer's env-vars file. Returns undefined
109
+ * when none names a tree — callers decide whether that is an error
110
+ * (explicit sync) or a skip (passive lookup / no-tree machine).
111
+ */
112
+ export function locateZephyrBaseCheap() {
113
+ // $ZEPHYR_BASE is authoritative when set: an explicit pointer that isn't a
114
+ // tree is an error state the caller should see, not something to paper
115
+ // over with a well-known fallback (and it makes test isolation possible
116
+ // on machines that DO have ~/zephyrproject).
117
+ if (process.env.ZEPHYR_BASE) {
118
+ return isZephyrBase(process.env.ZEPHYR_BASE) ? path.resolve(process.env.ZEPHYR_BASE) : undefined;
119
+ }
120
+ const candidates = [
121
+ ...wellKnownWorkspaces().map((ws) => path.join(ws, 'zephyr')),
122
+ micromambaZephyrBase(),
123
+ ];
124
+ for (const c of candidates) {
125
+ if (c && isZephyrBase(c))
126
+ return path.resolve(c);
127
+ }
128
+ return undefined;
129
+ }
130
+ /** Deterministic JSON of a record — key-sorted so field order never fakes a diff. */
131
+ function stableStringify(value) {
132
+ if (Array.isArray(value))
133
+ return `[${value.map(stableStringify).join(',')}]`;
134
+ if (value && typeof value === 'object') {
135
+ const entries = Object.entries(value)
136
+ .filter(([, v]) => v !== undefined)
137
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
138
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(',')}}`;
139
+ }
140
+ return JSON.stringify(value) ?? 'null';
141
+ }
142
+ /** Diff a fresh catalog against a snapshot (the previous overlay). */
143
+ export function diffBoardCatalogs(boards, against) {
144
+ const previous = against?.data ?? {};
145
+ const added = [];
146
+ const changed = [];
147
+ for (const [id, rec] of Object.entries(boards)) {
148
+ const old = previous[id];
149
+ if (!old)
150
+ added.push(id);
151
+ else if (stableStringify(old) !== stableStringify(rec))
152
+ changed.push(id);
153
+ }
154
+ const removed = Object.keys(previous).filter((id) => !(id in boards));
155
+ return { added, changed, removed };
156
+ }
157
+ /** Overlay file path for a Zephyr base: the workspace dir beside the tree. */
158
+ export function overlayPathFor(zephyrBase) {
159
+ return path.join(path.dirname(path.resolve(zephyrBase)), '.cuttlefish', 'board-catalog.json');
160
+ }
161
+ /** Read + validate one overlay file. Undefined when absent/mismatched/unreadable. */
162
+ export function readBoardCatalogOverlayFile(file) {
163
+ let parsed;
164
+ try {
165
+ parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
166
+ }
167
+ catch {
168
+ return undefined;
169
+ }
170
+ if (parsed.schema !== OVERLAY_SCHEMA || !parsed.provenance || !parsed.boards)
171
+ return undefined;
172
+ return {
173
+ path: file,
174
+ provenance: parsed.provenance,
175
+ data: parsed.boards,
176
+ ...(parsed.generatorRev !== undefined ? { generatorRev: parsed.generatorRev } : {}),
177
+ ...(parsed.stats ? { stats: parsed.stats } : {}),
178
+ };
179
+ }
180
+ let cachedOverlay = undefined;
181
+ /** Clear the memoized overlay lookup (tests). */
182
+ export function resetBoardCatalogOverlayCache() {
183
+ cachedOverlay = undefined;
184
+ resetActiveBoardCatalog();
185
+ }
186
+ /**
187
+ * The active local overlay, fs-only discovery:
188
+ *
189
+ * 1. $CUTTLEFISH_BOARD_CATALOG — explicit file path (''/off disables)
190
+ * 2. The overlay beside a cheaply-located Zephyr tree
191
+ *
192
+ * Memoized per process. There is no compiled-in fallback: a machine with no
193
+ * Zephyr tree and no overlay has no catalog, and callers report that
194
+ * honestly (`cuttlefish board sync` is the fix).
195
+ */
196
+ export function loadBoardCatalogOverlay() {
197
+ if (cachedOverlay !== undefined)
198
+ return cachedOverlay ?? undefined;
199
+ cachedOverlay = null;
200
+ const explicit = process.env[OVERLAY_ENV];
201
+ if (explicit !== undefined) {
202
+ if (explicit !== '' && explicit.toLowerCase() !== 'off')
203
+ cachedOverlay = readBoardCatalogOverlayFile(explicit);
204
+ }
205
+ else {
206
+ const zephyrBase = locateZephyrBaseCheap();
207
+ if (zephyrBase)
208
+ cachedOverlay = readBoardCatalogOverlayFile(overlayPathFor(zephyrBase));
209
+ }
210
+ return cachedOverlay ?? undefined;
211
+ }
212
+ /**
213
+ * True when the overlay no longer describes its tree: the extraction
214
+ * revision moved, the VERSION moved, the git HEAD moved, or boards/ was
215
+ * touched after the overlay was generated. Best-effort — a git-less tarball
216
+ * tree falls back to the mtime signal.
217
+ */
218
+ export function isOverlayStale(overlay) {
219
+ if ((overlay.generatorRev ?? 0) !== GENERATOR_REV)
220
+ return true;
221
+ const base = overlay.provenance.zephyrBase;
222
+ if (!isZephyrBase(base))
223
+ return true; // the tree itself is gone
224
+ if (zephyrVersionOf(base) !== overlay.provenance.version)
225
+ return true;
226
+ const head = gitHeadOf(base);
227
+ if (head && overlay.provenance.gitHead && head !== overlay.provenance.gitHead)
228
+ return true;
229
+ try {
230
+ if (fs.statSync(path.join(base, 'boards')).mtimeMs > overlay.provenance.boardsMtimeMs + 1000)
231
+ return true;
232
+ }
233
+ catch {
234
+ return true;
235
+ }
236
+ return false;
237
+ }
238
+ /**
239
+ * Make sure the overlay is current, walking the tree ONLY when work is
240
+ * needed — no overlay yet (and a tree is cheaply locatable), or the
241
+ * overlay's provenance/revision no longer matches the tree. This runs on
242
+ * every build's board-module check; the fresh path is a handful of stat
243
+ * calls. Returns 'no-tree' on machines without a discoverable tree — the
244
+ * caller falls back to whatever catalog is already present.
245
+ */
246
+ export function ensureFreshBoardCatalog() {
247
+ const explicit = process.env[OVERLAY_ENV];
248
+ if (explicit !== undefined && explicit !== '' && explicit.toLowerCase() !== 'off') {
249
+ // An explicit overlay is authoritative — never re-walk past it.
250
+ const overlay = readBoardCatalogOverlayFile(explicit);
251
+ return overlay
252
+ ? { status: 'fresh', zephyrBase: overlay.provenance.zephyrBase, overlayPath: overlay.path }
253
+ : { status: 'no-tree' };
254
+ }
255
+ const zephyrBase = locateZephyrBaseCheap();
256
+ if (!zephyrBase)
257
+ return { status: 'no-tree' };
258
+ const overlayPath = overlayPathFor(zephyrBase);
259
+ const overlay = readBoardCatalogOverlayFile(overlayPath);
260
+ // No overlay yet (fresh machine / first project) — create it. The walk is
261
+ // once; every later build rides the cheap provenance check.
262
+ if (!overlay)
263
+ return { status: 'synced', report: syncBoardCatalog({ zephyrBase }) };
264
+ if (isOverlayStale(overlay))
265
+ return { status: 'synced', report: syncBoardCatalog({ zephyrBase }) };
266
+ return { status: 'fresh', zephyrBase, overlayPath };
267
+ }
268
+ /**
269
+ * Walk a tree, write the overlay beside it, report the diff vs the previous
270
+ * overlay. The diff describes what changed in YOUR tree — on the first sync
271
+ * everything is "added".
272
+ */
273
+ export function syncBoardCatalog(opts = {}) {
274
+ const zephyrBase = path.resolve(opts.zephyrBase ?? locateZephyrBaseCheap() ?? '');
275
+ if (!zephyrBase || !isZephyrBase(zephyrBase)) {
276
+ throw new Error(`No Zephyr tree found to sync the board catalog from.\n` +
277
+ `Set ZEPHYR_BASE, or pass the checkout explicitly:\n` +
278
+ ` cuttlefish board sync <path-to-zephyr>\n` +
279
+ `Or install one via '@typecad/zephyr-installer'.`);
280
+ }
281
+ const overlayPath = overlayPathFor(zephyrBase);
282
+ const previous = readBoardCatalogOverlayFile(overlayPath);
283
+ const walk = walkBoardCatalog(zephyrBase);
284
+ fs.mkdirSync(path.dirname(overlayPath), { recursive: true });
285
+ fs.writeFileSync(overlayPath, JSON.stringify({
286
+ schema: OVERLAY_SCHEMA,
287
+ generatorRev: GENERATOR_REV,
288
+ provenance: walk.provenance,
289
+ boards: walk.boards,
290
+ stats: walk.stats,
291
+ }, null, 1), 'utf-8');
292
+ resetBoardCatalogOverlayCache();
293
+ return {
294
+ zephyrBase,
295
+ overlayPath,
296
+ provenance: walk.provenance,
297
+ stats: walk.stats,
298
+ ...diffBoardCatalogs(walk.boards, previous ? { data: previous.data } : undefined),
299
+ };
300
+ }
301
+ // ── the active catalog (listing/lookup surface) ────────────────────────────
302
+ let cachedActive;
303
+ /** Test hook — drop the memo so env/discovery changes take effect. */
304
+ export function resetActiveBoardCatalog() {
305
+ cachedActive = undefined;
306
+ }
307
+ /**
308
+ * The active board catalog: the local overlay when one is discoverable,
309
+ * else NOTHING (there is no compiled-in database — a machine without a
310
+ * Zephyr tree has no boards until `cuttlefish board sync`). Memoized per
311
+ * process.
312
+ */
313
+ export function activeBoardCatalog() {
314
+ if (cachedActive)
315
+ return cachedActive;
316
+ cachedActive = { ...loadBoardCatalogOverlay()?.data };
317
+ return cachedActive;
318
+ }
319
+ /**
320
+ * Fingerprint of one board record as resolved from one overlay: covers the
321
+ * record content, the extraction revision, and the tree provenance. Board
322
+ * modules stamp it; `cuttlefish build` recomputes it cheaply and
323
+ * regenerates the module when it moves — a board change in the config, the
324
+ * catalog overlay, or the Zephyr tree itself recreates the project's board
325
+ * artifacts.
326
+ */
327
+ export function boardRecordFingerprint(entry, overlay) {
328
+ return createHash('sha1').update(stableStringify({
329
+ rev: GENERATOR_REV,
330
+ version: overlay?.provenance.version ?? null,
331
+ gitHead: overlay?.provenance.gitHead ?? null,
332
+ record: entry,
333
+ })).digest('hex').slice(0, 16);
334
+ }
335
+ /**
336
+ * The suffix user facts append to the board-module fingerprint: the SAME
337
+ * raw cuttlefish.facts.json text is hashed by the writer (boardgen, via the
338
+ * framework) and the staleness check (config-loader), so any edit to the
339
+ * file regenerates the module. Empty text → no suffix.
340
+ */
341
+ export function factsFingerprint(text) {
342
+ return text ? `+${createHash('sha1').update(text).digest('hex').slice(0, 12)}` : '';
343
+ }
344
+ /**
345
+ * Resolve a board target against a catalog: qualified identifier, bare
346
+ * board id, or board/soc prefix. Case rules mirror west: exact case first
347
+ * (revision qualifiers are case-sensitive), then lowercase, then prefix.
348
+ * The single implementation of these rules — boardgen, the config-loader's
349
+ * regen check, and the create flow all resolve through this.
350
+ */
351
+ export function findBoardInCatalog(data, target) {
352
+ const raw = target.trim();
353
+ if (data[raw])
354
+ return data[raw];
355
+ const t = raw.toLowerCase();
356
+ if (data[t])
357
+ return data[t];
358
+ const exact = Object.keys(data).find((k) => k === t);
359
+ if (exact)
360
+ return data[exact];
361
+ const prefix = Object.keys(data).find((k) => k.toLowerCase().startsWith(t + '/'));
362
+ return prefix ? data[prefix] : undefined;
363
+ }