@typecad/cuttlefish 1.0.0-alpha.14 → 1.0.0-alpha.16

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 (315) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +25 -26
  3. package/dist/api/config.d.ts +22 -26
  4. package/dist/api/schema/types.d.ts +20 -3
  5. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  6. package/dist/api/shared/async-runtime-static.js +5 -5
  7. package/dist/api/shared/async-types.d.ts +3 -2
  8. package/dist/api/shared/board-catalog.d.ts +2 -0
  9. package/dist/api/shared/board-catalog.js +6 -0
  10. package/dist/api/shared/board-resolver.d.ts +4 -7
  11. package/dist/api/shared/board-resolver.js +2 -1
  12. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  13. package/dist/api/shared/coop-scheduler.js +1 -1
  14. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  15. package/dist/api/shared/cpp-type-ir.js +1 -1
  16. package/dist/api/shared/display-profile.d.ts +1 -1
  17. package/dist/api/shared/display-profile.js +3 -5
  18. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  19. package/dist/api/shared/framework-manifest-registry.js +0 -1
  20. package/dist/api/shared/framework-manifest.d.ts +7 -6
  21. package/dist/api/shared/framework-manifest.js +6 -11
  22. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  23. package/dist/api/shared/hal-op-ir.js +31 -65
  24. package/dist/api/shared/index.d.ts +0 -4
  25. package/dist/api/shared/index.js +1 -7
  26. package/dist/api/shared/ir-core.d.ts +6 -0
  27. package/dist/api/shared/ir-declarations.d.ts +4 -0
  28. package/dist/api/shared/ir.d.ts +1 -3
  29. package/dist/api/shared/platform-strategy.d.ts +14 -37
  30. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  31. package/dist/api/shared/polyfill-types.js +0 -27
  32. package/dist/api/shared/promise-runtime.js +3 -3
  33. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  34. package/dist/api/shared/spdx-licenses.js +1 -1
  35. package/dist/api/shared/toolchain-types.d.ts +1 -1
  36. package/dist/api/shared/toolchain-types.js +3 -3
  37. package/dist/api/shared/types.d.ts +0 -3
  38. package/dist/api/shared/validate-framework-manifest.js +13 -33
  39. package/dist/board-catalog/dts-reader.d.ts +220 -0
  40. package/dist/board-catalog/dts-reader.js +1052 -0
  41. package/dist/board-catalog/index.d.ts +11 -0
  42. package/dist/board-catalog/index.js +12 -0
  43. package/dist/board-catalog/pinconfig.d.ts +48 -0
  44. package/dist/board-catalog/pinconfig.js +263 -0
  45. package/dist/board-catalog/sdk.d.ts +82 -0
  46. package/dist/board-catalog/sdk.js +180 -0
  47. package/dist/board-catalog/store.d.ts +196 -0
  48. package/dist/board-catalog/store.js +363 -0
  49. package/dist/board-catalog/types.d.ts +233 -0
  50. package/dist/board-catalog/types.js +7 -0
  51. package/dist/board-catalog/walker.d.ts +112 -0
  52. package/dist/board-catalog/walker.js +1101 -0
  53. package/dist/cli-utils.d.ts +2 -2
  54. package/dist/cli-utils.js +5 -5
  55. package/dist/cli.js +219 -242
  56. package/dist/config-loader.d.ts +14 -19
  57. package/dist/config-loader.js +240 -78
  58. package/dist/config-schema.d.ts +3 -5
  59. package/dist/config-schema.js +8 -8
  60. package/dist/contract/board-generator.d.ts +4 -4
  61. package/dist/contract/board-generator.js +16 -5
  62. package/dist/contract/contract-parser.d.ts +27 -0
  63. package/dist/contract/contract-parser.js +76 -0
  64. package/dist/contract/index.d.ts +1 -1
  65. package/dist/contract/index.js +70 -21
  66. package/dist/create/active-board-catalog.d.ts +9 -0
  67. package/dist/create/active-board-catalog.js +94 -0
  68. package/dist/create/board-catalog.generated.d.ts +96 -0
  69. package/dist/create/board-catalog.generated.js +128737 -0
  70. package/dist/create/board-search.d.ts +23 -0
  71. package/dist/create/board-search.js +88 -0
  72. package/dist/create/editor-integration.d.ts +4 -2
  73. package/dist/create/editor-integration.js +41 -2
  74. package/dist/create/framework-catalog.d.ts +57 -9
  75. package/dist/create/framework-catalog.js +76 -57
  76. package/dist/create/index.d.ts +5 -11
  77. package/dist/create/index.js +3 -8
  78. package/dist/create/mcu-target.d.ts +39 -0
  79. package/dist/create/mcu-target.js +80 -0
  80. package/dist/create/pack-targets.d.ts +19 -0
  81. package/dist/create/pack-targets.js +56 -0
  82. package/dist/create/scaffold.d.ts +4 -7
  83. package/dist/create/scaffold.js +19 -109
  84. package/dist/create/templates.d.ts +32 -5
  85. package/dist/create/templates.js +130 -116
  86. package/dist/create/wizard.d.ts +12 -1
  87. package/dist/create/wizard.js +168 -38
  88. package/dist/debug/preprocessor.d.ts +5 -0
  89. package/dist/debug/preprocessor.js +31 -7
  90. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  91. package/dist/diagnostics/diagnostics-report.js +3 -11
  92. package/dist/diagnostics/json-schema.d.ts +1 -1
  93. package/dist/diagnostics/md-writer.js +2 -2
  94. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  95. package/dist/diagnostics/mermaid-builder.js +1 -1
  96. package/dist/emit/cpp-emitter.js +4 -1
  97. package/dist/emit/emitters/class-emitter.js +32 -4
  98. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  99. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  100. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  101. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  102. package/dist/emit/emitters/output-finalizer.js +31 -23
  103. package/dist/emit/emitters/setup.js +125 -63
  104. package/dist/emit/emitters/top-level-prep.js +1 -1
  105. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  106. package/dist/emit/emitters/ui-emitter.js +1 -1
  107. package/dist/emit/expression-renderer.d.ts +3 -3
  108. package/dist/emit/expression-renderer.js +17 -7
  109. package/dist/emit/route-hal-op.js +17 -48
  110. package/dist/emit/snprintf-helpers.d.ts +1 -3
  111. package/dist/emit/snprintf-helpers.js +1 -39
  112. package/dist/emit/statement-renderer.d.ts +0 -4
  113. package/dist/emit/statement-renderer.js +54 -45
  114. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  115. package/dist/emit/utils/async-state-machine.js +56 -54
  116. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  117. package/dist/emit/utils/comment-helpers.js +1 -1
  118. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  119. package/dist/emit/utils/cpp-helpers.js +2 -2
  120. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  121. package/dist/emit/utils/index.d.ts +3 -3
  122. package/dist/emit/utils/index.js +3 -3
  123. package/dist/emit/utils/type-inference.d.ts +1 -29
  124. package/dist/emit/utils/type-inference.js +0 -75
  125. package/dist/framework-package.d.ts +3 -0
  126. package/dist/framework-package.js +23 -6
  127. package/dist/framework-registry.d.ts +5 -5
  128. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  129. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  130. package/dist/frameworks/native/index.d.ts +3 -0
  131. package/dist/frameworks/native/index.js +15 -0
  132. package/dist/frameworks/native/native-compile.d.ts +17 -0
  133. package/dist/frameworks/native/native-compile.js +170 -0
  134. package/dist/frameworks/native/native-config.d.ts +41 -0
  135. package/dist/frameworks/native/native-config.js +7 -0
  136. package/dist/frameworks/native/strategy.d.ts +97 -0
  137. package/dist/frameworks/native/strategy.js +664 -0
  138. package/dist/ir/board-resolver.d.ts +18 -23
  139. package/dist/ir/board-resolver.js +43 -401
  140. package/dist/ir/build-ir-state.d.ts +3 -0
  141. package/dist/ir/build-ir-state.js +42 -0
  142. package/dist/ir/build-ir.d.ts +1 -1
  143. package/dist/ir/build-ir.js +40 -42
  144. package/dist/ir/declaration-builders.js +1 -0
  145. package/dist/ir/expression-to-ir.js +17 -10
  146. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  147. package/dist/ir/hal/hal-emitter.js +92 -41
  148. package/dist/ir/hal/hal-parser.d.ts +5 -5
  149. package/dist/ir/hal/hal-parser.js +128 -36
  150. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  151. package/dist/ir/hal/hal-plugins.js +425 -716
  152. package/dist/ir/heap-analysis.js +2 -41
  153. package/dist/ir/identifier-collector.js +32 -20
  154. package/dist/ir/network-validation.js +7 -11
  155. package/dist/ir/ownership-analysis.js +75 -13
  156. package/dist/ir/peripheral-usage.d.ts +8 -2
  157. package/dist/ir/peripheral-usage.js +78 -149
  158. package/dist/ir/peripheral-validation.js +23 -3
  159. package/dist/ir/pin-capability-validation.d.ts +12 -0
  160. package/dist/ir/pin-capability-validation.js +61 -97
  161. package/dist/ir/pin-mode-validation.js +5 -5
  162. package/dist/ir/program-analysis.d.ts +8 -15
  163. package/dist/ir/program-analysis.js +140 -323
  164. package/dist/ir/pulldown-validation.js +11 -4
  165. package/dist/ir/resource-analysis.js +15 -3
  166. package/dist/ir/statement-to-ir.js +0 -6
  167. package/dist/ir/timing-validation.js +1 -1
  168. package/dist/ir/transformers/array-methods.js +11 -5
  169. package/dist/ir/transformers/call-statement.js +20 -87
  170. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  171. package/dist/ir/transformers/callback-context-registry.js +8 -16
  172. package/dist/ir/transformers/control-flow.js +0 -13
  173. package/dist/ir/transformers/expressions.js +10 -9
  174. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  175. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  176. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  177. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  178. package/dist/ir/transformers/namespace-methods.js +1 -1
  179. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  180. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  181. package/dist/ir/transformers/variables.js +338 -42
  182. package/dist/ir/try-catch-validation.js +4 -3
  183. package/dist/ir/type-resolution.js +15 -4
  184. package/dist/ir/validation-orchestrator.js +0 -9
  185. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  186. package/dist/libdef/cpp-to-decl.js +0 -72
  187. package/dist/libdef/registry.d.ts +0 -1
  188. package/dist/libdef/registry.js +1 -24
  189. package/dist/library/init.d.ts +1 -1
  190. package/dist/library/init.js +137 -137
  191. package/dist/library-packages.d.ts +1 -1
  192. package/dist/library-packages.js +1 -1
  193. package/dist/mapping/peripheral-names.js +9 -4
  194. package/dist/mapping/source-map.d.ts +3 -4
  195. package/dist/mapping/source-map.js +12 -18
  196. package/dist/orchestrator/graph-builder.d.ts +3 -3
  197. package/dist/orchestrator/graph-builder.js +13 -12
  198. package/dist/orchestrator/type-checker.d.ts +3 -3
  199. package/dist/orchestrator/type-checker.js +3 -3
  200. package/dist/platform/async-runtime.js +1 -1
  201. package/dist/platform/coop-scheduler-runtime.js +2 -2
  202. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  203. package/dist/platform/generic-debug-codegen.js +185 -0
  204. package/dist/platform/generic-strategy.d.ts +3 -4
  205. package/dist/platform/generic-strategy.js +5 -80
  206. package/dist/platform/index.js +1 -1
  207. package/dist/platform/registry.d.ts +2 -2
  208. package/dist/platform/registry.js +2 -2
  209. package/dist/safety/asil-decorators.d.ts +32 -0
  210. package/dist/safety/asil-decorators.js +32 -0
  211. package/dist/safety/authoring.d.ts +85 -0
  212. package/dist/safety/authoring.js +61 -0
  213. package/dist/safety/engine.d.ts +3 -0
  214. package/dist/safety/engine.js +83 -0
  215. package/dist/safety/hal/ops.d.ts +48 -0
  216. package/dist/safety/hal/ops.js +46 -0
  217. package/dist/safety/iso26262/analyze.d.ts +13 -0
  218. package/dist/safety/iso26262/analyze.js +29 -0
  219. package/dist/safety/iso26262/asil.d.ts +10 -0
  220. package/dist/safety/iso26262/asil.js +22 -0
  221. package/dist/safety/iso26262/collect.d.ts +14 -0
  222. package/dist/safety/iso26262/collect.js +137 -0
  223. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  224. package/dist/safety/iso26262/goto-checker.js +72 -0
  225. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  226. package/dist/safety/iso26262/heap-checker.js +57 -0
  227. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  228. package/dist/safety/iso26262/init-checker.js +127 -0
  229. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  230. package/dist/safety/iso26262/loop-checker.js +86 -0
  231. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  232. package/dist/safety/iso26262/recursion-checker.js +134 -0
  233. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  234. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  235. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  236. package/dist/safety/passes/pinMode-intercept.js +60 -0
  237. package/dist/safety/runtime/mode-table.d.ts +5 -0
  238. package/dist/safety/runtime/mode-table.js +55 -0
  239. package/dist/safety/runtime/polyfills.d.ts +6 -0
  240. package/dist/safety/runtime/polyfills.js +12 -0
  241. package/dist/safety/runtime/result-struct.d.ts +11 -0
  242. package/dist/safety/runtime/result-struct.js +98 -0
  243. package/dist/safety/runtime/safe-int.d.ts +33 -0
  244. package/dist/safety/runtime/safe-int.js +170 -0
  245. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  246. package/dist/safety/runtime/safe-traits.js +95 -0
  247. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  248. package/dist/safety/runtime/safe-variable.js +226 -0
  249. package/dist/safety/runtime/vote.d.ts +8 -0
  250. package/dist/safety/runtime/vote.js +92 -0
  251. package/dist/safety/runtime/write-verify.d.ts +8 -0
  252. package/dist/safety/runtime/write-verify.js +71 -0
  253. package/dist/safety/safe-int-types.d.ts +45 -0
  254. package/dist/safety/safe-int-types.js +25 -0
  255. package/dist/safety/safe-variable-types.d.ts +29 -0
  256. package/dist/safety/safe-variable-types.js +19 -0
  257. package/dist/safety/safety-bridge.d.ts +5 -6
  258. package/dist/safety/safety-bridge.js +15 -28
  259. package/dist/safety/sidecar-bridge.d.ts +1 -3
  260. package/dist/safety/sidecar-bridge.js +3 -20
  261. package/dist/safety/specifiers.d.ts +6 -0
  262. package/dist/safety/specifiers.js +14 -0
  263. package/dist/safety-hook.d.ts +8 -7
  264. package/dist/safety-hook.js +12 -13
  265. package/dist/testing.d.ts +4 -5
  266. package/dist/testing.js +5 -5
  267. package/dist/transpile/resolution.d.ts +1 -1
  268. package/dist/transpile/resolution.js +18 -9
  269. package/dist/transpile/test-pins.d.ts +18 -0
  270. package/dist/transpile/test-pins.js +142 -0
  271. package/dist/transpile.d.ts +1 -2
  272. package/dist/transpile.js +36 -30
  273. package/dist/types.d.ts +26 -26
  274. package/dist/ui-hook.d.ts +1 -1
  275. package/dist/utils/cli.d.ts +2 -2
  276. package/dist/utils/cli.js +92 -154
  277. package/dist/utils/fs.d.ts +3 -4
  278. package/dist/utils/fs.js +4 -5
  279. package/dist/utils/strings.d.ts +0 -1
  280. package/dist/utils/strings.js +0 -8
  281. package/dist/utils/ui.d.ts +5 -2
  282. package/dist/utils/ui.js +13 -3
  283. package/package.json +158 -157
  284. package/dist/add-preset.d.ts +0 -4
  285. package/dist/add-preset.js +0 -74
  286. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  287. package/dist/api/shared/async-symbol-detector.js +0 -140
  288. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  289. package/dist/api/shared/native-display-op-resolver.js +0 -64
  290. package/dist/create/board-checklist.d.ts +0 -2
  291. package/dist/create/board-checklist.js +0 -52
  292. package/dist/create/board-codegen.d.ts +0 -9
  293. package/dist/create/board-codegen.js +0 -238
  294. package/dist/create/board-generators.d.ts +0 -12
  295. package/dist/create/board-generators.js +0 -651
  296. package/dist/create/board-spec.d.ts +0 -181
  297. package/dist/create/board-spec.js +0 -214
  298. package/dist/install/framework-catalog.d.ts +0 -53
  299. package/dist/install/framework-catalog.js +0 -107
  300. package/dist/install/handle-install.d.ts +0 -35
  301. package/dist/install/handle-install.js +0 -177
  302. package/dist/install/index.d.ts +0 -4
  303. package/dist/install/index.js +0 -3
  304. package/dist/ir/pin-state-tracking.d.ts +0 -58
  305. package/dist/ir/pin-state-tracking.js +0 -182
  306. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  307. package/dist/ir/pwm-timer-sharing.js +0 -94
  308. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  309. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  310. package/dist/libdef/component-decls.d.ts +0 -2
  311. package/dist/libdef/component-decls.js +0 -6
  312. package/dist/libdef/component-discovery.d.ts +0 -43
  313. package/dist/libdef/component-discovery.js +0 -83
  314. package/dist/theme-tokens.d.ts +0 -22
  315. package/dist/theme-tokens.js +0 -172
@@ -5,21 +5,24 @@
5
5
  */
6
6
  export interface ResolvedCuttlefishConfig {
7
7
  target?: string;
8
- /** MCU package specifier (e.g. '@typecad/mcu-atmega328p'). */
9
- mcu?: string;
10
- /** Board package specifier (e.g. '@typecad/board-arduino-uno'). (Deprecated) */
8
+ /** Zephyr board target — the qualified `west build -b` argument (e.g.
9
+ * 'esp32s3_devkitc/esp32s3/procpu'). The project-local board module is
10
+ * generated from the framework's board data pack on first build. */
11
11
  board?: string;
12
+ /** Zephyr SoC name for contract-based projects (custom PCBs, no board
13
+ * target) — e.g. 'stm32f411xe'. Selects the curated soc descriptor. */
14
+ soc?: string;
12
15
  /** Path to a TypeCAD contract file (*.contract.json). */
13
16
  contract?: string;
14
- /** Build target identifier (e.g. FQBN for Arduino CLI). */
17
+ /** Build target identifier (the framework's board id, e.g. 'blackpill_f411ce/stm32f411xe'). */
15
18
  buildTarget?: string;
16
- /** Output framework (e.g. 'arduino'). */
19
+ /** Output framework (e.g. 'zephyr'). */
17
20
  outputFramework?: string;
18
21
  /** Output directory. */
19
22
  outputOutDir?: string;
20
23
  /**
21
24
  * Framework package for code generation strategy.
22
- * Can be '@typecad/framework-arduino', '@typecad/framework-zephyr',
25
+ * Can be '@typecad/framework-zephyr', '@typecad/framework-native',
23
26
  * '@typecad/framework-native', or a custom path.
24
27
  */
25
28
  framework?: string;
@@ -29,11 +32,6 @@ export interface ResolvedCuttlefishConfig {
29
32
  entry?: string;
30
33
  /** Path to the config file that was loaded. */
31
34
  configPath: string;
32
- /** Console polyfill configuration. */
33
- console?: {
34
- baudRate?: number;
35
- port?: string;
36
- };
37
35
  /** Extra compiler flags from `output.extraFlags`. */
38
36
  outputExtraFlags?: string[];
39
37
  /** Additional defines from `output.defines`. */
@@ -69,15 +67,12 @@ export type ConfigDropWarning = (message: string) => void;
69
67
  */
70
68
  export declare function parseConfigFile(configPath: string): ResolvedCuttlefishConfig | undefined;
71
69
  /**
72
- * Generates (or updates) a `cuttlefish-env.d.ts` file next to the config file.
73
- *
74
- * The file declares an ambient `@typecad/board` module that simply re-exports
75
- * everything from the concrete board package. This lets the TypeScript
76
- * language server resolve `import { ... } from '@typecad/board'` in user files.
77
- *
78
- * The file is regenerated on every transpiler run so it stays in sync when
79
- * the board changes in `cuttlefish.config.ts`.
70
+ * Force-regenerate the project-local board module the `cuttlefish board
71
+ * regen` path. Board-target configs only: contract projects write their
72
+ * board module through the contract reader, and native projects have none.
73
+ * Returns the .cuttlefish directory the module was written to.
80
74
  */
75
+ export declare function regenBoardModule(config: ResolvedCuttlefishConfig): string;
81
76
  export declare function generateVirtualTypeDeclaration(config: ResolvedCuttlefishConfig, platformDeclarations?: string[]): void;
82
77
  /**
83
78
  * High-level entry point: find and load `cuttlefish.config.ts` starting from
@@ -9,13 +9,17 @@
9
9
  import path from "node:path";
10
10
  import fs from "node:fs";
11
11
  import ts from "typescript";
12
+ import { createRequire } from "node:module";
13
+ import { loadBoardCatalogOverlay, ensureFreshBoardCatalog, boardRecordFingerprint, factsFingerprint, findBoardInCatalog, } from "./board-catalog/index.js";
14
+ import { fileURLToPath } from "node:url";
15
+ import { readTestPinsFile, buildTestPinsModuleContent } from "./transpile/test-pins.js";
12
16
  import { safeValidateConfig } from "./config-schema.js";
13
17
  /** The filename we search for when walking up directories. */
14
18
  const CONFIG_FILENAME = "cuttlefish.config.ts";
15
19
  /** Top-level keys recognized by CuttlefishConfigSchema — used to warn about
16
20
  * misspelled keys that the AST extraction would otherwise drop silently. */
17
21
  const KNOWN_TOP_LEVEL_KEYS = new Set([
18
- "entry", "target", "mcu", "board", "contract", "framework", "psram",
22
+ "entry", "target", "board", "soc", "contract", "framework", "psram",
19
23
  "output", "frameworkData", "include", "exclude", "test", "toolchain",
20
24
  "console", "native", "zephyr", "display",
21
25
  ]);
@@ -390,19 +394,33 @@ export function parseConfigFile(configPath) {
390
394
  const target = flat.get("target");
391
395
  if (typeof target === "string")
392
396
  resolved.target = target;
393
- const mcu = flat.get("mcu");
394
- if (typeof mcu === "string")
395
- resolved.mcu = mcu;
397
+ const soc = flat.get("soc");
398
+ if (typeof soc === "string")
399
+ resolved.soc = soc;
396
400
  const board = flat.get("board");
397
401
  if (typeof board === "string")
398
402
  resolved.board = board;
399
403
  const contract = flat.get("contract");
400
404
  if (typeof contract === "string")
401
405
  resolved.contract = contract;
402
- // We do not extract fqbn here anymore, it should be in frameworkData
403
- const buildTarget = flat.get("frameworkData.buildTarget");
404
- if (typeof buildTarget === "string")
405
- resolved.buildTarget = buildTarget;
406
+ // The build target (what `west build -b` receives) has a single source of
407
+ // truth: `board:` for board-target projects. `frameworkData.buildTarget`
408
+ // is honored only for board-less projects (bare silicon / contract, whose
409
+ // build target is a generated custom board). When both are present they
410
+ // must agree — a disagreement is the split-brain trap (board module for
411
+ // one board, firmware for another), so board wins with a warning.
412
+ const buildTargetFlat = flat.get("frameworkData.buildTarget");
413
+ if (typeof board === "string") {
414
+ if (typeof buildTargetFlat === "string" && buildTargetFlat !== board) {
415
+ warn(`config sets both board: '${board}' and frameworkData.buildTarget: '${buildTargetFlat}' — ` +
416
+ `they disagree. 'board' is the source of truth; building for '${board}'. ` +
417
+ `Remove the stale frameworkData.buildTarget entry.`);
418
+ }
419
+ resolved.buildTarget = board;
420
+ }
421
+ else if (typeof buildTargetFlat === "string") {
422
+ resolved.buildTarget = buildTargetFlat;
423
+ }
406
424
  const framework = flat.get("framework");
407
425
  if (typeof framework === "string")
408
426
  resolved.framework = framework;
@@ -424,18 +442,20 @@ export function parseConfigFile(configPath) {
424
442
  warn("'output.optimize' has no effect and is deprecated — remove it. " +
425
443
  "Control optimization via the framework (e.g. zephyr.kconfig CONFIG_SIZE_OPTIMIZATIONS / CONFIG_SPEED_OPTIMIZATIONS).");
426
444
  }
445
+ // The `console` section is removed: the console.* carry-over (console.log
446
+ // lowering to a platform print, plus this section routing/parameterizing it)
447
+ // is gone. Programs write to a serial console explicitly — USB0.writeLine
448
+ // or UART0.writeLine from the board module. Warn-and-drop so existing
449
+ // configs keep building while telling the user to delete the key.
450
+ for (const key of flat.keys()) {
451
+ if (key === "console" || key.startsWith("console.")) {
452
+ warn(`'${key}' is removed — the console.* carry-over is gone. ` +
453
+ "Write to a serial console explicitly (USB0.writeLine(...) / UART0.writeLine(...)) and delete the console section.");
454
+ }
455
+ }
427
456
  const outputOutDir = flat.get("output.outDir");
428
457
  if (typeof outputOutDir === "string")
429
458
  resolved.outputOutDir = outputOutDir;
430
- // Parse console configuration
431
- const consoleBaudRate = flat.get("console.baudRate");
432
- const consolePort = flat.get("console.port");
433
- if (typeof consoleBaudRate === "number" || typeof consolePort === "string") {
434
- resolved.console = {
435
- ...(typeof consoleBaudRate === "number" ? { baudRate: consoleBaudRate } : {}),
436
- ...(typeof consolePort === "string" ? { port: consolePort } : {}),
437
- };
438
- }
439
459
  // Extract structured fields that the flat walker cannot handle.
440
460
  const outputExtraFlags = extractStringArray(configObject, ["output", "extraFlags"], warn);
441
461
  if (outputExtraFlags)
@@ -452,12 +472,16 @@ export function parseConfigFile(configPath) {
452
472
  const zephyrCmakeArgs = extractStringArray(configObject, ["zephyr", "cmakeArgs"], warn);
453
473
  const zephyrRunnerArgs = extractStringArray(configObject, ["zephyr", "runnerArgs"], warn);
454
474
  const zephyrRunner = flat.get("zephyr.runner");
455
- if (zephyrKconfig || zephyrCmakeArgs || zephyrRunnerArgs || typeof zephyrRunner === "string") {
475
+ const zephyrProbe = flat.get("zephyr.probe");
476
+ const zephyrCustomBoard = flat.get("zephyr.customBoard");
477
+ if (zephyrKconfig || zephyrCmakeArgs || zephyrRunnerArgs || typeof zephyrRunner === "string" || typeof zephyrProbe === "string" || typeof zephyrCustomBoard === "boolean") {
456
478
  resolved.zephyrConfig = {
457
479
  ...(zephyrKconfig ? { kconfig: zephyrKconfig } : {}),
458
480
  ...(zephyrCmakeArgs ? { cmakeArgs: zephyrCmakeArgs } : {}),
459
481
  ...(zephyrRunnerArgs ? { runnerArgs: zephyrRunnerArgs } : {}),
482
+ ...(typeof zephyrProbe === "string" ? { probe: zephyrProbe } : {}),
460
483
  ...(typeof zephyrRunner === "string" ? { runner: zephyrRunner } : {}),
484
+ ...(zephyrCustomBoard === true ? { customBoard: true } : {}),
461
485
  };
462
486
  }
463
487
  // Parse display profile config (nested object with profile name, wiring, touch)
@@ -472,8 +496,8 @@ export function parseConfigFile(configPath) {
472
496
  const structuredForValidation = {};
473
497
  if (resolved.target)
474
498
  structuredForValidation.target = resolved.target;
475
- if (resolved.mcu)
476
- structuredForValidation.mcu = resolved.mcu;
499
+ if (resolved.soc)
500
+ structuredForValidation.soc = resolved.soc;
477
501
  if (resolved.board)
478
502
  structuredForValidation.board = resolved.board;
479
503
  if (resolved.contract)
@@ -494,8 +518,6 @@ export function parseConfigFile(configPath) {
494
518
  ...(resolved.outputDefines ? { defines: resolved.outputDefines } : {}),
495
519
  };
496
520
  }
497
- if (resolved.console)
498
- structuredForValidation.console = resolved.console;
499
521
  if (resolved.zephyrConfig)
500
522
  structuredForValidation.zephyr = resolved.zephyrConfig;
501
523
  if (resolved.frameworkConfig)
@@ -528,9 +550,162 @@ export function parseConfigFile(configPath) {
528
550
  * The file is regenerated on every transpiler run so it stays in sync when
529
551
  * the board changes in `cuttlefish.config.ts`.
530
552
  */
553
+ /** Refresh a stale catalog overlay before the board-module check — fs-only
554
+ * discovery, re-walks the tree only when the provenance moved. Errors are
555
+ * swallowed deliberately: an offline or tree-less machine keeps whatever
556
+ * catalog it already has; generation surfaces the real problem. */
557
+ function refreshBoardCatalogQuietly() {
558
+ try {
559
+ ensureFreshBoardCatalog();
560
+ }
561
+ catch {
562
+ // non-fatal by design
563
+ }
564
+ }
565
+ /**
566
+ * Ensure the generated board module (.cuttlefish/board.ts + board.json)
567
+ * is current for a board-target config. Regen-first: any input change —
568
+ * the config's board, the catalog overlay, the Zephyr tree, the generator
569
+ * revision — recreates the module, so the emitted module is
570
+ * project-pinned and diffable, not rebuilt on every compile. One exception:
571
+ * when the module on disk is for a DIFFERENT board than config.board (the
572
+ * user switched boards), it regenerates automatically — a stale module is
573
+ * the split-brain trap, not something to pin.
574
+ */
575
+ function ensureGeneratedBoard(config, cuttlefishDir) {
576
+ const boardTsPath = path.join(cuttlefishDir, "board.ts");
577
+ const boardJsonPath = path.join(cuttlefishDir, "board.json");
578
+ // Regen-first: the board module is a derived artifact, recreated whenever
579
+ // ANY input moves — the config's board target, the catalog overlay, the
580
+ // Zephyr tree the overlay was generated from, or the extraction revision.
581
+ // The check is cheap (fs-only provenance + a fingerprint compare); a
582
+ // stale overlay re-walks the tree once, then this regenerates.
583
+ refreshBoardCatalogQuietly();
584
+ if (fs.existsSync(boardTsPath) && fs.existsSync(boardJsonPath)) {
585
+ try {
586
+ const existing = JSON.parse(fs.readFileSync(boardJsonPath, "utf8"));
587
+ const sameBoard = existing.identifier
588
+ && existing.identifier.toLowerCase() === config.board.toLowerCase();
589
+ const overlay = loadBoardCatalogOverlay();
590
+ const entry = overlay && sameBoard ? findBoardInCatalog(overlay.data, config.board) : undefined;
591
+ const sameSource = entry && overlay
592
+ ? existing.source?.fingerprint
593
+ === boardRecordFingerprint(entry, overlay)
594
+ + factsFingerprint(readUserFactsJson(config) ?? "")
595
+ + factsFingerprint(readAsBuiltJson(config) ?? "")
596
+ : true; // no overlay to verify against — keep the existing module
597
+ if (sameBoard && sameSource)
598
+ return;
599
+ }
600
+ catch {
601
+ // Unreadable manifest — regenerate below rather than build on a
602
+ // module of unknown provenance.
603
+ }
604
+ }
605
+ // Missing files, a board mismatch, or a source change — (re)generate.
606
+ const target = config.board;
607
+ if (!config.framework) {
608
+ throw new Error(`Cannot generate the board module for '${target}' — no framework configured.`);
609
+ }
610
+ // The framework owns board generation; reach its strategy through the
611
+ // configured package (exported as ZephyrStrategy / default). The package
612
+ // is ESM ("type": "module"), so resolve it from the built dist through
613
+ // createRequire against the project's node_modules resolution.
614
+ const generated = loadFrameworkBoardModule(config, configDirOf(config), readUserFactsJson(config), readAsBuiltJson(config));
615
+ if (!generated) {
616
+ throw new Error(`Framework '${config.framework}' cannot generate a board module for '${target}'. ` +
617
+ `Check the target against the framework's board catalog.`);
618
+ }
619
+ // User-facts notes (shadowed harvest routes) surface once per regen —
620
+ // the fingerprint keeps regens rare.
621
+ for (const w of generated.warnings ?? [])
622
+ console.warn(`cuttlefish: ${w}`);
623
+ fs.writeFileSync(boardTsPath, generated.boardTs, "utf-8");
624
+ fs.writeFileSync(boardJsonPath, generated.boardJson, "utf-8");
625
+ }
626
+ /**
627
+ * Force-regenerate the project-local board module — the `cuttlefish board
628
+ * regen` path. Board-target configs only: contract projects write their
629
+ * board module through the contract reader, and native projects have none.
630
+ * Returns the .cuttlefish directory the module was written to.
631
+ */
632
+ export function regenBoardModule(config) {
633
+ if (!config.board || config.contract) {
634
+ throw new Error("'cuttlefish board regen' applies to board-target projects. " +
635
+ "Set 'board:' in cuttlefish.config.ts (contract projects regenerate on build).");
636
+ }
637
+ const cuttlefishDir = path.join(path.dirname(config.configPath), ".cuttlefish");
638
+ if (!fs.existsSync(cuttlefishDir)) {
639
+ fs.mkdirSync(cuttlefishDir, { recursive: true });
640
+ }
641
+ fs.rmSync(path.join(cuttlefishDir, "board.ts"), { force: true });
642
+ fs.rmSync(path.join(cuttlefishDir, "board.json"), { force: true });
643
+ ensureGeneratedBoard(config, cuttlefishDir);
644
+ return cuttlefishDir;
645
+ }
646
+ /** The project's last-build snapshot (.cuttlefish/as-built.json, written by
647
+ * the framework toolchain after each successful west build). Absent →
648
+ * undefined; malformed content is handled downstream (soft ignore). */
649
+ function readAsBuiltJson(config) {
650
+ const p = path.join(path.dirname(config.configPath), ".cuttlefish", "as-built.json");
651
+ if (!fs.existsSync(p))
652
+ return undefined;
653
+ try {
654
+ return fs.readFileSync(p, "utf8");
655
+ }
656
+ catch {
657
+ return undefined; // best-effort enrichment — never blocks generation
658
+ }
659
+ }
660
+ /** The project's raw cuttlefish.facts.json text, when one exists beside the
661
+ * config. Absent file → undefined; present-but-unreadable surfaces. */
662
+ function readUserFactsJson(config) {
663
+ const factsPath = path.join(path.dirname(config.configPath), "cuttlefish.facts.json");
664
+ if (!fs.existsSync(factsPath))
665
+ return undefined;
666
+ try {
667
+ return fs.readFileSync(factsPath, "utf8");
668
+ }
669
+ catch (err) {
670
+ throw new Error(`cuttlefish.facts.json exists but cannot be read: ${err.message}`);
671
+ }
672
+ }
673
+ /** Resolve the configured framework package's board generator. The framework
674
+ * packages are ESM, so this imports their built entry from the resolving
675
+ * project's node_modules (createRequire against the project dir).
676
+ * @param configDir the project directory (where node_modules resolution starts) */
677
+ function configDirOf(config) {
678
+ return path.dirname(config.configPath);
679
+ }
680
+ function loadFrameworkBoardModule(config, configDir, factsJson, asBuiltJson) {
681
+ const target = config.board;
682
+ const spec = config.framework;
683
+ const tryResolve = (anchor) => {
684
+ try {
685
+ const projectRequire = createRequire(anchor);
686
+ const mod = projectRequire(projectRequire.resolve(spec));
687
+ const Ctor = mod.ZephyrStrategy ?? mod.default;
688
+ const strategy = typeof Ctor === "function" ? new Ctor() : undefined;
689
+ const opts = {};
690
+ if (factsJson !== undefined)
691
+ opts.factsJson = factsJson;
692
+ if (asBuiltJson !== undefined)
693
+ opts.asBuiltJson = asBuiltJson;
694
+ return strategy?.generateBoardModule?.(target, Object.keys(opts).length > 0 ? opts : undefined) ?? undefined;
695
+ }
696
+ catch {
697
+ return undefined;
698
+ }
699
+ };
700
+ // Resolve from the project's package.json when it has one; projects without
701
+ // one (bare configs, tests) fall back to cuttlefish's own resolution — the
702
+ // framework is a peer of the tool in every real layout.
703
+ return (tryResolve(path.join(configDir, "package.json")) ??
704
+ tryResolve(path.join(path.dirname(fileURLToPath(import.meta.url)), "package.json")));
705
+ }
531
706
  export function generateVirtualTypeDeclaration(config, platformDeclarations) {
532
- if (!config.mcu && !config.board) {
533
- // For native targets with no board/mcu, still generate declarations
707
+ if (!config.board && !config.soc) {
708
+ // For native targets with no board, still generate declarations
534
709
  if (!platformDeclarations || platformDeclarations.length === 0)
535
710
  return;
536
711
  }
@@ -543,22 +718,23 @@ export function generateVirtualTypeDeclaration(config, platformDeclarations) {
543
718
  }
544
719
  // Determine what @typecad/board exports
545
720
  let boardExport = "";
546
- if (config.contract) {
547
- // Contract-based: export from generated board (same dir — .cuttlefish/)
721
+ if (config.contract || config.board) {
722
+ // Board-target or contract project: the project-local generated board
723
+ // module (.cuttlefish/board.ts) — produced by boardgen (board target)
724
+ // or the contract reader (soc + contract), never a package.
548
725
  boardExport = "export * from './board.js';";
549
726
  }
550
- else if (config.board) {
551
- // Explicit board package (legacy)
552
- boardExport = `export * from '${config.board}';`;
553
- }
554
- else if (config.mcu) {
555
- // MCU-only: export all from MCU
556
- boardExport = `export * from '${config.mcu}';`;
557
- }
558
727
  else {
559
- // Native target with no board/mcu: empty export
728
+ // Native target with no board: empty export
560
729
  boardExport = "";
561
730
  }
731
+ // Board-target projects: ensure the generated board module exists. The
732
+ // framework owns the generation (the Zephyr framework joins its board
733
+ // data pack with its curated soc descriptors) — reach it through the
734
+ // configured framework package's strategy hook.
735
+ if (config.board && !config.contract) {
736
+ ensureGeneratedBoard(config, cuttlefishDir);
737
+ }
562
738
  const content = [
563
739
  "// ---------------------------------------------------------------------------",
564
740
  "// cuttlefish-env.d.ts — Virtual module declaration for '@typecad/board'",
@@ -566,7 +742,7 @@ export function generateVirtualTypeDeclaration(config, platformDeclarations) {
566
742
  "// Auto-generated by the cuttlefish transpiler. Do not edit manually.",
567
743
  "// To change the board, update cuttlefish.config.ts and re-run the transpiler.",
568
744
  "//",
569
- `// Board: ${config.board ?? config.mcu ?? "native"}`,
745
+ `// Board: ${config.board ?? config.soc ?? "native"}`,
570
746
  "// ---------------------------------------------------------------------------",
571
747
  "",
572
748
  "declare global {",
@@ -602,30 +778,10 @@ export function generateVirtualTypeDeclaration(config, platformDeclarations) {
602
778
  " type float = number;",
603
779
  " type double = number;",
604
780
  "",
605
- " // console — declared here (not pulled from lib.dom) so a project does not",
606
- " // need \"dom\" in tsconfig lib just to type console.log. Avoiding lib.dom",
607
- " // also keeps DOM global type names (Node, Element, Event, Document, ...)",
608
- " // out of scope, so a user class named e.g. `Node` is not shadowed by the",
609
- " // DOM global of the same name. Platforms may declaration-merge extra",
610
- " // members onto this interface via ambientTypeDeclarations().",
611
- " interface Console {",
612
- " log(...args: unknown[]): void;",
613
- " info(...args: unknown[]): void;",
614
- " debug(...args: unknown[]): void;",
615
- " warn(...args: unknown[]): void;",
616
- " error(...args: unknown[]): void;",
617
- " }",
618
- " const console: Console;",
619
- "",
620
781
  " // Convenience helper for volatile variables in TypeCAD programs.",
621
782
  " // The transpiler detects calls to volatile() and emits the C++ volatile qualifier.",
622
783
  " declare function volatile<T>(value: T): T;",
623
784
  "",
624
- " // JS-style timers",
625
- " declare function setInterval(handler: () => void, timeout?: number): number;",
626
- " declare function setTimeout(handler: () => void, timeout?: number): number;",
627
- " declare function clearInterval(id: number): void;",
628
- " declare function clearTimeout(id: number): void;",
629
785
  ...(platformDeclarations ?? []),
630
786
  "}",
631
787
  "",
@@ -640,28 +796,34 @@ export function generateVirtualTypeDeclaration(config, platformDeclarations) {
640
796
  "",
641
797
  ].join("\n");
642
798
  fs.writeFileSync(outPath, content, "utf-8");
643
- // Also write a `.cuttlefish/board.ts` re-export. The scaffolded tsconfig.json
644
- // path mapping points the virtual specifier ("@typecad/board") at this file so
645
- // the TypeScript language server and the transpiler's type-check resolve
646
- // `import { ... } from '@typecad/board'` to the configured board — which
647
- // re-exports the HAL plus board-specific pins. Without this, the ambient
648
- // `declare module` in cuttlefish-env.d.ts is treated as module augmentation
649
- // (because that file has `export {}`), which can't define a new module.
650
- //
651
- // For contract-based configs, the narrowed board.ts is generated by the
652
- // contract reader (`contract/board-generator.ts`, invoked from cli.ts before
653
- // this function runs), so don't overwrite it with the generic re-export here.
654
- if (!config.contract && (config.board || config.mcu)) {
655
- const reExportSource = config.board ?? config.mcu;
656
- const boardTsPath = path.join(cuttlefishDir, "board.ts");
657
- const boardTsContent = [
658
- `// Auto-generated by the cuttlefish transpiler. Do not edit manually.`,
659
- `// Re-exports the configured board/MCU package so the tsconfig path`,
660
- `// mapping for the virtual "@typecad/board" specifier resolves correctly.`,
661
- `export * from '${reExportSource}';`,
662
- ``,
663
- ].join("\n");
664
- fs.writeFileSync(boardTsPath, boardTsContent, "utf-8");
799
+ // The .cuttlefish/board.ts module itself is written by ensureGeneratedBoard
800
+ // (board-target configs, above) or the contract reader (invoked from cli.ts
801
+ // before this function runs) never a package re-export here. The
802
+ // scaffolded tsconfig path mapping points the virtual specifier
803
+ // ("@typecad/board") at that file, and the ambient declare module in
804
+ // cuttlefish-env.d.ts mirrors it for the language server.
805
+ // A project-local test-pins.json (board packages are gone projects that
806
+ // want role pins carry them) gets a sibling .cuttlefish/test-pins.ts
807
+ // re-export, so the language server resolves the '@typecad/test-pins'
808
+ // virtual specifier to the same role consts the transpiler generates.
809
+ if (config.board) {
810
+ const projectTestPins = path.join(configDir, "test-pins.json");
811
+ const testPinsData = fs.existsSync(projectTestPins)
812
+ ? readTestPinsFile(configDir)
813
+ : undefined;
814
+ const testPinsContent = testPinsData
815
+ ? buildTestPinsModuleContent(config.board, testPinsData)
816
+ : undefined;
817
+ if (testPinsContent) {
818
+ const testPinsTsPath = path.join(cuttlefishDir, "test-pins.ts");
819
+ const existing = fs.existsSync(testPinsTsPath)
820
+ ? fs.readFileSync(testPinsTsPath, "utf8")
821
+ : undefined;
822
+ const withHeader = testPinsContent.replace(/^\/\/ Generated from/, "// Auto-generated by the cuttlefish transpiler. Do not edit manually.\n// Generated from");
823
+ if (existing !== withHeader) {
824
+ fs.writeFileSync(testPinsTsPath, withHeader, "utf-8");
825
+ }
826
+ }
665
827
  }
666
828
  }
667
829
  /**
@@ -7,8 +7,8 @@ import { z } from 'zod';
7
7
  export declare const CuttlefishConfigSchema: z.ZodObject<{
8
8
  entry: z.ZodOptional<z.ZodString>;
9
9
  target: z.ZodOptional<z.ZodString>;
10
- mcu: z.ZodOptional<z.ZodString>;
11
10
  board: z.ZodOptional<z.ZodString>;
11
+ soc: z.ZodOptional<z.ZodString>;
12
12
  contract: z.ZodOptional<z.ZodString>;
13
13
  framework: z.ZodOptional<z.ZodString>;
14
14
  psram: z.ZodOptional<z.ZodEnum<{
@@ -36,17 +36,15 @@ export declare const CuttlefishConfigSchema: z.ZodObject<{
36
36
  type: z.ZodOptional<z.ZodString>;
37
37
  frameworkOptions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
38
38
  }, z.core.$strict>>;
39
- console: z.ZodOptional<z.ZodObject<{
40
- baudRate: z.ZodOptional<z.ZodNumber>;
41
- port: z.ZodOptional<z.ZodString>;
42
- }, z.core.$strict>>;
43
39
  native: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
44
40
  display: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
45
41
  zephyr: z.ZodOptional<z.ZodObject<{
46
42
  kconfig: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
47
43
  cmakeArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
44
+ probe: z.ZodOptional<z.ZodString>;
48
45
  runner: z.ZodOptional<z.ZodString>;
49
46
  runnerArgs: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
+ customBoard: z.ZodOptional<z.ZodBoolean>;
50
48
  }, z.core.$strict>>;
51
49
  }, z.core.$strict>;
52
50
  /** Inferred TypeScript type from the Zod schema. */
@@ -15,11 +15,6 @@ const OutputConfig = z.object({
15
15
  defines: z.record(z.string(), z.string()).optional(),
16
16
  extraFlags: z.array(z.string()).optional(),
17
17
  }).strict();
18
- /** Schema for the `console` section. */
19
- const ConsoleConfig = z.object({
20
- baudRate: z.number().int().positive().optional(),
21
- port: z.string().optional(),
22
- }).strict();
23
18
  /** Schema for the `test` section. */
24
19
  const TestConfig = z.object({
25
20
  include: z.array(z.string()).optional(),
@@ -38,8 +33,14 @@ const ToolchainConfig = z.object({
38
33
  const ZephyrConfig = z.object({
39
34
  kconfig: z.record(z.string(), z.string()).optional(),
40
35
  cmakeArgs: z.array(z.string()).optional(),
36
+ probe: z.string().optional(),
41
37
  runner: z.string().optional(),
42
38
  runnerArgs: z.array(z.string()).optional(),
39
+ /** Generate an out-of-tree Zephyr board for an MCU-only target (no board
40
+ * package): the framework emits boards/typecad/<buildTarget>/ from the
41
+ * MCU package's silicon data (SoC, console, clock plan). The board is
42
+ * named after `frameworkData.buildTarget`. */
43
+ customBoard: z.boolean().optional(),
43
44
  }).strict();
44
45
  /**
45
46
  * Schema for the full CuttlefishConfig shape.
@@ -49,8 +50,8 @@ const ZephyrConfig = z.object({
49
50
  export const CuttlefishConfigSchema = z.object({
50
51
  entry: z.string().optional(),
51
52
  target: z.string().min(1).optional(),
52
- mcu: z.string().min(1).optional(),
53
53
  board: z.string().optional(),
54
+ soc: z.string().optional(),
54
55
  contract: z.string().optional(),
55
56
  framework: z.string().optional(),
56
57
  /** ESP32 PSRAM type. When set, the framework emits the PSRAM-enabling
@@ -64,13 +65,12 @@ export const CuttlefishConfigSchema = z.object({
64
65
  exclude: z.array(z.string()).optional(),
65
66
  test: TestConfig.optional(),
66
67
  toolchain: ToolchainConfig.optional(),
67
- console: ConsoleConfig.optional(),
68
68
  native: z.record(z.string(), z.unknown()).optional(),
69
69
  /** Display profile config (driver, wiring, touch) — loosely typed here so
70
70
  * the loader's presence/shape extraction round-trips through validation. */
71
71
  display: z.record(z.string(), z.unknown()).optional(),
72
72
  zephyr: ZephyrConfig.optional(),
73
- }).strict().refine(data => !(data.board && data.contract), {
73
+ }).strict().refine(data => !(data.board && (data.contract || data.soc)), {
74
74
  message: "Specifying both 'board' and 'contract' is not allowed. Choose one.",
75
75
  path: ['board'],
76
76
  });
@@ -3,11 +3,11 @@ export declare const CUTTLEFISH_DIR = ".cuttlefish";
3
3
  export interface GenerateBoardOptions {
4
4
  /** Absolute project root dir (where .cuttlefish/ lives). */
5
5
  projectDir: string;
6
- /** MCU package specifier, e.g. '@typecad/mcu-atmega328p'. */
7
- mcuPackage: string;
8
- /** Matched MCU pin names to re-export (e.g. ['PB5','PC4']). */
6
+ /** The Zephyr SoC name the contract narrows (e.g. 'stm32f411xe'). */
7
+ soc: string;
8
+ /** Matched pin names to export (e.g. ['PB5','PC4']). */
9
9
  connectedPins: string[];
10
- /** Selected peripheral instance names to re-export (e.g. ['I2C0']). */
10
+ /** Selected peripheral instance names to export (e.g. ['I2C0']). */
11
11
  peripherals: string[];
12
12
  }
13
13
  /**
@@ -17,6 +17,7 @@
17
17
  // ---------------------------------------------------------------------------
18
18
  import fs from 'node:fs';
19
19
  import path from 'node:path';
20
+ const BS_DOT = /\./g;
20
21
  /** The directory (relative to the project root) where board.ts is generated. */
21
22
  export const CUTTLEFISH_DIR = '.cuttlefish';
22
23
  /**
@@ -24,7 +25,7 @@ export const CUTTLEFISH_DIR = '.cuttlefish';
24
25
  * @returns the absolute path to the generated file.
25
26
  */
26
27
  export function generateBoardFile(opts) {
27
- const { projectDir, mcuPackage, connectedPins, peripherals } = opts;
28
+ const { projectDir, soc, connectedPins, peripherals } = opts;
28
29
  const cuttlefishDir = path.join(projectDir, CUTTLEFISH_DIR);
29
30
  if (!fs.existsSync(cuttlefishDir)) {
30
31
  fs.mkdirSync(cuttlefishDir, { recursive: true });
@@ -34,21 +35,31 @@ export function generateBoardFile(opts) {
34
35
  '// Do not edit manually — regenerate by re-running cuttlefish.',
35
36
  '//',
36
37
  '// This board exposes only the pins and peripherals that the source contract',
37
- `// (${mcuPackage}) declares as connected on the PCB. Pins the board did not wire`,
38
+ `// (soc ${soc}) declares as connected on the PCB. Pins the board did not wire`,
38
39
  '// are intentionally absent: using one is a compile error.',
39
40
  '',
40
41
  '// Full HAL runtime surface (constants, timing, math, GPIO/bus helpers).',
41
- "export * from '@typecad/hal';",
42
+ "import { Pin, I2CBus, SPIBus, UART } from '@typecad/hal';",
42
43
  '',
43
44
  '// Narrowed pin set — only pins the contract declares connected.',
44
45
  ];
45
46
  if (connectedPins.length > 0) {
46
- lines.push(`export { ${connectedPins.join(', ')} } from '${mcuPackage}';`);
47
+ for (const pin of connectedPins) {
48
+ const ident = pin.replace(BS_DOT, '_');
49
+ lines.push(`export const ${ident} = Pin.fromPort('${pin}');`);
50
+ }
47
51
  }
48
52
  if (peripherals.length > 0) {
49
53
  lines.push('');
50
54
  lines.push('// Narrowed peripheral set — only buses whose pins are wired on the PCB.');
51
- lines.push(`export { ${peripherals.join(', ')} } from '${mcuPackage}';`);
55
+ for (const p of peripherals) {
56
+ if (p.startsWith('I2C'))
57
+ lines.push(`export const ${p} = new I2CBus('${p}');`);
58
+ else if (p.startsWith('SPI'))
59
+ lines.push(`export const ${p} = new SPIBus('${p}');`);
60
+ else if (p.startsWith('UART'))
61
+ lines.push(`export const ${p} = new UART('${p}');`);
62
+ }
52
63
  }
53
64
  lines.push(''); // trailing newline
54
65
  const outPath = path.join(cuttlefishDir, 'board.ts');