@typecad/cuttlefish 1.0.0-alpha.11 → 1.0.0-alpha.12

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.
@@ -44,10 +44,10 @@ export declare const BoardSpecSchema: z.ZodObject<{
44
44
  instance: number;
45
45
  role: string;
46
46
  }[] | undefined;
47
+ notes?: string | undefined;
47
48
  alt?: string[] | undefined;
48
49
  warnings?: string[] | undefined;
49
50
  unsafe?: boolean | undefined;
50
- notes?: string | undefined;
51
51
  onboardLed?: boolean | undefined;
52
52
  }, {
53
53
  gpio: number;
@@ -57,10 +57,10 @@ export declare const BoardSpecSchema: z.ZodObject<{
57
57
  instance: number;
58
58
  role: string;
59
59
  }[] | undefined;
60
+ notes?: string | undefined;
60
61
  alt?: string[] | undefined;
61
62
  warnings?: string[] | undefined;
62
63
  unsafe?: boolean | undefined;
63
- notes?: string | undefined;
64
64
  onboardLed?: boolean | undefined;
65
65
  }>, "many">;
66
66
  unsafe: z.ZodArray<z.ZodString, "many">;
@@ -75,21 +75,21 @@ export declare const BoardSpecSchema: z.ZodObject<{
75
75
  tx: z.ZodOptional<z.ZodString>;
76
76
  rx: z.ZodOptional<z.ZodString>;
77
77
  }, "strict", z.ZodTypeAny, {
78
- sda?: string | undefined;
79
- scl?: string | undefined;
78
+ cs?: string | undefined;
80
79
  mosi?: string | undefined;
81
- miso?: string | undefined;
82
80
  sck?: string | undefined;
83
- cs?: string | undefined;
81
+ miso?: string | undefined;
82
+ sda?: string | undefined;
83
+ scl?: string | undefined;
84
84
  tx?: string | undefined;
85
85
  rx?: string | undefined;
86
86
  }, {
87
- sda?: string | undefined;
88
- scl?: string | undefined;
87
+ cs?: string | undefined;
89
88
  mosi?: string | undefined;
90
- miso?: string | undefined;
91
89
  sck?: string | undefined;
92
- cs?: string | undefined;
90
+ miso?: string | undefined;
91
+ sda?: string | undefined;
92
+ scl?: string | undefined;
93
93
  tx?: string | undefined;
94
94
  rx?: string | undefined;
95
95
  }>>;
@@ -103,21 +103,21 @@ export declare const BoardSpecSchema: z.ZodObject<{
103
103
  tx: z.ZodOptional<z.ZodString>;
104
104
  rx: z.ZodOptional<z.ZodString>;
105
105
  }, "strict", z.ZodTypeAny, {
106
- sda?: string | undefined;
107
- scl?: string | undefined;
106
+ cs?: string | undefined;
108
107
  mosi?: string | undefined;
109
- miso?: string | undefined;
110
108
  sck?: string | undefined;
111
- cs?: string | undefined;
109
+ miso?: string | undefined;
110
+ sda?: string | undefined;
111
+ scl?: string | undefined;
112
112
  tx?: string | undefined;
113
113
  rx?: string | undefined;
114
114
  }, {
115
- sda?: string | undefined;
116
- scl?: string | undefined;
115
+ cs?: string | undefined;
117
116
  mosi?: string | undefined;
118
- miso?: string | undefined;
119
117
  sck?: string | undefined;
120
- cs?: string | undefined;
118
+ miso?: string | undefined;
119
+ sda?: string | undefined;
120
+ scl?: string | undefined;
121
121
  tx?: string | undefined;
122
122
  rx?: string | undefined;
123
123
  }>>;
@@ -131,21 +131,21 @@ export declare const BoardSpecSchema: z.ZodObject<{
131
131
  tx: z.ZodOptional<z.ZodString>;
132
132
  rx: z.ZodOptional<z.ZodString>;
133
133
  }, "strict", z.ZodTypeAny, {
134
- sda?: string | undefined;
135
- scl?: string | undefined;
134
+ cs?: string | undefined;
136
135
  mosi?: string | undefined;
137
- miso?: string | undefined;
138
136
  sck?: string | undefined;
139
- cs?: string | undefined;
137
+ miso?: string | undefined;
138
+ sda?: string | undefined;
139
+ scl?: string | undefined;
140
140
  tx?: string | undefined;
141
141
  rx?: string | undefined;
142
142
  }, {
143
- sda?: string | undefined;
144
- scl?: string | undefined;
143
+ cs?: string | undefined;
145
144
  mosi?: string | undefined;
146
- miso?: string | undefined;
147
145
  sck?: string | undefined;
148
- cs?: string | undefined;
146
+ miso?: string | undefined;
147
+ sda?: string | undefined;
148
+ scl?: string | undefined;
149
149
  tx?: string | undefined;
150
150
  rx?: string | undefined;
151
151
  }>>;
@@ -230,18 +230,18 @@ export declare const BoardSpecSchema: z.ZodObject<{
230
230
  maxValue: z.ZodNumber;
231
231
  referenceVoltages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
232
232
  }, "strict", z.ZodTypeAny, {
233
+ maxValue: number;
233
234
  instance: number;
234
235
  channels: number;
235
236
  resolution: number;
236
237
  referenceVoltage: number;
237
- maxValue: number;
238
238
  referenceVoltages?: Record<string, number> | undefined;
239
239
  }, {
240
+ maxValue: number;
240
241
  instance: number;
241
242
  channels: number;
242
243
  resolution: number;
243
244
  referenceVoltage: number;
244
- maxValue: number;
245
245
  referenceVoltages?: Record<string, number> | undefined;
246
246
  }>, "many">;
247
247
  pwm: z.ZodObject<{
@@ -273,14 +273,14 @@ export declare const BoardSpecSchema: z.ZodObject<{
273
273
  bits: z.ZodUnion<[z.ZodLiteral<8>, z.ZodLiteral<16>, z.ZodLiteral<32>, z.ZodLiteral<64>]>;
274
274
  features: z.ZodOptional<z.ZodArray<z.ZodEnum<["pwm", "capture", "compare", "interrupt", "dma"]>, "many">>;
275
275
  }, "strict", z.ZodTypeAny, {
276
+ bits: 8 | 16 | 32 | 64;
276
277
  type: "general" | "high_speed" | "rtc" | "sys";
277
278
  instance: number;
278
- bits: 8 | 16 | 32 | 64;
279
279
  features?: ("pwm" | "capture" | "compare" | "interrupt" | "dma")[] | undefined;
280
280
  }, {
281
+ bits: 8 | 16 | 32 | 64;
281
282
  type: "general" | "high_speed" | "rtc" | "sys";
282
283
  instance: number;
283
- bits: 8 | 16 | 32 | 64;
284
284
  features?: ("pwm" | "capture" | "compare" | "interrupt" | "dma")[] | undefined;
285
285
  }>, "many">;
286
286
  wifi: z.ZodObject<{
@@ -365,68 +365,42 @@ export declare const BoardSpecSchema: z.ZodObject<{
365
365
  }>;
366
366
  }, "strict", z.ZodTypeAny, {
367
367
  architecture: string;
368
- mcuId: string;
369
- mcuName: string;
370
- boardId: string;
371
- boardName: string;
372
- vendor: string;
373
- clockSpeed: number;
374
- fqbn: string;
375
- arduinoDefine: string;
376
- moduleFlash: number | null;
377
- externalRam: number | null;
378
- gpioRange: [number, number];
379
- excludedGpio: number[];
380
- pins: {
381
- gpio: number;
382
- capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
383
- functions?: {
384
- type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
385
- instance: number;
386
- role: string;
387
- }[] | undefined;
388
- alt?: string[] | undefined;
389
- warnings?: string[] | undefined;
390
- unsafe?: boolean | undefined;
391
- notes?: string | undefined;
392
- onboardLed?: boolean | undefined;
393
- }[];
394
368
  i2c: Record<string, {
395
- sda?: string | undefined;
396
- scl?: string | undefined;
369
+ cs?: string | undefined;
397
370
  mosi?: string | undefined;
398
- miso?: string | undefined;
399
371
  sck?: string | undefined;
400
- cs?: string | undefined;
372
+ miso?: string | undefined;
373
+ sda?: string | undefined;
374
+ scl?: string | undefined;
401
375
  tx?: string | undefined;
402
376
  rx?: string | undefined;
403
377
  }>;
404
378
  spi: Record<string, {
405
- sda?: string | undefined;
406
- scl?: string | undefined;
379
+ cs?: string | undefined;
407
380
  mosi?: string | undefined;
408
- miso?: string | undefined;
409
381
  sck?: string | undefined;
410
- cs?: string | undefined;
382
+ miso?: string | undefined;
383
+ sda?: string | undefined;
384
+ scl?: string | undefined;
411
385
  tx?: string | undefined;
412
386
  rx?: string | undefined;
413
387
  }>;
414
388
  uart: Record<string, {
415
- sda?: string | undefined;
416
- scl?: string | undefined;
389
+ cs?: string | undefined;
417
390
  mosi?: string | undefined;
418
- miso?: string | undefined;
419
391
  sck?: string | undefined;
420
- cs?: string | undefined;
392
+ miso?: string | undefined;
393
+ sda?: string | undefined;
394
+ scl?: string | undefined;
421
395
  tx?: string | undefined;
422
396
  rx?: string | undefined;
423
397
  }>;
424
398
  adc: {
399
+ maxValue: number;
425
400
  instance: number;
426
401
  channels: number;
427
402
  resolution: number;
428
403
  referenceVoltage: number;
429
- maxValue: number;
430
404
  referenceVoltages?: Record<string, number> | undefined;
431
405
  }[];
432
406
  pwm: {
@@ -439,6 +413,37 @@ export declare const BoardSpecSchema: z.ZodObject<{
439
413
  vid: string;
440
414
  pid: string;
441
415
  };
416
+ wifi: {
417
+ type: "wifi" | "wifi6";
418
+ supportsStation: boolean;
419
+ supportsAp: boolean;
420
+ };
421
+ mcuId: string;
422
+ mcuName: string;
423
+ boardId: string;
424
+ boardName: string;
425
+ vendor: string;
426
+ clockSpeed: number;
427
+ fqbn: string;
428
+ arduinoDefine: string;
429
+ moduleFlash: number | null;
430
+ externalRam: number | null;
431
+ gpioRange: [number, number];
432
+ excludedGpio: number[];
433
+ pins: {
434
+ gpio: number;
435
+ capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
436
+ functions?: {
437
+ type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
438
+ instance: number;
439
+ role: string;
440
+ }[] | undefined;
441
+ notes?: string | undefined;
442
+ alt?: string[] | undefined;
443
+ warnings?: string[] | undefined;
444
+ unsafe?: boolean | undefined;
445
+ onboardLed?: boolean | undefined;
446
+ }[];
442
447
  unsafe: string[];
443
448
  analog: string[];
444
449
  peripheralInstances: {
@@ -459,9 +464,9 @@ export declare const BoardSpecSchema: z.ZodObject<{
459
464
  }[];
460
465
  };
461
466
  timers: {
467
+ bits: 8 | 16 | 32 | 64;
462
468
  type: "general" | "high_speed" | "rtc" | "sys";
463
469
  instance: number;
464
- bits: 8 | 16 | 32 | 64;
465
470
  features?: ("pwm" | "capture" | "compare" | "interrupt" | "dma")[] | undefined;
466
471
  }[];
467
472
  features: {
@@ -473,11 +478,6 @@ export declare const BoardSpecSchema: z.ZodObject<{
473
478
  hardwareRng: boolean;
474
479
  fpu: boolean;
475
480
  };
476
- wifi: {
477
- type: "wifi" | "wifi6";
478
- supportsStation: boolean;
479
- supportsAp: boolean;
480
- };
481
481
  bluetooth: {
482
482
  type: "classic" | "ble" | "dual";
483
483
  version: string;
@@ -489,75 +489,49 @@ export declare const BoardSpecSchema: z.ZodObject<{
489
489
  externalRam?: number | undefined;
490
490
  rtcMemory?: number | undefined;
491
491
  };
492
- description?: string | undefined;
493
492
  touch?: {
494
493
  pins: string[];
495
494
  channels: number;
496
495
  } | null | undefined;
496
+ description?: string | undefined;
497
497
  }, {
498
498
  architecture: string;
499
- mcuId: string;
500
- mcuName: string;
501
- boardId: string;
502
- boardName: string;
503
- vendor: string;
504
- clockSpeed: number;
505
- fqbn: string;
506
- arduinoDefine: string;
507
- moduleFlash: number | null;
508
- externalRam: number | null;
509
- gpioRange: [number, number];
510
- excludedGpio: number[];
511
- pins: {
512
- gpio: number;
513
- capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
514
- functions?: {
515
- type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
516
- instance: number;
517
- role: string;
518
- }[] | undefined;
519
- alt?: string[] | undefined;
520
- warnings?: string[] | undefined;
521
- unsafe?: boolean | undefined;
522
- notes?: string | undefined;
523
- onboardLed?: boolean | undefined;
524
- }[];
525
499
  i2c: Record<string, {
526
- sda?: string | undefined;
527
- scl?: string | undefined;
500
+ cs?: string | undefined;
528
501
  mosi?: string | undefined;
529
- miso?: string | undefined;
530
502
  sck?: string | undefined;
531
- cs?: string | undefined;
503
+ miso?: string | undefined;
504
+ sda?: string | undefined;
505
+ scl?: string | undefined;
532
506
  tx?: string | undefined;
533
507
  rx?: string | undefined;
534
508
  }>;
535
509
  spi: Record<string, {
536
- sda?: string | undefined;
537
- scl?: string | undefined;
510
+ cs?: string | undefined;
538
511
  mosi?: string | undefined;
539
- miso?: string | undefined;
540
512
  sck?: string | undefined;
541
- cs?: string | undefined;
513
+ miso?: string | undefined;
514
+ sda?: string | undefined;
515
+ scl?: string | undefined;
542
516
  tx?: string | undefined;
543
517
  rx?: string | undefined;
544
518
  }>;
545
519
  uart: Record<string, {
546
- sda?: string | undefined;
547
- scl?: string | undefined;
520
+ cs?: string | undefined;
548
521
  mosi?: string | undefined;
549
- miso?: string | undefined;
550
522
  sck?: string | undefined;
551
- cs?: string | undefined;
523
+ miso?: string | undefined;
524
+ sda?: string | undefined;
525
+ scl?: string | undefined;
552
526
  tx?: string | undefined;
553
527
  rx?: string | undefined;
554
528
  }>;
555
529
  adc: {
530
+ maxValue: number;
556
531
  instance: number;
557
532
  channels: number;
558
533
  resolution: number;
559
534
  referenceVoltage: number;
560
- maxValue: number;
561
535
  referenceVoltages?: Record<string, number> | undefined;
562
536
  }[];
563
537
  pwm: {
@@ -570,6 +544,37 @@ export declare const BoardSpecSchema: z.ZodObject<{
570
544
  vid: string;
571
545
  pid: string;
572
546
  };
547
+ wifi: {
548
+ type: "wifi" | "wifi6";
549
+ supportsStation: boolean;
550
+ supportsAp: boolean;
551
+ };
552
+ mcuId: string;
553
+ mcuName: string;
554
+ boardId: string;
555
+ boardName: string;
556
+ vendor: string;
557
+ clockSpeed: number;
558
+ fqbn: string;
559
+ arduinoDefine: string;
560
+ moduleFlash: number | null;
561
+ externalRam: number | null;
562
+ gpioRange: [number, number];
563
+ excludedGpio: number[];
564
+ pins: {
565
+ gpio: number;
566
+ capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
567
+ functions?: {
568
+ type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
569
+ instance: number;
570
+ role: string;
571
+ }[] | undefined;
572
+ notes?: string | undefined;
573
+ alt?: string[] | undefined;
574
+ warnings?: string[] | undefined;
575
+ unsafe?: boolean | undefined;
576
+ onboardLed?: boolean | undefined;
577
+ }[];
573
578
  unsafe: string[];
574
579
  analog: string[];
575
580
  peripheralInstances: {
@@ -590,9 +595,9 @@ export declare const BoardSpecSchema: z.ZodObject<{
590
595
  }[];
591
596
  };
592
597
  timers: {
598
+ bits: 8 | 16 | 32 | 64;
593
599
  type: "general" | "high_speed" | "rtc" | "sys";
594
600
  instance: number;
595
- bits: 8 | 16 | 32 | 64;
596
601
  features?: ("pwm" | "capture" | "compare" | "interrupt" | "dma")[] | undefined;
597
602
  }[];
598
603
  features: {
@@ -604,11 +609,6 @@ export declare const BoardSpecSchema: z.ZodObject<{
604
609
  hardwareRng: boolean;
605
610
  fpu: boolean;
606
611
  };
607
- wifi: {
608
- type: "wifi" | "wifi6";
609
- supportsStation: boolean;
610
- supportsAp: boolean;
611
- };
612
612
  bluetooth: {
613
613
  type: "classic" | "ble" | "dual";
614
614
  version: string;
@@ -620,11 +620,11 @@ export declare const BoardSpecSchema: z.ZodObject<{
620
620
  externalRam?: number | undefined;
621
621
  rtcMemory?: number | undefined;
622
622
  };
623
- description?: string | undefined;
624
623
  touch?: {
625
624
  pins: string[];
626
625
  channels: number;
627
626
  } | null | undefined;
627
+ description?: string | undefined;
628
628
  }>;
629
629
  export type BoardSpec = z.infer<typeof BoardSpecSchema>;
630
630
  export declare function parseBoardSpec(jsoncText: string): BoardSpec;
@@ -300,9 +300,12 @@ export default {
300
300
  return "";
301
301
  }
302
302
  function isContainerVar(node) {
303
- if (node.type === "Identifier" && containerVars.has(node.name)) return true;
304
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && FUNCTIONAL_METHODS.has(node.callee.property.name)) return true;
305
- return false;
303
+ // Only identifiers with a known container type count. A
304
+ // CallExpression receiver (e.g. arr.map(f).filter(g)) is the RESULT
305
+ // of a functional method — on arrays that's another array, which
306
+ // the transpiler lowers; treating it as a container flagged every
307
+ // supported method chain as an error.
308
+ return node.type === "Identifier" && containerVars.has(node.name);
306
309
  }
307
310
  return {
308
311
  VariableDeclarator(node) {
@@ -43,6 +43,40 @@ export const RULES = [
43
43
  justification: "AVR freeHeap() measures the gap between the stack and heap via the avr-libc __heap_start/__brkval linker symbols; pointer-to-integer conversion is the only way to compute a byte distance between two addresses on a target with no numeric free-heap API.",
44
44
  kind: "polyfill",
45
45
  },
46
+ {
47
+ // framework-zephyr BLE lowering: characteristic read handlers are TS
48
+ // functions with heterogeneous inferred return types (const char*,
49
+ // double), but the GATT attribute table registers one C callback for
50
+ // every characteristic. Handlers are stored in a fixed-size void*
51
+ // table (__tc_ble.on_read[]) and the characteristic index rides in
52
+ // bt_gatt_attr::user_data; the dispatcher casts back to the concrete
53
+ // signature per the char's type field. C++14 has no type-safe way to
54
+ // store or invoke heterogeneous signatures through one type-erased
55
+ // slot. Marker-based: every site references the table or its index
56
+ // channel.
57
+ detect: /__tc_ble\.on_read|__tc_ble_attrs|attr->user_data/,
58
+ justification: "framework-zephyr BLE lowering stores heterogeneously-typed read handlers in a void* table (__tc_ble.on_read[]) keyed by bt_gatt_attr::user_data; C++14 offers no type-safe mechanism to store or invoke mixed signatures through one type-erased slot, so the store/dispatch sites must reinterpret_cast.",
59
+ kind: "polyfill",
60
+ },
61
+ {
62
+ // Zephyr C APIs take byte buffers as uint8_t*/void* (bt_data.data,
63
+ // mqtt_utf8.utf8, MQTT payload.data, spi_buf.buf, i2c_read tx/rx)
64
+ // while cuttlefish lowers TS buffers and strings to char*/uint8_t
65
+ // arrays. The pointer reinterpretation happens exactly at the C API
66
+ // boundary in the framework shims (ble/mqtt/i2c/spi lowerings).
67
+ detect: /reinterpret_cast<(?:const )?uint8_t\*>|\.buf = reinterpret_cast<void\*>/,
68
+ justification: "Zephyr's C APIs (bt_data, mqtt_utf8, MQTT payload, spi_buf, i2c_read) accept byte buffers as uint8_t*/void* while the transpiler lowers TS buffers/strings to char*/uint8_t arrays; reinterpret_cast at the API boundary is the only way to pass the lowered buffer.",
69
+ kind: "polyfill",
70
+ },
71
+ {
72
+ // Raw register declarations (framework-native and MCU register
73
+ // exports) map a literal address to a volatile uint32_t* MMIO
74
+ // pointer. C++14 has no standard integer→pointer conversion other
75
+ // than reinterpret_cast.
76
+ detect: /reinterpret_cast<volatile uint32_t\*>/,
77
+ justification: "MMIO register declarations convert a literal address to a volatile uint32_t* pointer; reinterpret_cast is the only standard C++14 integer-to-pointer conversion.",
78
+ kind: "other",
79
+ },
46
80
  ],
47
81
  },
48
82
  { id: "M5-2-8", title: "No pointer arithmetic out of bounds", severity: "required", category: "C", enabled: true },
@@ -320,6 +320,15 @@ export function buildEmitterContext(program, options) {
320
320
  if (!programAnalysis.usesNativeTiming && entryHasUI()) {
321
321
  programAnalysis.usesNativeTiming = true;
322
322
  }
323
+ // The UI runtime header calls constrain() in the progress/range node draw
324
+ // and touch-slider paths (injected by the emitter, not present in user
325
+ // source), so a mounted UI needs the constrain polyfill even when
326
+ // usesConstrain is false. OR entryHasUI() into the flag the native
327
+ // strategy reads when gating the constrain helper, mirroring the
328
+ // usesNativeTiming handling above.
329
+ if (!programAnalysis.usesConstrain && entryHasUI()) {
330
+ programAnalysis.usesConstrain = true;
331
+ }
323
332
  options.platformContext.analysis = programAnalysis;
324
333
  if (!options.platformContext.architecture && program.boardConstants) {
325
334
  options.platformContext.architecture = program.boardConstants.get("architecture");
@@ -416,8 +425,12 @@ export function buildEmitterContext(program, options) {
416
425
  }
417
426
  // The UI runtime's per-frame tick uses millis() (injected by the emitter,
418
427
  // not authored in user source), so keep the millis() shim when a UI is
419
- // mounted even if the source-level analysis didn't flag usesMillis.
420
- if (!programAnalysis.usesMillis && !entryHasUI()) {
428
+ // mounted even if the source-level analysis didn't flag usesMillis. The
429
+ // async runtime and the setInterval/setTimeout scheduler also poll
430
+ // millis() without any user-source millis() call (Async.sleep lowers to a
431
+ // raw hal-op the timing scanners can't see) — mirror the usesNativeTiming
432
+ // derivation and keep the shim for those hidden consumers too.
433
+ if (!programAnalysis.usesMillis && !programAnalysis.hasAsync && programAnalysis.timerCallCount === 0 && !entryHasUI()) {
421
434
  shimLines = shimLines.filter(l => !l.includes('millis()'));
422
435
  }
423
436
  // Strip the nullish helper FUNCTIONS (not the CUTTLEFISH_UNDEFINED macro)
@@ -112,7 +112,10 @@ add(ts.SyntaxKind.BigIntLiteral, {
112
112
  hint: "Use a number literal with an explicit fixed-width type annotation.",
113
113
  code: "TS2CPP_NO_EQUIVALENT",
114
114
  eslint: {
115
- selector: "Literal[bigInt=true]",
115
+ // The parser emits the attribute as lowercase `bigint` (a string); the
116
+ // previous [bigInt=true] form matched nothing, so the editor never
117
+ // flagged what the build-time prescan did.
118
+ selector: "Literal[bigint]",
116
119
  message: "[transpiler] BigInt literals are not supported (no C++ equivalent for embedded targets). Use a number literal with an explicit fixed-width type.",
117
120
  },
118
121
  });
@@ -222,7 +225,10 @@ const CONTEXT_LINT_RULES = [
222
225
  source: "context",
223
226
  },
224
227
  {
225
- selector: "TSTypeOperator[type='keyof']",
228
+ // The parser names the attribute `operator`, not `type` — the previous
229
+ // [type='keyof'] form matched nothing, so the editor never flagged what
230
+ // the build-time prescan did.
231
+ selector: "TSTypeOperator[operator='keyof']",
226
232
  message: "[transpiler] the keyof operator is not supported (no C++ equivalent). Use a string union or a switch over field names.",
227
233
  source: "context",
228
234
  },
@@ -317,12 +323,22 @@ const CONTEXT_LINT_RULES = [
317
323
  source: "context",
318
324
  },
319
325
  {
320
- selector: "CallExpression > MemberExpression.callee[property.name=/^(bind|call|apply)$/]",
326
+ // ui.bind is a recognized UI authoring call (intercepted by the
327
+ // transpiler's call-lowering), not Function.prototype.bind — the :not()
328
+ // guard exempts the `ui` receiver so the editor selector matches the
329
+ // build-time prescan (checkContextSensitive exempts exactly ui.bind).
330
+ // The exemption lives in the selector because generateEslintConfig()
331
+ // drops non-selector fields when rendering no-restricted-syntax. Only
332
+ // `bind` is exempted — ui.call/ui.apply are not UI APIs and the prescan
333
+ // flags them, so the editor does too.
334
+ selector: "CallExpression > MemberExpression.callee[property.name='bind']:not([object.name='ui'])",
335
+ message: "[transpiler] .bind/.call/.apply rebind `this` at call time, which has no C++ lowering (this is a fixed pointer). Call the function/method directly.",
336
+ source: "context",
337
+ },
338
+ {
339
+ selector: "CallExpression > MemberExpression.callee[property.name=/^(call|apply)$/]",
321
340
  message: "[transpiler] .bind/.call/.apply rebind `this` at call time, which has no C++ lowering (this is a fixed pointer). Call the function/method directly.",
322
341
  source: "context",
323
- // ui.bind is a recognized UI authoring call (intercepted by the
324
- // transpiler's call-lowering), not Function.prototype.bind.
325
- filter: "(node) => { const o = node.callee.object; return !(o && o.type === 'Identifier' && o.name === 'ui'); }",
326
342
  },
327
343
  {
328
344
  selector: "NewExpression[callee.name='Function']",
@@ -52,6 +52,10 @@ export interface ProgramAnalysisResult {
52
52
  * __tc_TimerRuntime::MAX_TIMERS to the observed count (floor 1) rather than
53
53
  * a blind constant, so a one-timer program links one slot, not eight. */
54
54
  timerCallCount: number;
55
+ /** True when any function is declared `async` — the async runtime polls
56
+ * millis() every pump even when no timing call appears in user source
57
+ * (Async.sleep lowers to a raw hal-op the text scanners can't see). */
58
+ hasAsync: boolean;
55
59
  /** ESP32 peripheral usage — framework-esp32 gates its IDF driver blocks and
56
60
  * forced includes on these. Detected from HAL-op operation names, the same
57
61
  * way usesUart/usesSPI/usesI2C are. Other frameworks have no CUTTLEFISH_*
@@ -103,8 +107,6 @@ export interface ProgramAnalysisResult {
103
107
  /** Worker offload usage. Detected from worker.* ops. Frameworks gate the
104
108
  * worker_runtime polyfill (and its per-framework backing) on this. */
105
109
  usesWorker: boolean;
106
- /** Native/desktop: std::chrono / steady_clock usage (gates <chrono>). */
107
- usesChrono: boolean;
108
110
  /** Native/desktop: std::set usage (gates <set>). */
109
111
  usesSet: boolean;
110
112
  /** Native/desktop: std::algorithm usage (std::sort/find/transform etc., gates <algorithm>). */
@@ -77,9 +77,6 @@ function analyzeExpression(expr, result, strategy) {
77
77
  if (expr.value.includes('__tc_str_ptr')) {
78
78
  result.usesStrPtr = true;
79
79
  }
80
- if (/std::chrono|steady_clock/.test(expr.value)) {
81
- result.usesChrono = true;
82
- }
83
80
  if (/std::set\s*</.test(expr.value)) {
84
81
  result.usesSet = true;
85
82
  }
@@ -844,7 +841,7 @@ export function analyzeProgram(program, strategy) {
844
841
  usesHwtimer: false,
845
842
  usesCapacitive: false,
846
843
  usesWorker: false,
847
- usesChrono: false,
844
+ hasAsync: false,
848
845
  usesSet: false,
849
846
  usesAlgorithm: false,
850
847
  usesCstdio: false,
@@ -1010,6 +1007,7 @@ export function analyzeProgram(program, strategy) {
1010
1007
  // consume site. Without this gate, every AVR program pulled in the Timer0
1011
1008
  // ISR even when it never uses timing.
1012
1009
  const hasAsync = program.functions.some(fn => fn.isAsync);
1010
+ result.hasAsync = hasAsync;
1013
1011
  result.usesNativeTiming = result.usesMillis
1014
1012
  || result.usesTiming
1015
1013
  || result.timerCallCount > 0
@@ -5,4 +5,4 @@ import type { RuntimePolyfillIR } from "../api/shared/index.js";
5
5
  * Build a RuntimePolyfillIR for the async Promise runtime, if the program
6
6
  * has async functions and the target architecture has stdlib support.
7
7
  */
8
- export declare function buildAsyncRuntimePolyfill(program: ProgramIR, ctx: PlatformContext | undefined, target: string, queueCapacity?: number): RuntimePolyfillIR | null;
8
+ export declare function buildAsyncRuntimePolyfill(program: ProgramIR, ctx: PlatformContext | undefined, target: string, queueCapacity?: number, strategy?: import("../api/shared/platform-strategy.js").PlatformStrategy): RuntimePolyfillIR | null;