@typecad/cuttlefish 1.0.0-alpha.7 → 1.0.0-alpha.8

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 (160) hide show
  1. package/dist/api/config.d.ts +32 -0
  2. package/dist/api/index.d.ts +1 -1
  3. package/dist/api/schema/types.d.ts +15 -0
  4. package/dist/api/shared/async-runtime-static.d.ts +12 -1
  5. package/dist/api/shared/async-runtime-static.js +69 -24
  6. package/dist/api/shared/async-symbol-detector.d.ts +11 -0
  7. package/dist/api/shared/async-symbol-detector.js +140 -0
  8. package/dist/api/shared/async-types.d.ts +24 -0
  9. package/dist/api/shared/coop-scheduler.d.ts +60 -0
  10. package/dist/api/shared/coop-scheduler.js +149 -0
  11. package/dist/api/shared/display-adapter.d.ts +2 -2
  12. package/dist/api/shared/display-adapter.js +9 -119
  13. package/dist/api/shared/display-adapters/sdl.js +29 -29
  14. package/dist/api/shared/display-profile.d.ts +38 -33
  15. package/dist/api/shared/display-profile.js +22 -170
  16. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  17. package/dist/api/shared/framework-manifest-registry.js +8 -6
  18. package/dist/api/shared/framework-manifest.d.ts +147 -28
  19. package/dist/api/shared/framework-manifest.js +35 -2
  20. package/dist/api/shared/graphics-strategy.d.ts +15 -6
  21. package/dist/api/shared/hal-op-ir.d.ts +340 -3
  22. package/dist/api/shared/hal-op-ir.js +34 -0
  23. package/dist/api/shared/index.d.ts +9 -1
  24. package/dist/api/shared/index.js +11 -1
  25. package/dist/api/shared/ir-core.d.ts +4 -0
  26. package/dist/api/shared/ir-declarations.d.ts +6 -0
  27. package/dist/api/shared/platform-strategy.d.ts +104 -2
  28. package/dist/api/shared/polyfill-helper-registry.js +18 -1
  29. package/dist/api/shared/promise-runtime.d.ts +1 -1
  30. package/dist/api/shared/promise-runtime.js +17 -13
  31. package/dist/api/shared/toolchain-types.d.ts +17 -0
  32. package/dist/api/shared/validate-framework-manifest.js +77 -0
  33. package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
  34. package/dist/api/shared/worker-runtime-polyfill.js +34 -0
  35. package/dist/api/shared/worker-runtime.d.ts +69 -0
  36. package/dist/api/shared/worker-runtime.js +163 -0
  37. package/dist/cli.js +120 -121
  38. package/dist/config-loader.d.ts +7 -2
  39. package/dist/config-loader.js +20 -19
  40. package/dist/config-schema.d.ts +107 -65
  41. package/dist/config-schema.js +14 -0
  42. package/dist/contract/board-generator.d.ts +17 -0
  43. package/dist/contract/board-generator.js +57 -0
  44. package/dist/contract/contract-parser.d.ts +217 -0
  45. package/dist/contract/contract-parser.js +224 -0
  46. package/dist/contract/index.d.ts +22 -0
  47. package/dist/contract/index.js +68 -0
  48. package/dist/create/board-codegen.js +4 -4
  49. package/dist/create/board-generators.js +4 -5
  50. package/dist/create/board-spec.d.ts +74 -77
  51. package/dist/create/board-spec.js +0 -1
  52. package/dist/create/init-scaffold.d.ts +7 -3
  53. package/dist/create/init-scaffold.js +0 -62
  54. package/dist/create/init-templates.js +62 -16
  55. package/dist/create/init-wizard.js +32 -27
  56. package/dist/debug/preprocessor.js +178 -39
  57. package/dist/debug/types.d.ts +23 -0
  58. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  59. package/dist/diagnostics/mermaid-builder.js +34 -24
  60. package/dist/emit/compliance/arxml-writer.d.ts +11 -0
  61. package/dist/emit/compliance/arxml-writer.js +34 -0
  62. package/dist/emit/compliance/compliance-context.d.ts +57 -0
  63. package/dist/emit/compliance/compliance-context.js +113 -0
  64. package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
  65. package/dist/emit/compliance/deviation-ledger.js +47 -0
  66. package/dist/emit/compliance/deviation-writer.d.ts +30 -0
  67. package/dist/emit/compliance/deviation-writer.js +37 -0
  68. package/dist/emit/compliance/index.d.ts +7 -0
  69. package/dist/emit/compliance/index.js +6 -0
  70. package/dist/emit/compliance/rule-engine.d.ts +13 -0
  71. package/dist/emit/compliance/rule-engine.js +101 -0
  72. package/dist/emit/compliance/rules.d.ts +16 -0
  73. package/dist/emit/compliance/rules.js +191 -0
  74. package/dist/emit/compliance/types.d.ts +59 -0
  75. package/dist/emit/compliance/types.js +8 -0
  76. package/dist/emit/emitters/class-emitter.js +6 -1
  77. package/dist/emit/emitters/emitter-context.d.ts +23 -1
  78. package/dist/emit/emitters/function-emitter-impl.js +96 -6
  79. package/dist/emit/emitters/line-appender.js +19 -0
  80. package/dist/emit/emitters/line-marker.d.ts +38 -0
  81. package/dist/emit/emitters/line-marker.js +39 -0
  82. package/dist/emit/emitters/output-finalizer.js +80 -1
  83. package/dist/emit/emitters/setup.d.ts +17 -0
  84. package/dist/emit/emitters/setup.js +217 -36
  85. package/dist/emit/emitters/top-level-prep.js +6 -0
  86. package/dist/emit/emitters/type-decl-emitter.js +28 -3
  87. package/dist/emit/emitters/ui-emitter.js +17 -8
  88. package/dist/emit/expression-renderer.d.ts +1 -1
  89. package/dist/emit/expression-renderer.js +81 -5
  90. package/dist/emit/route-hal-op.js +18 -5
  91. package/dist/emit/snprintf-helpers.js +7 -4
  92. package/dist/emit/statement-renderer.d.ts +10 -0
  93. package/dist/emit/statement-renderer.js +21 -5
  94. package/dist/emit/utils/async-state-machine.js +39 -12
  95. package/dist/framework-package.js +2 -0
  96. package/dist/framework-registry.d.ts +17 -0
  97. package/dist/ir/adc-range-validation.js +14 -1
  98. package/dist/ir/build-ir-state.d.ts +1 -0
  99. package/dist/ir/build-ir-state.js +14 -0
  100. package/dist/ir/build-ir.js +4 -4
  101. package/dist/ir/call-graph.js +16 -0
  102. package/dist/ir/expression-to-ir.js +150 -0
  103. package/dist/ir/function-builder.js +22 -0
  104. package/dist/ir/hal/hal-emitter.js +10 -6
  105. package/dist/ir/hal/hal-parser.js +16 -9
  106. package/dist/ir/hal/hal-plugins.js +164 -3
  107. package/dist/ir/identifier-collector.js +21 -4
  108. package/dist/ir/interrupt-analysis.d.ts +5 -1
  109. package/dist/ir/interrupt-analysis.js +8 -16
  110. package/dist/ir/ownership-analysis.js +6 -0
  111. package/dist/ir/pin-capability-validation.js +37 -0
  112. package/dist/ir/pin-mode-validation.d.ts +2 -2
  113. package/dist/ir/pin-mode-validation.js +49 -18
  114. package/dist/ir/program-analysis.d.ts +42 -0
  115. package/dist/ir/program-analysis.js +243 -6
  116. package/dist/ir/render-expr.js +11 -0
  117. package/dist/ir/timing-validation.js +11 -2
  118. package/dist/ir/transformers/call-statement.js +105 -0
  119. package/dist/ir/transformers/expressions.js +4 -0
  120. package/dist/ir/transformers/hal-call-resolver.js +21 -0
  121. package/dist/ir/transformers/namespace-methods.js +17 -12
  122. package/dist/ir/transformers/ui-reactive.js +2 -2
  123. package/dist/ir/transformers/variables.js +51 -0
  124. package/dist/ir/type-resolution.js +18 -0
  125. package/dist/ir/ui-element-auto-wire.js +7 -5
  126. package/dist/ir/utils/map-statements.d.ts +4 -0
  127. package/dist/ir/utils/map-statements.js +79 -0
  128. package/dist/ir/validation-orchestrator.js +6 -1
  129. package/dist/ir/worker-analysis.d.ts +10 -0
  130. package/dist/ir/worker-analysis.js +261 -0
  131. package/dist/orchestrator/graph-builder.js +8 -7
  132. package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
  133. package/dist/platform/coop-scheduler-runtime.js +41 -0
  134. package/dist/platform/generic-strategy.d.ts +15 -3
  135. package/dist/platform/generic-strategy.js +49 -4
  136. package/dist/safety/safety-bridge.d.ts +11 -0
  137. package/dist/safety/safety-bridge.js +48 -0
  138. package/dist/safety/sidecar-bridge.d.ts +5 -0
  139. package/dist/safety/sidecar-bridge.js +22 -0
  140. package/dist/safety-hook.d.ts +79 -0
  141. package/dist/safety-hook.js +35 -0
  142. package/dist/stores/display-profile-store.d.ts +0 -1
  143. package/dist/stores/display-profile-store.js +0 -1
  144. package/dist/testing.d.ts +4 -0
  145. package/dist/testing.js +5 -0
  146. package/dist/transpile.js +124 -35
  147. package/dist/types.d.ts +13 -2
  148. package/dist/ui-hook.d.ts +0 -2
  149. package/dist/utils/cli.js +51 -3
  150. package/package.json +20 -5
  151. package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
  152. package/dist/api/shared/display-adapters/eink-mono.js +0 -53
  153. package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
  154. package/dist/api/shared/display-adapters/ssd1309.js +0 -136
  155. package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
  156. package/dist/api/shared/display-adapters/st7796.js +0 -132
  157. package/dist/libdef/idf-discovery.d.ts +0 -7
  158. package/dist/libdef/idf-discovery.js +0 -59
  159. package/dist/licenses.d.ts +0 -185
  160. package/dist/licenses.js +0 -963
@@ -25,43 +25,13 @@ const RESISTIVE_TOUCH_LIBS = new Set([
25
25
  "XPT2046_Touchscreen",
26
26
  "Adafruit_TouchScreen",
27
27
  ]);
28
- /** Default RGB565 scroll viewport canvas budget (~88 KB, ESP32-class SRAM). */
29
- export const DEFAULT_SCROLL_CANVAS_BUDGET_BYTES = 88000;
30
- /** PSRAM scroll canvas budget (~2 MB). ESP32-S3 with OPI/QSPI PSRAM exposes
31
- * ~8 MB external RAM; a full-width scroll viewport canvas (e.g. 460×266×2 ≈
32
- * 239 KB) is trivial there, so the transpile-time scroll-canvas-memory
33
- * diagnostic should use a PSRAM-appropriate budget rather than the no-PSRAM
34
- * SRAM default — otherwise every PSRAM target emits stale "exceeds budget"
35
- * warnings for canvases the runtime allocates without issue. */
36
- export const PSRAM_SCROLL_CANVAS_BUDGET_BYTES = 2_000_000;
37
- /** Detect PSRAM from an Arduino FQBN buildTarget (e.g. "esp32:esp32:esp32s3:PSRAM=opi").
38
- * Returns true for any PSRAM=<value> option whose value indicates PSRAM is
39
- * enabled (opi, io, qspi, enabled, true). Returns false when absent or set
40
- * to a disabled-looking value (disabled, false, none). */
41
- export function buildTargetHasPsram(buildTarget) {
42
- if (!buildTarget)
43
- return false;
44
- const opts = buildTarget.split(":");
45
- for (const opt of opts) {
46
- const eq = opt.indexOf("=");
47
- if (eq < 0)
48
- continue;
49
- const key = opt.slice(0, eq).trim().toLowerCase();
50
- const val = opt.slice(eq + 1).trim().toLowerCase();
51
- if (key !== "psram")
52
- continue;
53
- // Any explicit PSRAM option except an explicit disabled value means PSRAM.
54
- return !["disabled", "false", "none", "no", "0"].includes(val);
55
- }
56
- return false;
57
- }
58
28
  /**
59
29
  * Resolve scroll config from a display profile. Declared overrides win;
60
30
  * otherwise derive the input tier from the touch library (resistive chips →
61
31
  * "resistive", any other touch → "capacitive", no touch → "none") and assume a
62
32
  * full render tier (ESP32-class SRAM can fit a viewport canvas).
63
33
  */
64
- export function resolveScrollConfig(display, ctx) {
34
+ export function resolveScrollConfig(display) {
65
35
  const s = display.scroll ?? {};
66
36
  const lib = display.touch && typeof display.touch === "object"
67
37
  ? display.touch.library
@@ -71,15 +41,6 @@ export function resolveScrollConfig(display, ctx) {
71
41
  : display.touch
72
42
  ? "capacitive"
73
43
  : "none";
74
- // PSRAM-aware canvas budget: an explicit override always wins; otherwise use
75
- // the PSRAM budget when the target opts into PSRAM — either via an explicit
76
- // frameworkData.psram flag (IDF path) or an Arduino FQBN PSRAM= option — so
77
- // the scroll-canvas-memory diagnostic doesn't emit stale warnings for canvases
78
- // the runtime allocates in external RAM without issue. Else the SRAM default.
79
- const psramBudget = ctx?.psram === true || buildTargetHasPsram(ctx?.buildTarget);
80
- const budgetDefault = psramBudget
81
- ? PSRAM_SCROLL_CANVAS_BUDGET_BYTES
82
- : DEFAULT_SCROLL_CANVAS_BUDGET_BYTES;
83
44
  return {
84
45
  inputTier: s.inputTier ?? derivedInput,
85
46
  renderTier: s.renderTier ?? "full",
@@ -90,7 +51,6 @@ export function resolveScrollConfig(display, ctx) {
90
51
  inputSmoothing: s.inputSmoothing ?? 0.3,
91
52
  overrideProbes: s.overrideProbes ?? true,
92
53
  debug: s.debug ?? false,
93
- scrollCanvasBudgetBytes: s.scrollCanvasBudgetBytes ?? budgetDefault,
94
54
  };
95
55
  }
96
56
  export function resolveDisplayProfile(config, registry) {
@@ -152,6 +112,8 @@ export function resolveDisplayProfile(config, registry) {
152
112
  base.colorOrder = config.colorOrder;
153
113
  if (config.invertDisplay !== undefined)
154
114
  base.invertDisplay = config.invertDisplay;
115
+ if (config.scanlineSync !== undefined)
116
+ base.scanlineSync = config.scanlineSync;
155
117
  if (config.touch === false)
156
118
  base.touch = undefined;
157
119
  else if (config.touch !== undefined)
@@ -179,13 +141,18 @@ export function resolveDisplayProfile(config, registry) {
179
141
  }
180
142
  /** Generate C++ code for a built-in touch library adapter.
181
143
  *
182
- * Strategy-owned touch adapters (ESP32 native) take precedence: when the
183
- * strategy's `providesTouchAdapter()` returns true and `resolveTouchAdapter`
184
- * returns non-null, that codegen wins. Otherwise falls through to the
185
- * built-in library switch below (the Arduino path, unchanged).
144
+ * Strategy-owned touch adapters take precedence: when the strategy's
145
+ * `providesTouchAdapter()` returns true and `resolveTouchAdapter` returns
146
+ * non-null, that codegen wins. The Arduino-ecosystem libraries
147
+ * (XPT2046_Touchscreen, Adafruit_TouchScreen, Adafruit_STMPE610, FT6336U)
148
+ * are strategy-owned — they live in @typecad/framework-arduino
149
+ * (src/displays/touch-adapters-codegen.ts) and ArduinoStrategy dispatches to
150
+ * them. Cuttlefish keeps only the framework-agnostic `sdl` path here.
186
151
  *
187
- * Passing `undefined` for the strategy (or a strategy that doesn't provide
188
- * touch adapters) keeps the historical behavior — the Arduino library switch.
152
+ * Passing `undefined` for the strategy (or a strategy whose
153
+ * resolveTouchAdapter returns undefined for the given library) falls through
154
+ * to the `sdl` branch below; any other library without a strategy-provided
155
+ * adapter throws.
189
156
  */
190
157
  export function generateTouchAdapter(touch, strategy) {
191
158
  if (strategy?.providesTouchAdapter?.() && strategy.resolveTouchAdapter) {
@@ -193,124 +160,6 @@ export function generateTouchAdapter(touch, strategy) {
193
160
  if (code)
194
161
  return code;
195
162
  }
196
- const cs = touch.cs ?? 0;
197
- const irq = touch.irq;
198
- if (touch.library === "XPT2046_Touchscreen") {
199
- return {
200
- includes: ["#include <XPT2046_Touchscreen.h>"],
201
- declaration: `XPT2046_Touchscreen __tc_touch(${cs}${irq ? `, ${irq}` : ""});`,
202
- functions: [
203
- `static inline void touch_init() { __tc_touch.begin(); }`,
204
- `static inline bool touch_isTouched() { return __tc_touch.touched(); }`,
205
- `static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
206
- ` TS_Point __tp = __tc_touch.getPoint();`,
207
- ` if (x) *x = (int16_t)__tp.x;`,
208
- ` if (y) *y = (int16_t)__tp.y;`,
209
- ` if (z) *z = (int16_t)__tp.z;`,
210
- `}`,
211
- ].join("\n"),
212
- };
213
- }
214
- if (touch.library === "Adafruit_TouchScreen" && touch.analogPins) {
215
- const a = touch.analogPins;
216
- return {
217
- includes: ["#include <TouchScreen.h>"],
218
- declaration: `TouchScreen __tc_touch = TouchScreen(${a.xp}, ${a.yp}, ${a.xm}, ${a.ym}, ${a.rx});`,
219
- functions: [
220
- `static inline void touch_init() {}`,
221
- `static inline bool touch_isTouched() { return __tc_touch.isTouching(); }`,
222
- `static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
223
- ` TS_Point __tp = __tc_touch.getPoint();`,
224
- ` if (x) *x = (int16_t)__tp.x;`,
225
- ` if (y) *y = (int16_t)__tp.y;`,
226
- ` if (z) *z = (int16_t)__tp.z;`,
227
- `}`,
228
- ].join("\n"),
229
- };
230
- }
231
- if (touch.library === "Adafruit_STMPE610") {
232
- return {
233
- includes: ["#include <Adafruit_STMPE610.h>"],
234
- declaration: `Adafruit_STMPE610 __tc_touch(${cs});`,
235
- functions: [
236
- `static inline void touch_init() { __tc_touch.begin(); }`,
237
- `static inline bool touch_isTouched() { return __tc_touch.touched() && !__tc_touch.bufferEmpty(); }`,
238
- `static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
239
- ` TS_Point __tp = __tc_touch.getPoint();`,
240
- ` if (x) *x = (int16_t)__tp.x;`,
241
- ` if (y) *y = (int16_t)__tp.y;`,
242
- ` if (z) *z = (int16_t)__tp.z;`,
243
- `}`,
244
- ].join("\n"),
245
- };
246
- }
247
- if (touch.library === "FT6336U") {
248
- const addr = touch.i2cAddress ?? 0x38;
249
- // I2C addresses are conventional in hex in Arduino code.
250
- const addrHex = "0x" + addr.toString(16).toUpperCase();
251
- const i2cFrequency = touch.i2cFrequency ?? 400000;
252
- const reset = touch.resetPin;
253
- const resetLines = reset
254
- ? [
255
- ` pinMode(${reset}, OUTPUT);`,
256
- ` digitalWrite(${reset}, LOW);`,
257
- ` delay(10);`,
258
- ` digitalWrite(${reset}, HIGH);`,
259
- ` delay(500);`,
260
- ].join("\n")
261
- : ``;
262
- return {
263
- includes: ["#include <Wire.h>", "#include <RAK14014_FT6336U.h>"],
264
- declaration: [
265
- `FT6336U __tc_touch(${addrHex});`,
266
- `static int16_t __tc_touch_cached_x = 0;`,
267
- `static int16_t __tc_touch_cached_y = 0;`,
268
- `static int16_t __tc_touch_cached_z = 0;`,
269
- `static uint8_t __tc_touch_cached_valid = 0;`,
270
- ].join("\n"),
271
- functions: [
272
- `static inline uint8_t __tc_ft6336u_read_block(uint8_t reg, uint8_t* buf, uint8_t len) {`,
273
- ` Wire.beginTransmission(${addrHex});`,
274
- ` Wire.write(reg);`,
275
- ` if (Wire.endTransmission(false) != 0) return 0;`,
276
- ` uint8_t got = Wire.requestFrom((uint8_t)${addrHex}, len);`,
277
- ` if (got < len) return 0;`,
278
- ` for (uint8_t i = 0; i < len; i++) {`,
279
- ` if (!Wire.available()) return 0;`,
280
- ` buf[i] = Wire.read();`,
281
- ` }`,
282
- ` return 1;`,
283
- `}`,
284
- `static inline void touch_init() {`,
285
- resetLines,
286
- ` __tc_touch.begin(Wire, ${addrHex});`,
287
- ` Wire.setClock(${i2cFrequency});`,
288
- `}`,
289
- `static inline bool touch_isTouched() {`,
290
- ` uint8_t buf[5] = {0, 0, 0, 0, 0};`,
291
- ` __tc_touch_cached_valid = 0;`,
292
- ` __tc_touch_cached_z = 0;`,
293
- ` if (!__tc_ft6336u_read_block(0x02, buf, 5)) return false;`,
294
- ` uint8_t count = buf[0] & 0x0F;`,
295
- ` if (count == 0) return false;`,
296
- ` __tc_touch_cached_x = (int16_t)(((uint16_t)(buf[1] & 0x0F) << 8) | buf[2]);`,
297
- ` __tc_touch_cached_y = (int16_t)(((uint16_t)(buf[3] & 0x0F) << 8) | buf[4]);`,
298
- ` __tc_touch_cached_z = 255;`,
299
- ` __tc_touch_cached_valid = 1;`,
300
- ` return true;`,
301
- `}`,
302
- `static inline void touch_readRaw(int16_t* x, int16_t* y, int16_t* z) {`,
303
- ` if (!__tc_touch_cached_valid) {`,
304
- ` (void)touch_isTouched();`,
305
- ` }`,
306
- ` if (x) *x = __tc_touch_cached_x;`,
307
- ` if (y) *y = __tc_touch_cached_y;`,
308
- ` if (z) *z = __tc_touch_cached_z;`,
309
- ` __tc_touch_cached_valid = 0;`,
310
- `}`,
311
- ].join("\n"),
312
- };
313
- }
314
163
  if (touch.library === "sdl") {
315
164
  return {
316
165
  includes: ["#include <SDL2/SDL.h>"],
@@ -340,14 +189,17 @@ export function generateTouchAdapter(touch, strategy) {
340
189
  ` int __ww = 0, __wh = 0; SDL_GetWindowSize(__tc_display.win, &__ww, &__wh);`,
341
190
  ` if (__ww <= 0) __ww = display_width();`,
342
191
  ` if (__wh <= 0) __wh = display_height();`,
343
- ` if (x) *x = (int16_t)((int32_t)__sdl_mouse_x * display_width() / __ww);`,
344
- ` if (y) *y = (int16_t)((int32_t)__sdl_mouse_y * display_height() / __wh);`,
192
+ ` if (x) *x = static_cast<int16_t>(static_cast<int32_t>(__sdl_mouse_x) * display_width() / __ww);`,
193
+ ` if (y) *y = static_cast<int16_t>(static_cast<int32_t>(__sdl_mouse_y) * display_height() / __wh);`,
345
194
  ` if (z) *z = 200; // constant > default minPressure (10)`,
346
195
  `}`,
347
196
  ].join("\n"),
348
197
  };
349
198
  }
350
- throw new Error(`Unknown touch library "${touch.library}". ` +
351
- `Use { adapter: './path' } for custom touch adapters, or one of: ` +
352
- `XPT2046_Touchscreen, Adafruit_TouchScreen, Adafruit_STMPE610, FT6336U, sdl.`);
199
+ throw new Error(`No touch adapter for library "${touch.library}". ` +
200
+ `The built-in Arduino touch libraries (XPT2046_Touchscreen, Adafruit_TouchScreen, ` +
201
+ `Adafruit_STMPE610, FT6336U) are provided by @typecad/framework-arduino — pass the ` +
202
+ `ArduinoStrategy (or another framework strategy that provides touch adapters) so its ` +
203
+ `resolveTouchAdapter can dispatch to them. Otherwise use the framework-agnostic ` +
204
+ `"sdl" library, or { adapter: './path' } for a custom touch adapter.`);
353
205
  }
@@ -1,5 +1,5 @@
1
1
  import type { FrameworkManifest } from './framework-manifest.js';
2
- export declare const KNOWN_FRAMEWORK_PACKAGES: readonly ["@typecad/framework-arduino", "@typecad/framework-avr", "@typecad/framework-esp32"];
2
+ export declare const KNOWN_FRAMEWORK_PACKAGES: readonly ["@typecad/framework-arduino", "@typecad/framework-zephyr"];
3
3
  export type KnownFrameworkPackage = typeof KNOWN_FRAMEWORK_PACKAGES[number];
4
4
  /**
5
5
  * Loads a framework's manifest via its `./framework.manifest` subpath export.
@@ -1,15 +1,17 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // Framework manifest discovery
3
3
  //
4
- // KNOWN_FRAMEWORK_PACKAGES is the authoritative list of framework packages.
5
- // Add a new framework here when it ships. loadFrameworkManifest uses the
6
- // "./framework.manifest" subpath export each framework declares in its
7
- // package.json.
4
+ // KNOWN_FRAMEWORK_PACKAGES is the authoritative list of framework packages
5
+ // that ship a framework.manifest (i.e. participate in the manifest validation
6
+ // system). It names real packages by their npm scope+name so the manifest
7
+ // validator and coverage tooling can iterate them — it is not framework-
8
+ // specific knowledge. Add a new framework here when it ships a manifest.
9
+ // loadFrameworkManifest uses the "./framework.manifest" subpath export each
10
+ // framework declares in its package.json.
8
11
  // ---------------------------------------------------------------------------
9
12
  export const KNOWN_FRAMEWORK_PACKAGES = [
10
13
  '@typecad/framework-arduino',
11
- '@typecad/framework-avr',
12
- '@typecad/framework-esp32',
14
+ '@typecad/framework-zephyr',
13
15
  ];
14
16
  /**
15
17
  * Loads a framework's manifest via its `./framework.manifest` subpath export.
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- declare const HAL_CATEGORIES: readonly ["gpio", "pwm", "adc", "dac", "interrupts", "tone", "timing", "power", "i2c", "spi", "uart", "pulse", "shift", "board", "wdt", "wifi", "http", "display"];
2
+ declare const HAL_CATEGORIES: readonly ["gpio", "pwm", "adc", "dac", "interrupts", "tone", "timing", "power", "i2c", "spi", "uart", "pulse", "shift", "board", "wdt", "wifi", "http", "mqtt", "display"];
3
3
  export type HalCategory = typeof HAL_CATEGORIES[number];
4
4
  export declare const FrameworkManifestSchema: z.ZodObject<{
5
5
  schemaVersion: z.ZodLiteral<1>;
@@ -74,23 +74,102 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
74
74
  }, {
75
75
  supported: boolean;
76
76
  }>>;
77
+ }, "strip", z.ZodObject<{
78
+ supported: z.ZodBoolean;
79
+ unsupportedReason: z.ZodOptional<z.ZodString>;
80
+ ops: z.ZodRecord<z.ZodString, z.ZodEnum<["supported", "stub", "unsupported", "probe-inconclusive", "polyfill"]>>;
81
+ partialCoverage: z.ZodDefault<z.ZodBoolean>;
77
82
  }, "strip", z.ZodTypeAny, {
78
- [x: string]: {
83
+ supported: boolean;
84
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
85
+ partialCoverage: boolean;
86
+ unsupportedReason?: string | undefined;
87
+ }, {
88
+ supported: boolean;
89
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
90
+ unsupportedReason?: string | undefined;
91
+ partialCoverage?: boolean | undefined;
92
+ }>, z.objectOutputType<{
93
+ [x: string]: z.ZodObject<{
94
+ supported: z.ZodBoolean;
95
+ unsupportedReason: z.ZodOptional<z.ZodString>;
96
+ ops: z.ZodRecord<z.ZodString, z.ZodEnum<["supported", "stub", "unsupported", "probe-inconclusive", "polyfill"]>>;
97
+ partialCoverage: z.ZodDefault<z.ZodBoolean>;
98
+ }, "strip", z.ZodTypeAny, {
79
99
  supported: boolean;
80
100
  ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
81
101
  partialCoverage: boolean;
82
102
  unsupportedReason?: string | undefined;
83
- };
84
- raw?: unknown;
103
+ }, {
104
+ supported: boolean;
105
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
106
+ unsupportedReason?: string | undefined;
107
+ partialCoverage?: boolean | undefined;
108
+ }>;
109
+ } & {
110
+ raw: z.ZodDefault<z.ZodObject<{
111
+ supported: z.ZodBoolean;
112
+ }, "strip", z.ZodTypeAny, {
113
+ supported: boolean;
114
+ }, {
115
+ supported: boolean;
116
+ }>>;
117
+ }, z.ZodObject<{
118
+ supported: z.ZodBoolean;
119
+ unsupportedReason: z.ZodOptional<z.ZodString>;
120
+ ops: z.ZodRecord<z.ZodString, z.ZodEnum<["supported", "stub", "unsupported", "probe-inconclusive", "polyfill"]>>;
121
+ partialCoverage: z.ZodDefault<z.ZodBoolean>;
122
+ }, "strip", z.ZodTypeAny, {
123
+ supported: boolean;
124
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
125
+ partialCoverage: boolean;
126
+ unsupportedReason?: string | undefined;
85
127
  }, {
86
- [x: string]: {
128
+ supported: boolean;
129
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
130
+ unsupportedReason?: string | undefined;
131
+ partialCoverage?: boolean | undefined;
132
+ }>, "strip">, z.objectInputType<{
133
+ [x: string]: z.ZodObject<{
134
+ supported: z.ZodBoolean;
135
+ unsupportedReason: z.ZodOptional<z.ZodString>;
136
+ ops: z.ZodRecord<z.ZodString, z.ZodEnum<["supported", "stub", "unsupported", "probe-inconclusive", "polyfill"]>>;
137
+ partialCoverage: z.ZodDefault<z.ZodBoolean>;
138
+ }, "strip", z.ZodTypeAny, {
139
+ supported: boolean;
140
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
141
+ partialCoverage: boolean;
142
+ unsupportedReason?: string | undefined;
143
+ }, {
87
144
  supported: boolean;
88
145
  ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
89
146
  unsupportedReason?: string | undefined;
90
147
  partialCoverage?: boolean | undefined;
91
- };
92
- raw?: unknown;
93
- }>;
148
+ }>;
149
+ } & {
150
+ raw: z.ZodDefault<z.ZodObject<{
151
+ supported: z.ZodBoolean;
152
+ }, "strip", z.ZodTypeAny, {
153
+ supported: boolean;
154
+ }, {
155
+ supported: boolean;
156
+ }>>;
157
+ }, z.ZodObject<{
158
+ supported: z.ZodBoolean;
159
+ unsupportedReason: z.ZodOptional<z.ZodString>;
160
+ ops: z.ZodRecord<z.ZodString, z.ZodEnum<["supported", "stub", "unsupported", "probe-inconclusive", "polyfill"]>>;
161
+ partialCoverage: z.ZodDefault<z.ZodBoolean>;
162
+ }, "strip", z.ZodTypeAny, {
163
+ supported: boolean;
164
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
165
+ partialCoverage: boolean;
166
+ unsupportedReason?: string | undefined;
167
+ }, {
168
+ supported: boolean;
169
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
170
+ unsupportedReason?: string | undefined;
171
+ partialCoverage?: boolean | undefined;
172
+ }>, "strip">>;
94
173
  polyfills: z.ZodObject<{
95
174
  emitted: z.ZodArray<z.ZodObject<{
96
175
  id: z.ZodString;
@@ -269,10 +348,40 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
269
348
  hardwareTestGroups: string[];
270
349
  halResolutionTests: string[];
271
350
  }>;
351
+ doctor: z.ZodOptional<z.ZodObject<{
352
+ available: z.ZodBoolean;
353
+ }, "strip", z.ZodTypeAny, {
354
+ available: boolean;
355
+ }, {
356
+ available: boolean;
357
+ }>>;
358
+ licenses: z.ZodOptional<z.ZodObject<{
359
+ available: z.ZodBoolean;
360
+ }, "strip", z.ZodTypeAny, {
361
+ available: boolean;
362
+ }, {
363
+ available: boolean;
364
+ }>>;
272
365
  }, "strip", z.ZodTypeAny, {
366
+ hal: {
367
+ [x: string]: {
368
+ supported: boolean;
369
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
370
+ partialCoverage: boolean;
371
+ unsupportedReason?: string | undefined;
372
+ };
373
+ raw?: unknown;
374
+ } & {
375
+ [k: string]: {
376
+ supported: boolean;
377
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
378
+ partialCoverage: boolean;
379
+ unsupportedReason?: string | undefined;
380
+ };
381
+ };
382
+ packageName: string;
273
383
  schemaVersion: 1;
274
384
  frameworkId: string;
275
- packageName: string;
276
385
  canonical: boolean;
277
386
  displayName: string;
278
387
  description: string;
@@ -291,15 +400,6 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
291
400
  symbolAliases: Record<string, string>;
292
401
  targets: string[];
293
402
  };
294
- hal: {
295
- [x: string]: {
296
- supported: boolean;
297
- ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
298
- partialCoverage: boolean;
299
- unsupportedReason?: string | undefined;
300
- };
301
- raw?: unknown;
302
- };
303
403
  polyfills: {
304
404
  emitted: {
305
405
  id: string;
@@ -343,6 +443,12 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
343
443
  hardwareTestGroups: string[];
344
444
  halResolutionTests: string[];
345
445
  };
446
+ doctor?: {
447
+ available: boolean;
448
+ } | undefined;
449
+ licenses?: {
450
+ available: boolean;
451
+ } | undefined;
346
452
  basedOn?: string | undefined;
347
453
  inheritsStrategyId?: string | undefined;
348
454
  libraryResolution?: {
@@ -353,9 +459,25 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
353
459
  reexportedFrom?: string | undefined;
354
460
  } | undefined;
355
461
  }, {
462
+ hal: {
463
+ [x: string]: {
464
+ supported: boolean;
465
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
466
+ unsupportedReason?: string | undefined;
467
+ partialCoverage?: boolean | undefined;
468
+ };
469
+ raw?: unknown;
470
+ } & {
471
+ [k: string]: {
472
+ supported: boolean;
473
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
474
+ unsupportedReason?: string | undefined;
475
+ partialCoverage?: boolean | undefined;
476
+ };
477
+ };
478
+ packageName: string;
356
479
  schemaVersion: 1;
357
480
  frameworkId: string;
358
- packageName: string;
359
481
  displayName: string;
360
482
  description: string;
361
483
  implementationMode: "from-scratch" | "extends-canonical" | "extends-other";
@@ -373,15 +495,6 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
373
495
  symbolAliases: Record<string, string>;
374
496
  targets: string[];
375
497
  };
376
- hal: {
377
- [x: string]: {
378
- supported: boolean;
379
- ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
380
- unsupportedReason?: string | undefined;
381
- partialCoverage?: boolean | undefined;
382
- };
383
- raw?: unknown;
384
- };
385
498
  polyfills: {
386
499
  emitted: {
387
500
  id: string;
@@ -425,6 +538,12 @@ export declare const FrameworkManifestSchema: z.ZodObject<{
425
538
  hardwareTestGroups: string[];
426
539
  halResolutionTests: string[];
427
540
  };
541
+ doctor?: {
542
+ available: boolean;
543
+ } | undefined;
544
+ licenses?: {
545
+ available: boolean;
546
+ } | undefined;
428
547
  canonical?: boolean | undefined;
429
548
  basedOn?: string | undefined;
430
549
  inheritsStrategyId?: string | undefined;
@@ -11,10 +11,21 @@
11
11
  // cross-checks each claim against the loaded strategy/toolchain.
12
12
  // ---------------------------------------------------------------------------
13
13
  import { z } from 'zod';
14
+ // Core HAL categories that EVERY framework MUST declare coverage for (the
15
+ // universal embedded surface: digital/analog I/O, comms buses, timing, power,
16
+ // watchdog). These keys are required in every manifest's `hal` block.
17
+ //
18
+ // Extended categories — `rmt`, `ble`, `preferences`, `random`, `fs`, `mqtt`,
19
+ // `mdns`, `ota`, etc. — are OPTIONAL: a framework declares them only if it
20
+ // lowers them. The HalCoverageSchema below uses `.catchall()` so declared
21
+ // extended categories survive zod parsing (they used to be silently stripped,
22
+ // which made esp32's ble/rmt/preferences manifest blocks dead data) and get
23
+ // validated by the manifest validator like any core category. A framework that
24
+ // does not lower an extended category simply omits it.
14
25
  const HAL_CATEGORIES = [
15
26
  'gpio', 'pwm', 'adc', 'dac', 'interrupts', 'tone', 'timing', 'power',
16
27
  'i2c', 'spi', 'uart', 'pulse', 'shift', 'board', 'wdt', 'wifi', 'http',
17
- 'display',
28
+ 'mqtt', 'display',
18
29
  ];
19
30
  const OpStatus = z.enum(['supported', 'stub', 'unsupported', 'probe-inconclusive', 'polyfill']);
20
31
  const HalCategorySchema = z.object({
@@ -32,7 +43,15 @@ const HalCoverageSchema = z.object(Object.fromEntries(HAL_CATEGORIES.map((c) =>
32
43
  c === 'display' ? DisplayCategorySchema : HalCategorySchema,
33
44
  ]))).extend({
34
45
  raw: z.object({ supported: z.boolean() }).default({ supported: true }),
35
- });
46
+ // catchall: extended categories (rmt, ble, preferences, random, fs, mqtt,
47
+ // mdns, ota, ...) declared by a framework that lowers them. Without this,
48
+ // zod's default object parsing silently strips unknown keys, so a manifest's
49
+ // `ble:` / `rmt:` / `preferences:` / `random:` blocks never reached the
50
+ // validator or consumers. The catchall validates them with the same shape as
51
+ // core categories (HalCategorySchema) and preserves them on the parsed
52
+ // object. Frameworks that don't lower an extended category simply omit it.
53
+ // `raw` is excluded — it is the escape hatch, not a HAL category.
54
+ }).catchall(HalCategorySchema);
36
55
  const EntrypointSchema = z.object({
37
56
  entrypointFunctionName: z.string(),
38
57
  requiresLoopFunction: z.boolean(),
@@ -75,6 +94,15 @@ const LibraryResolutionSchema = z.object({
75
94
  tryGenerateLibDecl: z.boolean(),
76
95
  reexportedFrom: z.string().optional(),
77
96
  });
97
+ /**
98
+ * Capability block for an optional subcommand the framework may own (e.g.
99
+ * `cuttlefish doctor`, `cuttlefish licenses`). The manifest only declares
100
+ * availability; the framework's runtime `doctor` / `licenses` exports provide
101
+ * the implementation, and cuttlefish dispatches to them when present.
102
+ */
103
+ const SubcommandCapabilitySchema = z.object({
104
+ available: z.boolean(),
105
+ });
78
106
  const StdLibSupportSchema = z.object({
79
107
  hasVector: z.boolean(),
80
108
  hasString: z.boolean(),
@@ -116,6 +144,11 @@ export const FrameworkManifestSchema = z.object({
116
144
  typeEmission: TypeEmissionSchema,
117
145
  ambientTypes: z.array(z.string()),
118
146
  conformance: ConformanceSchema,
147
+ // Optional subcommand capabilities owned by the framework. When declared
148
+ // `{ available: true }`, the framework exports matching `doctor` / `licenses`
149
+ // functions that cuttlefish dispatches the corresponding CLI subcommands to.
150
+ doctor: SubcommandCapabilitySchema.optional(),
151
+ licenses: SubcommandCapabilitySchema.optional(),
119
152
  });
120
153
  /**
121
154
  * Authoritative map of HAL op kinds that are lowered via the polyfill system
@@ -1,5 +1,5 @@
1
1
  import type { DisplayHALOp } from "./display-op-ir.js";
2
- import type { ResolvedDisplay, TouchProfile, TouchAdapterCodegen } from "./display-profile.js";
2
+ import type { DisplayProfile, ResolvedDisplay, TouchProfile, TouchAdapterCodegen } from "./display-profile.js";
3
3
  import type { DisplayAdapterCode } from "./display-adapter.js";
4
4
  export interface GraphicsCapacity {
5
5
  /** Max nodes the retained tree may hold on this target. */
@@ -18,14 +18,15 @@ export interface PlatformGraphicsStrategy {
18
18
  expression?: string;
19
19
  } | undefined;
20
20
  /**
21
- * True if this strategy emits its own display adapters (AVR, ESP32). When
22
- * false or undefined, generateDisplayAdapter() falls back to the built-in
23
- * Adafruit registry the Arduino path, unchanged.
21
+ * True if this strategy emits its own display adapters (e.g. framework-arduino
22
+ * owns the Adafruit_GFX drivers, framework-native owns SDL). When false or
23
+ * undefined, generateDisplayAdapter() falls back to cuttlefish's generic
24
+ * built-in adapter registry.
24
25
  */
25
26
  providesDisplayAdapter?(): boolean;
26
27
  /**
27
- * Returns the adapter code for one display, or undefined to defer to the
28
- * built-in Adafruit registry. Only called when providesDisplayAdapter()
28
+ * Returns the adapter code for one display, or undefined to defer to cuttlefish's
29
+ * built-in adapter registry. Only called when providesDisplayAdapter()
29
30
  * returns true.
30
31
  */
31
32
  resolveDisplayAdapter?(display: ResolvedDisplay): DisplayAdapterCode | undefined;
@@ -43,6 +44,14 @@ export interface PlatformGraphicsStrategy {
43
44
  * surface a clear compile-time error instead of emitting incompatible code.
44
45
  */
45
46
  resolveTouchAdapter?(touch: TouchProfile): TouchAdapterCodegen | undefined;
47
+ /**
48
+ * Optional hook returning this framework's named display-profile registry
49
+ * (profile name → DisplayProfile). When present, transpile.ts consults it to
50
+ * resolve `display.profile` config values instead of dynamically importing a
51
+ * framework module — so profile names resolve correctly per-framework.
52
+ * Returns undefined to fall back to the legacy dynamic-import path.
53
+ */
54
+ getProfileRegistry?(): Map<string, DisplayProfile>;
46
55
  /** Display driver ids this framework provides (e.g. new Set(["ili9341"])). */
47
56
  supportedDisplayDrivers(): ReadonlySet<string>;
48
57
  /** Target color format — drives transpile-time color resolution.