@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
@@ -1,177 +0,0 @@
1
- // `cuttlefish install` — installs a chosen @typecad/framework-* package into the
2
- // current project. The flow asks which board to target first, then narrows the
3
- // framework choices to those compatible with that board (see framework-catalog).
4
- // A framework id may be passed directly (`cuttlefish install arduino`) to skip
5
- // the prompts entirely, which also makes the command usable in CI.
6
- import * as readline from "node:readline/promises";
7
- import { stdin as input, stdout as output } from "node:process";
8
- import { spawnSync } from "node:child_process";
9
- import chalk from "chalk";
10
- import { KNOWN_TARGETS } from "../create/index.js";
11
- import { FRAMEWORK_CATALOG, frameworkCatalogEntry, frameworksForTarget, detectPackageManager, buildInstallCommand, } from "./framework-catalog.js";
12
- // ── prompts (mirror the create wizard's style for a consistent UX) ──────────
13
- async function promptSelect(rl, prompt, options) {
14
- console.log(`${chalk.cyan("?")} ${prompt}:`);
15
- for (let i = 0; i < options.length; i++) {
16
- console.log(` ${chalk.dim(`${i + 1})`)} ${options[i].label}`);
17
- }
18
- while (true) {
19
- const answer = await rl.question(` Enter number (1-${options.length}): `);
20
- const idx = parseInt(answer.trim(), 10) - 1;
21
- if (idx >= 0 && idx < options.length) {
22
- return options[idx].value;
23
- }
24
- console.log(` ${chalk.red("✗")} Please enter a number between 1 and ${options.length}.`);
25
- }
26
- }
27
- async function selectBoardInteractively(rl) {
28
- const options = KNOWN_TARGETS.map((t) => ({
29
- label: t.isNative
30
- ? `${t.displayName} (Windows/Linux executable)`
31
- : `${t.displayName} (${t.architecture.toUpperCase()})`,
32
- value: t.id,
33
- }));
34
- const selectedId = await promptSelect(rl, "Target board", options);
35
- return KNOWN_TARGETS.find((t) => t.id === selectedId);
36
- }
37
- /**
38
- * Pick a framework for `target`. When only one framework is compatible with the
39
- * board it is auto-selected (mirrors the create wizard's single-option path) so
40
- * the user isn't asked a question with one answer.
41
- */
42
- async function selectFrameworkInteractively(rl, target, compatible) {
43
- if (compatible.length === 1) {
44
- const only = compatible[0];
45
- console.log(`${chalk.cyan("?")} Framework: ${chalk.white(only.label)} ${chalk.dim(`(only option for ${target.id})`)}`);
46
- return only;
47
- }
48
- const selectedId = await promptSelect(rl, "Framework", compatible.map((f) => ({ label: f.label, value: f.id })));
49
- return compatible.find((f) => f.id === selectedId);
50
- }
51
- let testRunner;
52
- /**
53
- * FOR TESTS ONLY. Replaces the real spawn-based installer with `runner`.
54
- * Pass `undefined` to restore the real executor.
55
- */
56
- export function __setInstallRunnerForTest(runner) {
57
- testRunner = runner;
58
- }
59
- function runRealInstall(cmd) {
60
- // stdio: "inherit" streams the package manager's own output to the terminal
61
- // (install progress, deprecation warnings, etc.). On failure the user has
62
- // already seen the details above, so we only need to report the exit code.
63
- const result = spawnSync(cmd.bin, cmd.args, {
64
- cwd: cmd.cwd,
65
- stdio: "inherit",
66
- });
67
- if (result.error) {
68
- const errno = result.error.code;
69
- return {
70
- status: null,
71
- launchError: errno === "ENOENT" ? `'${cmd.bin}' not found on PATH` : String(result.error),
72
- };
73
- }
74
- return { status: result.status };
75
- }
76
- /**
77
- * Resolve the package manager and run the install for `entry`'s package. In
78
- * dry-run mode the command is printed but not executed. Throws on launch
79
- * failure or non-zero exit so the CLI surfaces a clear error.
80
- */
81
- export function installFrameworkPackage(entry, opts) {
82
- const pm = detectPackageManager(opts.cwd);
83
- const { bin, args } = buildInstallCommand(pm, entry.packageName);
84
- if (opts.dryRun) {
85
- console.log(`${chalk.cyan("$")} ${bin} ${args.join(" ")}`);
86
- return { pm, bin, args };
87
- }
88
- const cmd = { bin, args, cwd: opts.cwd };
89
- const run = testRunner ?? runRealInstall;
90
- const result = run(cmd);
91
- if (result.launchError) {
92
- throw new Error(`Failed to run '${bin}': ${result.launchError}`);
93
- }
94
- if (result.status !== 0) {
95
- throw new Error(`'${bin} ${args.join(" ")}' exited with code ${result.status}. See the package manager output above for details.`);
96
- }
97
- return { pm, bin, args };
98
- }
99
- // ── command entry point ─────────────────────────────────────────────────────
100
- export async function handleInstall(options) {
101
- const cwd = process.cwd();
102
- // 1. Resolve which framework to install.
103
- let entry;
104
- if (options.framework) {
105
- const resolved = frameworkCatalogEntry(options.framework);
106
- if (!resolved) {
107
- const available = FRAMEWORK_CATALOG.filter((f) => f.installable).map((f) => f.id).join(", ");
108
- throw new Error(`Unknown framework '${options.framework}'. Available: ${available}`);
109
- }
110
- if (!resolved.installable) {
111
- throw new Error(`Framework '${options.framework}' has no published package yet and cannot be installed.`);
112
- }
113
- entry = resolved;
114
- console.log(`${chalk.cyan("?")} Framework: ${chalk.white(entry.label)}`);
115
- }
116
- else {
117
- // Resolve the target board. --board skips the board prompt; otherwise the
118
- // board is chosen interactively (which requires a TTY — handled below).
119
- let target;
120
- if (options.board) {
121
- const found = KNOWN_TARGETS.find((t) => t.id === options.board);
122
- if (!found) {
123
- const available = KNOWN_TARGETS.map((t) => t.id).join(", ");
124
- throw new Error(`Unknown board '${options.board}'. Available: ${available}`);
125
- }
126
- target = found;
127
- console.log(`${chalk.cyan("?")} Board: ${chalk.white(target.displayName)} (${chalk.dim(target.id)})`);
128
- }
129
- // Narrow to the frameworks compatible with that board.
130
- let compatible;
131
- if (target) {
132
- compatible = frameworksForTarget(target).filter((f) => f.installable);
133
- if (compatible.length === 0) {
134
- throw new Error(`No installable frameworks are compatible with board '${target.id}'.`);
135
- }
136
- }
137
- if (compatible && compatible.length === 1) {
138
- // Only one framework fits this board — auto-select it (no prompt, so this
139
- // path also works under CI / non-interactive stdin).
140
- entry = compatible[0];
141
- console.log(`${chalk.cyan("?")} Framework: ${chalk.white(entry.label)} ${chalk.dim(`(only option for ${target.id})`)}`);
142
- }
143
- else {
144
- // Need to prompt — for the board (if --board wasn't given) and/or for the
145
- // framework (when 2+ are compatible). Refuse to hang on a readline that
146
- // can't be answered (e.g. piped stdin under CI).
147
- if (!process.stdin.isTTY) {
148
- throw new Error("No framework specified and stdin is not interactive. " +
149
- "Pass a framework id (e.g. 'cuttlefish install arduino'), " +
150
- "or a board with a single compatible framework (e.g. '--board arduino-uno').");
151
- }
152
- const rl = readline.createInterface({ input, output });
153
- try {
154
- if (!target) {
155
- target = await selectBoardInteractively(rl);
156
- compatible = frameworksForTarget(target).filter((f) => f.installable);
157
- if (compatible.length === 0) {
158
- throw new Error(`No installable frameworks are compatible with board '${target.id}'.`);
159
- }
160
- }
161
- entry = await selectFrameworkInteractively(rl, target, compatible);
162
- }
163
- finally {
164
- rl.close();
165
- }
166
- }
167
- }
168
- // 2. Install it.
169
- console.log(`\n${chalk.cyan("⤳")} Installing ${chalk.white(entry.packageName)} into ${chalk.dim(cwd)}…`);
170
- const result = installFrameworkPackage(entry, { cwd, dryRun: options.dryRun });
171
- if (options.dryRun) {
172
- console.log(chalk.dim("(dry-run — nothing was installed)"));
173
- return;
174
- }
175
- console.log(`\n${chalk.green("✓")} Installed ${chalk.white(entry.packageName)} via ${result.pm}.`);
176
- console.log(chalk.dim(`Next: run 'cuttlefish create' and pick ${entry.id}, or set framework: "${entry.packageName}" in cuttlefish.config.ts.`));
177
- }
@@ -1,4 +0,0 @@
1
- export { handleInstall, installFrameworkPackage, __setInstallRunnerForTest } from "./handle-install.js";
2
- export type { InstallResult } from "./handle-install.js";
3
- export { FRAMEWORK_CATALOG, frameworkCatalogEntry, frameworksForTarget, detectPackageManager, buildInstallCommand, } from "./framework-catalog.js";
4
- export type { FrameworkCatalogEntry, BoardLike, PackageManager, } from "./framework-catalog.js";
@@ -1,3 +0,0 @@
1
- // `cuttlefish install` command — install a @typecad/framework-* package.
2
- export { handleInstall, installFrameworkPackage, __setInstallRunnerForTest } from "./handle-install.js";
3
- export { FRAMEWORK_CATALOG, frameworkCatalogEntry, frameworksForTarget, detectPackageManager, buildInstallCommand, } from "./framework-catalog.js";
@@ -1,58 +0,0 @@
1
- import type { ProgramIR } from "../api/index.js";
2
- export type TrackedPinLevel = 'high' | 'low' | 'unknown';
3
- /** C++ identifier for a pin's shadow state variable. */
4
- export declare function pinShadowVarName(pin: number): string;
5
- export declare function resetPinStateTracking(): void;
6
- /** Record gpio.set_mode — OUTPUT* modes enable tracking, anything else ends it. */
7
- export declare function notePinSetMode(pin: number, mode: string | null): void;
8
- /** Record gpio.write — literal values set the level; expressions make it unknown. */
9
- export declare function notePinWrite(pin: number, literalValue: number | boolean | null): void;
10
- /** Record gpio.toggle — inverts a known level, keeps unknown as unknown. */
11
- export declare function notePinToggle(pin: number): void;
12
- /**
13
- * Record PWM/tone output — the pin no longer has a digital level, so any
14
- * tracked level goes unknown (reads must not fold to the pre-PWM literal).
15
- * A later digital write/toggle restores digital tracking.
16
- */
17
- export declare function notePinAnalogOutput(pin: number): void;
18
- /**
19
- * Resolve a read on a pin at the current program point.
20
- * Returns 'high'/'low' when the level folds to a constant, 'shadow' when a
21
- * shadow variable must carry it, or null when the pin is not a tracked
22
- * output pin (unconfigured or input mode → normal hardware read lowering).
23
- */
24
- export declare function resolveTrackedRead(pin: number): 'high' | 'low' | 'shadow' | null;
25
- /** Whether write/toggle ops on this pin must also update its shadow variable. */
26
- export declare function isShadowPin(pin: number): boolean;
27
- /** Consume the shadow declarations owed by this build (pin + initializer).
28
- * Clears the shadow set: emit reads the updatesShadow flags baked into the
29
- * ops below, never this module's live state (each file's build resets the
30
- * tracker, and all files finish building before any emit runs). */
31
- export declare function takeShadowDeclarations(): {
32
- pin: number;
33
- initial: boolean;
34
- }[];
35
- /** Mark every gpio.write / gpio.toggle op in the program whose pin has a
36
- * tracked shadow read, so the emitter appends the shadow-variable update.
37
- * Runs once at the end of buildProgramIR, after the whole file is lowered —
38
- * at that point the shadow set is final, so writes that were lowered BEFORE
39
- * the first shadow read are marked too (their ops are already in the IR).
40
- * This is what makes emit independent of tracker state across files. */
41
- export declare function markShadowUpdatingOps(program: ProgramIR): void;
42
- export type PinLevelSnapshot = Map<number, TrackedPinLevel>;
43
- export declare function snapshotPinLevels(): PinLevelSnapshot;
44
- export declare function restorePinLevels(snapshot: PinLevelSnapshot): void;
45
- /**
46
- * Merge two branch-end states into the live state: pins that agree keep
47
- * their level; pins that differ (or exist in only one branch) become
48
- * unknown. Pass the pre-branch state as one side when there is no else
49
- * branch, so pins written in the taken branch invalidate.
50
- */
51
- export declare function mergePinLevels(a: PinLevelSnapshot, b: PinLevelSnapshot): void;
52
- /** Invalidate all known levels (after loop/switch bodies, which may run any
53
- * number of times). Shadow tracking is unaffected — it is runtime truth. */
54
- export declare function invalidatePinLevels(): void;
55
- /** Disable/enable constant folding around function and callback bodies. */
56
- export declare function setPinFoldingEnabled(enabled: boolean): void;
57
- /** Whether pin-level constant folding is currently allowed. */
58
- export declare function isPinFoldingEnabled(): boolean;
@@ -1,182 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Output Pin State Tracking
3
- //
4
- // Tracks the driven level of pins configured as OUTPUT so that
5
- // OutputPin.read()/isHigh()/isLow() can be answered without a hardware pin
6
- // read. Reading back an OUTPUT-only pin is not portable (Zephyr's
7
- // gpio_pin_get reads the input latch, which is undefined for direction-only
8
- // outputs), so the transpiler instead maintains the state itself:
9
- //
10
- // - When the level is statically known at the read site (asOutput(false)
11
- // followed by literal high/low/write/toggle calls), the read folds to a
12
- // compile-time constant.
13
- // - When it is not (runtime-valued writes, loops, branches, function
14
- // bodies), the read lowers to a shadow variable (`__tc_pin_state_<n>`)
15
- // that every generated write/toggle on that pin keeps updated.
16
- //
17
- // The tracker is updated in program order during IR lowering (the same pass
18
- // that resolves HAL method bodies), so the state seen at each read site is
19
- // the state at that point in the source. Control-flow joins and loop bodies
20
- // conservatively invalidate levels (see control-flow.ts hooks); function and
21
- // callback bodies disable folding entirely and force the shadow form.
22
- // ---------------------------------------------------------------------------
23
- import { walkProgramIR, walkExpressions } from "./utils/walk-ir.js";
24
- /** Pin numbers explicitly configured as OUTPUT via gpio.set_mode. */
25
- const outputPins = new Set();
26
- /** Last driven level per output pin, at the current program point. */
27
- const pinLevels = new Map();
28
- /** Output pins whose reads lowered to the shadow variable form. */
29
- const shadowPins = new Set();
30
- /** Initializer for each shadow variable: the statically-known level at the
31
- * time the first shadow read was resolved, or `false` when unknown. */
32
- const shadowInitial = new Map();
33
- /** Folding is disabled inside function/callback bodies, where the top-level
34
- * program-point state does not apply at runtime. */
35
- let foldingEnabled = true;
36
- /** C++ identifier for a pin's shadow state variable. */
37
- export function pinShadowVarName(pin) {
38
- return `__tc_pin_state_${pin}`;
39
- }
40
- export function resetPinStateTracking() {
41
- outputPins.clear();
42
- pinLevels.clear();
43
- shadowPins.clear();
44
- shadowInitial.clear();
45
- foldingEnabled = true;
46
- }
47
- /** Record gpio.set_mode — OUTPUT* modes enable tracking, anything else ends it. */
48
- export function notePinSetMode(pin, mode) {
49
- const isOutput = mode !== null && /output/i.test(mode);
50
- if (isOutput) {
51
- outputPins.add(pin);
52
- pinLevels.set(pin, 'unknown');
53
- }
54
- else {
55
- outputPins.delete(pin);
56
- pinLevels.delete(pin);
57
- }
58
- }
59
- /** Record gpio.write — literal values set the level; expressions make it unknown. */
60
- export function notePinWrite(pin, literalValue) {
61
- if (!outputPins.has(pin))
62
- return;
63
- pinLevels.set(pin, literalValue === null ? 'unknown' : (literalValue ? 'high' : 'low'));
64
- }
65
- /** Record gpio.toggle — inverts a known level, keeps unknown as unknown. */
66
- export function notePinToggle(pin) {
67
- if (!outputPins.has(pin))
68
- return;
69
- const level = pinLevels.get(pin);
70
- if (level === 'high')
71
- pinLevels.set(pin, 'low');
72
- else if (level === 'low')
73
- pinLevels.set(pin, 'high');
74
- else
75
- pinLevels.set(pin, 'unknown');
76
- }
77
- /**
78
- * Record PWM/tone output — the pin no longer has a digital level, so any
79
- * tracked level goes unknown (reads must not fold to the pre-PWM literal).
80
- * A later digital write/toggle restores digital tracking.
81
- */
82
- export function notePinAnalogOutput(pin) {
83
- if (!outputPins.has(pin))
84
- return;
85
- pinLevels.set(pin, 'unknown');
86
- }
87
- /**
88
- * Resolve a read on a pin at the current program point.
89
- * Returns 'high'/'low' when the level folds to a constant, 'shadow' when a
90
- * shadow variable must carry it, or null when the pin is not a tracked
91
- * output pin (unconfigured or input mode → normal hardware read lowering).
92
- */
93
- export function resolveTrackedRead(pin) {
94
- if (!outputPins.has(pin))
95
- return null;
96
- const level = pinLevels.get(pin);
97
- if (foldingEnabled && (level === 'high' || level === 'low'))
98
- return level;
99
- shadowPins.add(pin);
100
- if (!shadowInitial.has(pin))
101
- shadowInitial.set(pin, level === 'high');
102
- return 'shadow';
103
- }
104
- /** Whether write/toggle ops on this pin must also update its shadow variable. */
105
- export function isShadowPin(pin) {
106
- return shadowPins.has(pin);
107
- }
108
- /** Consume the shadow declarations owed by this build (pin + initializer).
109
- * Clears the shadow set: emit reads the updatesShadow flags baked into the
110
- * ops below, never this module's live state (each file's build resets the
111
- * tracker, and all files finish building before any emit runs). */
112
- export function takeShadowDeclarations() {
113
- const decls = [...shadowPins].sort((a, b) => a - b).map(pin => ({
114
- pin,
115
- initial: shadowInitial.get(pin) ?? false,
116
- }));
117
- shadowPins.clear();
118
- shadowInitial.clear();
119
- return decls;
120
- }
121
- /** Mark every gpio.write / gpio.toggle op in the program whose pin has a
122
- * tracked shadow read, so the emitter appends the shadow-variable update.
123
- * Runs once at the end of buildProgramIR, after the whole file is lowered —
124
- * at that point the shadow set is final, so writes that were lowered BEFORE
125
- * the first shadow read are marked too (their ops are already in the IR).
126
- * This is what makes emit independent of tracker state across files. */
127
- export function markShadowUpdatingOps(program) {
128
- if (shadowPins.size === 0)
129
- return;
130
- const markOp = (op) => {
131
- const halOp = op;
132
- if (!halOp)
133
- return;
134
- if ((halOp.operation === "gpio.write" || halOp.operation === "gpio.toggle") && isShadowPin(halOp.pin)) {
135
- halOp.updatesShadow = true;
136
- }
137
- };
138
- walkProgramIR(program, (stmt) => {
139
- if (stmt.kind === "hal-op")
140
- markOp(stmt.operation);
141
- walkExpressions([stmt], (expr) => {
142
- if (expr.kind === "hal-expr")
143
- markOp(expr.operation);
144
- });
145
- });
146
- }
147
- export function snapshotPinLevels() {
148
- return new Map(pinLevels);
149
- }
150
- export function restorePinLevels(snapshot) {
151
- pinLevels.clear();
152
- for (const [pin, level] of snapshot)
153
- pinLevels.set(pin, level);
154
- }
155
- /**
156
- * Merge two branch-end states into the live state: pins that agree keep
157
- * their level; pins that differ (or exist in only one branch) become
158
- * unknown. Pass the pre-branch state as one side when there is no else
159
- * branch, so pins written in the taken branch invalidate.
160
- */
161
- export function mergePinLevels(a, b) {
162
- const pins = new Set([...a.keys(), ...b.keys()]);
163
- for (const pin of pins) {
164
- const la = a.get(pin) ?? 'unknown';
165
- const lb = b.get(pin) ?? 'unknown';
166
- pinLevels.set(pin, la === lb ? la : 'unknown');
167
- }
168
- }
169
- /** Invalidate all known levels (after loop/switch bodies, which may run any
170
- * number of times). Shadow tracking is unaffected — it is runtime truth. */
171
- export function invalidatePinLevels() {
172
- for (const pin of outputPins)
173
- pinLevels.set(pin, 'unknown');
174
- }
175
- /** Disable/enable constant folding around function and callback bodies. */
176
- export function setPinFoldingEnabled(enabled) {
177
- foldingEnabled = enabled;
178
- }
179
- /** Whether pin-level constant folding is currently allowed. */
180
- export function isPinFoldingEnabled() {
181
- return foldingEnabled;
182
- }
@@ -1,4 +0,0 @@
1
- import type { Diagnostic } from '../types.js';
2
- import type { BoardConstants } from './board-resolver.js';
3
- import type { PeripheralUsage } from './peripheral-usage.js';
4
- export declare function validatePWMTimerSharing(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
@@ -1,94 +0,0 @@
1
- import { findBoardPinByName, formatPinReference } from './board-pin-utils.js';
2
- function extractTimerId(role, timerField) {
3
- // Prefer explicit timer field from board definition
4
- if (timerField) {
5
- return timerField;
6
- }
7
- if (!role) {
8
- return undefined;
9
- }
10
- // Fall back to parsing AVR OC register naming convention
11
- const match = role.match(/^OC(\d+)[A-Z]?$/i);
12
- return match ? `timer${match[1]}` : undefined;
13
- }
14
- function getPwmTimerPins(boardConstants) {
15
- if (!boardConstants) {
16
- return [];
17
- }
18
- const pinsByIndex = new Map();
19
- for (const [key, value] of boardConstants) {
20
- const nameMatch = key.match(/^pins\.all\.(\d+)\.name$/);
21
- if (nameMatch && typeof value === 'string') {
22
- const pinIndex = nameMatch[1];
23
- const numberValue = boardConstants.get(`pins.all.${pinIndex}.number`);
24
- const aliasesValue = boardConstants.get(`pins.all.${pinIndex}.aliases`);
25
- const aliases = typeof aliasesValue === 'string' && aliasesValue.length > 0
26
- ? aliasesValue.split(',').map(entry => entry.trim()).filter(Boolean)
27
- : [];
28
- pinsByIndex.set(pinIndex, {
29
- pinNumber: typeof numberValue === 'number' ? numberValue : Number(pinIndex),
30
- canonicalName: value,
31
- aliases,
32
- timerId: '',
33
- });
34
- }
35
- }
36
- for (const [key, value] of boardConstants) {
37
- const typeMatch = key.match(/^pins\.all\.(\d+)\.functions\.\d+\.type$/);
38
- if (!typeMatch || value !== 'pwm') {
39
- continue;
40
- }
41
- const pinIndex = typeMatch[1];
42
- const roleKey = key.replace(/\.type$/, '.role');
43
- const timerKey = key.replace(/\.type$/, '.timer');
44
- const timerId = extractTimerId(boardConstants.get(roleKey), boardConstants.get(timerKey));
45
- if (!timerId) {
46
- continue;
47
- }
48
- const pin = pinsByIndex.get(pinIndex);
49
- if (pin) {
50
- pin.timerId = timerId;
51
- }
52
- }
53
- return Array.from(pinsByIndex.values()).filter(pin => pin.timerId.length > 0);
54
- }
55
- export function validatePWMTimerSharing(usage, boardConstants, filePath) {
56
- if (usage.pwmPinsUsed.size < 2 || !boardConstants) {
57
- return [];
58
- }
59
- const boardName = boardConstants.get('name');
60
- const boardLabel = typeof boardName === 'string' && boardName.length > 0
61
- ? ` on ${boardName}`
62
- : '';
63
- const pins = getPwmTimerPins(boardConstants);
64
- const pinsByTimer = new Map();
65
- for (const pin of pins) {
66
- if (!usage.pwmPinsUsed.has(pin.pinNumber)) {
67
- continue;
68
- }
69
- const group = pinsByTimer.get(pin.timerId) ?? [];
70
- group.push(pin);
71
- pinsByTimer.set(pin.timerId, group);
72
- }
73
- const diagnostics = [];
74
- for (const [timerId, timerPins] of pinsByTimer) {
75
- if (timerPins.length < 2) {
76
- continue;
77
- }
78
- const references = timerPins
79
- .sort((left, right) => left.pinNumber - right.pinNumber)
80
- .map((pin) => {
81
- const usedNames = [pin.canonicalName, ...pin.aliases].filter(name => usage.pinsUsed.has(name));
82
- const preferredName = usedNames[0] ?? pin.canonicalName;
83
- return formatPinReference(preferredName, findBoardPinByName(pin.canonicalName, boardConstants));
84
- });
85
- diagnostics.push({
86
- severity: 'info',
87
- code: 'pwm-timer-sharing',
88
- filePath,
89
- source: 'pwm-timer-sharing',
90
- message: `${references.join(', ')} share ${timerId}${boardLabel}. Duty cycle can differ per pin, but timer-wide PWM settings are shared across that group. Prefer pins on different timer groups if you need independent PWM timing behavior.`,
91
- });
92
- }
93
- return diagnostics;
94
- }
@@ -1,4 +0,0 @@
1
- import type { Diagnostic } from '../types.js';
2
- import type { BoardConstants } from './board-resolver.js';
3
- import type { PeripheralUsage } from './peripheral-usage.js';
4
- export declare function validateTimer0PWMTimingConflict(usage: PeripheralUsage, boardConstants: BoardConstants | undefined, filePath: string): Diagnostic[];
@@ -1,72 +0,0 @@
1
- import { findBoardPinByName, formatPinReference } from './board-pin-utils.js';
2
- function getTimer0PwmPins(boardConstants) {
3
- if (!boardConstants) {
4
- return [];
5
- }
6
- const pinsByIndex = new Map();
7
- for (const [key, value] of boardConstants) {
8
- const match = key.match(/^pins\.all\.(\d+)\.name$/);
9
- if (!match || typeof value !== 'string') {
10
- continue;
11
- }
12
- const pinIndex = match[1];
13
- const numberValue = boardConstants.get(`pins.all.${pinIndex}.number`);
14
- const aliasesValue = boardConstants.get(`pins.all.${pinIndex}.aliases`);
15
- const aliases = typeof aliasesValue === 'string' && aliasesValue.length > 0
16
- ? aliasesValue.split(',').map(entry => entry.trim()).filter(Boolean)
17
- : [];
18
- pinsByIndex.set(pinIndex, {
19
- pinNumber: typeof numberValue === 'number' ? numberValue : Number(pinIndex),
20
- canonicalName: value,
21
- aliases,
22
- });
23
- }
24
- const timer0Pins = [];
25
- for (const [key, value] of boardConstants) {
26
- // Check for timer field first, fall back to OC register regex
27
- const timerMatch = key.match(/^pins\.all\.(\d+)\.functions\.(\d+)\.timer$/);
28
- if (timerMatch && value === 'timer0') {
29
- const pin = pinsByIndex.get(timerMatch[1]);
30
- if (pin) {
31
- timer0Pins.push(pin);
32
- }
33
- continue;
34
- }
35
- const match = key.match(/^pins\.all\.(\d+)\.functions\.\d+\.role$/);
36
- if (!match || typeof value !== 'string' || !/^OC0[A-Z]?$/i.test(value)) {
37
- continue;
38
- }
39
- const pin = pinsByIndex.get(match[1]);
40
- if (pin && !timer0Pins.some(p => p.pinNumber === pin.pinNumber)) {
41
- timer0Pins.push(pin);
42
- }
43
- }
44
- return timer0Pins;
45
- }
46
- export function validateTimer0PWMTimingConflict(usage, boardConstants, filePath) {
47
- if (!usage.timer0 || usage.pwmPinsUsed.size === 0) {
48
- return [];
49
- }
50
- const timer0Pins = getTimer0PwmPins(boardConstants)
51
- .filter(pin => usage.pwmPinsUsed.has(pin.pinNumber));
52
- if (timer0Pins.length === 0) {
53
- return [];
54
- }
55
- const boardName = boardConstants?.get('name');
56
- const boardLabel = typeof boardName === 'string' && boardName.length > 0
57
- ? ` on ${boardName}`
58
- : '';
59
- const pinReferences = timer0Pins
60
- .sort((left, right) => left.pinNumber - right.pinNumber)
61
- .map((pin) => {
62
- const usedName = [pin.canonicalName, ...pin.aliases].find(name => usage.pinsUsed.has(name)) ?? pin.canonicalName;
63
- return formatPinReference(usedName, findBoardPinByName(pin.canonicalName, boardConstants));
64
- });
65
- return [{
66
- severity: 'info',
67
- code: 'timer0-pwm-timing-conflict',
68
- filePath,
69
- source: 'timer0-pwm-timing-conflict',
70
- message: `${pinReferences.join(', ')} use Timer0 PWM${boardLabel}, and your program also relies on Timer0-backed timing APIs such as delay(), millis(), or micros(). This coupling is common on AVR boards, so prefer non-Timer0 PWM pins when you want PWM behavior isolated from core timing.`,
71
- }];
72
- }
@@ -1,2 +0,0 @@
1
- export { generateComponentDeclsForProject } from "./cpp-to-decl.js";
2
- export type { ComponentScanRoots } from "./component-discovery.js";
@@ -1,6 +0,0 @@
1
- // Public barrel for component declaration generation.
2
- //
3
- // framework-esp32 imports `generateComponentDeclsForProject` from here so its
4
- // dependency on cuttlefish is declared through the package exports map rather
5
- // than a deep relative path. Spec: 2026-07-19-framework-esp32-components-design.md
6
- export { generateComponentDeclsForProject } from "./cpp-to-decl.js";
@@ -1,43 +0,0 @@
1
- export interface ComponentScanRoots {
2
- /** Subdirectory names under managed_components/ (e.g. espressif__esp_wifi). */
3
- managed: string[];
4
- /** Absolute paths to local component directories. */
5
- local: string[];
6
- /**
7
- * ESP-IDF built-in component names (e.g. esp_wifi). Resolved against
8
- * `idfRoot/components/<name>/include/` when idfRoot is provided; ignored
9
- * otherwise (the caller is responsible for passing idfRoot when builtins
10
- * are present).
11
- */
12
- builtin: string[];
13
- /** Optional ESP-IDF install root for resolving `builtin` names. */
14
- idfRoot?: string;
15
- }
16
- /**
17
- * A discovered header plus the directory its generated .d.ts should land in.
18
- *
19
- * `outputDir` is alongside the header for managed/local components (so the
20
- * .d.ts lives in the same gitignored component dir, regenerated on each run).
21
- * For built-in components it's a project-local cache — NEVER write into the
22
- * IDF install itself.
23
- */
24
- export interface DiscoveredHeader {
25
- /** Absolute path to the source .h file. */
26
- path: string;
27
- /** Absolute directory where the generated .d.ts should be written. */
28
- outputDir: string;
29
- }
30
- /**
31
- * Find all .h files for the declared components.
32
- *
33
- * - managed: scan `managed_components/<name>/include/` (falling back to the
34
- * component dir if no include/ exists). Populated by `idf.py reconfigure`.
35
- * - local: scan `<path>/include/` (or `<path>/`).
36
- * - builtin: scan `<idfRoot>/components/<name>/include/` (IDF ships with these).
37
- *
38
- * For builtins the output .d.ts goes to `<projectDir>/.cuttlefish/component-decls/<component>/`
39
- * — never into `$IDF_PATH/components/`, which would pollute the IDF install.
40
- *
41
- * Returned entries are unsorted — the caller decides ordering.
42
- */
43
- export declare function discoverComponentHeaders(projectDir: string, roots: ComponentScanRoots): DiscoveredHeader[];