@typecad/cuttlefish 0.1.0-alpha.2 → 1.0.0-alpha.11
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.
- package/README.md +10 -10
- package/dist/api/board-types.d.ts +1 -1
- package/dist/api/config.d.ts +38 -2
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +4 -3
- package/dist/api/shared/display-adapter.js +15 -118
- package/dist/api/shared/display-adapters/sdl.js +38 -31
- package/dist/api/shared/display-profile.d.ts +47 -25
- package/dist/api/shared/display-profile.js +31 -164
- package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
- package/dist/api/shared/framework-manifest-registry.js +27 -0
- package/dist/api/shared/framework-manifest.d.ts +595 -0
- package/dist/api/shared/framework-manifest.js +193 -0
- package/dist/api/shared/glcdfont.d.ts +12 -0
- package/dist/api/shared/glcdfont.js +124 -0
- package/dist/api/shared/graphics-strategy.d.ts +37 -0
- package/dist/api/shared/hal-op-ir.d.ts +764 -1
- package/dist/api/shared/hal-op-ir.js +129 -1
- package/dist/api/shared/index.d.ts +22 -2
- package/dist/api/shared/index.js +26 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
- package/dist/api/shared/native-display-op-resolver.js +64 -0
- package/dist/api/shared/platform-strategy.d.ts +119 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +95 -13
- package/dist/api/shared/spdx-licenses.d.ts +139 -0
- package/dist/api/shared/spdx-licenses.js +385 -0
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/types.d.ts +8 -0
- package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
- package/dist/api/shared/validate-framework-manifest.js +494 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli-utils.d.ts +1 -0
- package/dist/cli-utils.js +3 -1
- package/dist/cli.js +249 -32
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +23 -3
- package/dist/config-schema.d.ts +112 -70
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +72 -75
- package/dist/create/board-spec.js +0 -1
- package/dist/create/framework-catalog.d.ts +64 -0
- package/dist/create/framework-catalog.js +136 -0
- package/dist/create/index.d.ts +5 -1
- package/dist/create/index.js +3 -1
- package/dist/create/init-scaffold.d.ts +12 -3
- package/dist/create/init-scaffold.js +49 -14
- package/dist/create/init-templates.d.ts +4 -0
- package/dist/create/init-templates.js +271 -17
- package/dist/create/init-wizard.js +41 -27
- package/dist/create/install-deps.d.ts +32 -0
- package/dist/create/install-deps.js +46 -0
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/cpp-emitter.js +4 -3
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +28 -1
- package/dist/emit/emitters/function-emitter-impl.js +153 -53
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.d.ts +6 -0
- package/dist/emit/emitters/output-finalizer.js +101 -12
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +372 -30
- package/dist/emit/emitters/top-level-prep.js +8 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +33 -9
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +91 -6
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +15 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +34 -5
- package/dist/emit/utils/async-state-machine.js +221 -125
- package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
- package/dist/emit/utils/hal-op-cpp-type.js +40 -0
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +40 -26
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +9 -5
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +207 -6
- package/dist/ir/feature-registry.js +7 -25
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.d.ts +5 -2
- package/dist/ir/hal/hal-emitter.js +50 -18
- package/dist/ir/hal/hal-parser.d.ts +6 -0
- package/dist/ir/hal/hal-parser.js +81 -0
- package/dist/ir/hal/hal-plugins.js +733 -1
- package/dist/ir/identifier-collector.js +35 -0
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +16 -19
- package/dist/ir/memory-budget-validation.js +1 -0
- package/dist/ir/network-validation.d.ts +4 -0
- package/dist/ir/network-validation.js +184 -0
- package/dist/ir/ownership-analysis.js +39 -1
- package/dist/ir/peripheral-ownership.js +5 -0
- package/dist/ir/peripheral-validation.d.ts +1 -1
- package/dist/ir/peripheral-validation.js +6 -3
- package/dist/ir/pin-alias-conflict.d.ts +1 -1
- package/dist/ir/pin-alias-conflict.js +2 -1
- package/dist/ir/pin-capability-validation.js +71 -32
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +54 -18
- package/dist/ir/pin-safety.d.ts +1 -1
- package/dist/ir/pin-safety.js +2 -1
- package/dist/ir/program-analysis.d.ts +81 -0
- package/dist/ir/program-analysis.js +429 -0
- package/dist/ir/pulldown-validation.d.ts +1 -1
- package/dist/ir/pulldown-validation.js +2 -1
- package/dist/ir/pwm-timer-sharing.d.ts +1 -1
- package/dist/ir/pwm-timer-sharing.js +2 -1
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/resource-analysis.js +2 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
- package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
- package/dist/ir/timing-validation.d.ts +6 -1
- package/dist/ir/timing-validation.js +61 -13
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +62 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/hal-emit-helpers.js +1 -1
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +137 -19
- package/dist/ir/try-catch-validation.js +2 -0
- package/dist/ir/type-resolution.js +20 -2
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/unit-suspicion-validation.js +9 -7
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +15 -8
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/libdef/c-to-decl.d.ts +27 -0
- package/dist/libdef/c-to-decl.js +397 -0
- package/dist/libdef/component-decls.d.ts +2 -0
- package/dist/libdef/component-decls.js +6 -0
- package/dist/libdef/component-discovery.d.ts +43 -0
- package/dist/libdef/component-discovery.js +83 -0
- package/dist/libdef/cpp-to-decl.d.ts +9 -0
- package/dist/libdef/cpp-to-decl.js +72 -0
- package/dist/libdef/registry.js +5 -2
- package/dist/lint-cache.d.ts +59 -0
- package/dist/lint-cache.js +257 -0
- package/dist/orchestrator/graph-builder.js +14 -9
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/testing.d.ts +8 -2
- package/dist/testing.js +9 -2
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +191 -49
- package/dist/types.d.ts +22 -3
- package/dist/ui-hook.d.ts +17 -3
- package/dist/utils/cli.js +123 -2
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +50 -0
- package/dist/utils/ui.js +3 -1
- package/package.json +23 -3
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/ir/heap-array-validation.d.ts +0 -24
- package/dist/ir/heap-array-validation.js +0 -29
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Extracted from cpp-emitter.ts
|
|
5
5
|
*/
|
|
6
6
|
import { escapeCppStringLiteral } from "../../utils/strings.js";
|
|
7
|
+
import { routeHALOp } from "../route-hal-op.js";
|
|
7
8
|
/**
|
|
8
9
|
* Converts a string to PascalCase.
|
|
9
10
|
* Used for generating class names from function names.
|
|
@@ -53,6 +54,20 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
53
54
|
segments.push({ preStatements: currentPre, awaitedCallee: stmt.callee, awaitedArgs: stmt.args });
|
|
54
55
|
currentPre = [];
|
|
55
56
|
}
|
|
57
|
+
else if (
|
|
58
|
+
// Chained HAL calls (e.g. `await Http.get(url).send()`) resolve to a
|
|
59
|
+
// block of hal-ops whose LAST statement is the awaited wait marker.
|
|
60
|
+
// Flatten it so the split sees the marker; the leading ops (begin,
|
|
61
|
+
// setters) run as pre-statements of the same segment.
|
|
62
|
+
stmt.kind === "block" &&
|
|
63
|
+
stmt.body.length > 0 &&
|
|
64
|
+
stmt.body[stmt.body.length - 1].kind === "call" &&
|
|
65
|
+
stmt.body[stmt.body.length - 1].isAwaited) {
|
|
66
|
+
const last = stmt.body[stmt.body.length - 1];
|
|
67
|
+
currentPre.push(...stmt.body.slice(0, -1));
|
|
68
|
+
segments.push({ preStatements: currentPre, awaitedCallee: last.callee, awaitedArgs: last.args });
|
|
69
|
+
currentPre = [];
|
|
70
|
+
}
|
|
56
71
|
else {
|
|
57
72
|
currentPre.push(stmt);
|
|
58
73
|
}
|
|
@@ -61,11 +76,23 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
61
76
|
segments.push({ preStatements: currentPre, awaitedArgs: [] });
|
|
62
77
|
const awaitCount = segments.filter((s) => s.awaitedCallee !== undefined).length;
|
|
63
78
|
const stateCount = awaitCount + 1; // STATE_0 … STATE_{awaitCount}; cyclic loops back, linear adds STATE_DONE
|
|
79
|
+
// State enum is emitted as `enum class State` (AUTOSAR A7-2-1), so all
|
|
80
|
+
// references must be scope-qualified as `State::STATE_X`. The stateEnumList
|
|
81
|
+
// (used inside `enum class State { ... }`) uses the bare names; everywhere
|
|
82
|
+
// else (assignments, case labels, comparisons) uses the qualified form.
|
|
64
83
|
const stateNames = [];
|
|
65
|
-
|
|
84
|
+
const qualifiedStateNames = [];
|
|
85
|
+
for (let i = 0; i < stateCount; i++) {
|
|
66
86
|
stateNames.push(`STATE_${i}`);
|
|
67
|
-
|
|
87
|
+
qualifiedStateNames.push(`State::STATE_${i}`);
|
|
88
|
+
}
|
|
89
|
+
if (!isCyclic) {
|
|
68
90
|
stateNames.push("STATE_DONE");
|
|
91
|
+
qualifiedStateNames.push("State::STATE_DONE");
|
|
92
|
+
}
|
|
93
|
+
const Q = (i) => qualifiedStateNames[i];
|
|
94
|
+
const Q_DONE = "State::STATE_DONE";
|
|
95
|
+
const Q_0 = "State::STATE_0";
|
|
69
96
|
// Helper: render a non-await statement as a single C++ line
|
|
70
97
|
const renderStmt = (stmt) => renderStatement(stmt, false, strategy, undefined, undefined, knownFunctionReturnTypes);
|
|
71
98
|
// Collect edge-detection markers from segments
|
|
@@ -101,16 +128,74 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
101
128
|
tapMembers.set(member, i);
|
|
102
129
|
}
|
|
103
130
|
}
|
|
131
|
+
// Collect net-wait (WiFi/HTTP/HAL) markers: awaited hal-ops rewritten to
|
|
132
|
+
// __WIFI_WAIT__/__HTTP_WAIT__/__HAL_WAIT__ calls carrying the op as a
|
|
133
|
+
// hal-expr arg. The strategy lowers the op to start code + poll condition.
|
|
134
|
+
const netInfoMap = new Map();
|
|
135
|
+
for (let i = 0; i < segments.length; i++) {
|
|
136
|
+
const seg = segments[i];
|
|
137
|
+
if ((seg.awaitedCallee === "__WIFI_WAIT__" || seg.awaitedCallee === "__HTTP_WAIT__" || seg.awaitedCallee === "__BLE_WAIT__" || seg.awaitedCallee === "__HAL_WAIT__") &&
|
|
138
|
+
seg.awaitedArgs[0]?.kind === "hal-expr") {
|
|
139
|
+
const op = seg.awaitedArgs[0].operation;
|
|
140
|
+
netInfoMap.set(i, netWaitInfo(op, strategy));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ── Render each state ──────────────────────────────────────────────────
|
|
144
|
+
// Decomposition: a state's ENTRY condition comes from the await that ended
|
|
145
|
+
// the PREVIOUS segment (edge / tap / net poll / timed deadline); once the
|
|
146
|
+
// condition fires, the segment body runs and the await ending THIS segment
|
|
147
|
+
// is armed (pin snapshot / tap snapshot / net start op / deadline).
|
|
148
|
+
// Lines that arm the await ending segment i and advance the state.
|
|
149
|
+
const armNextLines = (i, pad) => {
|
|
150
|
+
const seg = segments[i];
|
|
151
|
+
const lines = [];
|
|
152
|
+
if (seg.awaitedCallee === undefined) {
|
|
153
|
+
lines.push(`${pad}_state = ${isCyclic ? Q_0 : Q_DONE};`);
|
|
154
|
+
return lines;
|
|
155
|
+
}
|
|
156
|
+
const edge = edgeInfoMap.get(i);
|
|
157
|
+
const tap = tapInfoMap.get(i);
|
|
158
|
+
const net = netInfoMap.get(i);
|
|
159
|
+
if (edge) {
|
|
160
|
+
lines.push(`${pad}_edgePrev_p${edge.pin} = ${strategy.readDigitalPin?.(String(edge.pin)) ?? `digitalRead(${edge.pin})`};`);
|
|
161
|
+
if (edge.timeout !== null) {
|
|
162
|
+
lines.push(`${pad}_waitUntil = ${strategy.currentTimeMillis()} + ${edge.timeout};`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (tap) {
|
|
166
|
+
lines.push(`${pad}_tapPrev_${i} = __ui_tap_seq;`);
|
|
167
|
+
}
|
|
168
|
+
else if (net) {
|
|
169
|
+
for (const startLine of net.startLines)
|
|
170
|
+
lines.push(`${pad}${startLine}`);
|
|
171
|
+
if (net.timeoutExpr !== null) {
|
|
172
|
+
lines.push(`${pad}_waitUntil = ${strategy.currentTimeMillis()} + ${net.timeoutExpr};`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Plain timed wait (await delay(ms)) — arm its deadline.
|
|
177
|
+
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
178
|
+
lines.push(`${pad}_waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
179
|
+
}
|
|
180
|
+
lines.push(`${pad}_state = ${Q(i + 1)};`);
|
|
181
|
+
return lines;
|
|
182
|
+
};
|
|
104
183
|
const caseLines = [];
|
|
105
184
|
for (let i = 0; i < segments.length; i++) {
|
|
106
185
|
const seg = segments[i];
|
|
107
|
-
const stateName =
|
|
108
|
-
const isTerminal = seg.awaitedCallee === undefined;
|
|
186
|
+
const stateName = Q(i);
|
|
109
187
|
const body = [];
|
|
110
|
-
const edgeSetup = edgeInfoMap.get(i);
|
|
111
188
|
const edgePoll = i > 0 ? edgeInfoMap.get(i - 1) : undefined;
|
|
112
|
-
const tapSetup = tapInfoMap.get(i);
|
|
113
189
|
const tapPoll = i > 0 ? tapInfoMap.get(i - 1) : undefined;
|
|
190
|
+
const netPoll = i > 0 ? netInfoMap.get(i - 1) : undefined;
|
|
191
|
+
// Segment body + arming of the next await, at the given indent.
|
|
192
|
+
const runSegment = (pad) => {
|
|
193
|
+
const lines = [];
|
|
194
|
+
for (const stmt of seg.preStatements)
|
|
195
|
+
lines.push(`${pad}${renderStmt(stmt)}`);
|
|
196
|
+
lines.push(...armNextLines(i, pad));
|
|
197
|
+
return lines;
|
|
198
|
+
};
|
|
114
199
|
if (edgePoll) {
|
|
115
200
|
// Poll state: check pin transition via digitalRead
|
|
116
201
|
const prevVar = `_edgePrev_p${edgePoll.pin}`;
|
|
@@ -121,56 +206,15 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
121
206
|
? `(${cond}) || ${strategy.currentTimeMillis()} >= _waitUntil`
|
|
122
207
|
: cond;
|
|
123
208
|
body.push(` {`);
|
|
124
|
-
body.push(` int _cur = digitalRead(${edgePoll.pin});`);
|
|
209
|
+
body.push(` int _cur = ${strategy.readDigitalPin?.(String(edgePoll.pin)) ?? `digitalRead(${edgePoll.pin})`};`);
|
|
125
210
|
body.push(` if (${fullCond}) {`);
|
|
126
211
|
body.push(` ${prevVar} = _cur;`);
|
|
127
|
-
|
|
128
|
-
body.push(` ${renderStmt(stmt)}`);
|
|
129
|
-
if (edgeSetup) {
|
|
130
|
-
// Next await is also edge detection — set up its pin tracking
|
|
131
|
-
body.push(` _edgePrev_p${edgeSetup.pin} = digitalRead(${edgeSetup.pin});`);
|
|
132
|
-
if (edgeSetup.timeout !== null) {
|
|
133
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${edgeSetup.timeout};`);
|
|
134
|
-
}
|
|
135
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
136
|
-
}
|
|
137
|
-
else if (!isTerminal) {
|
|
138
|
-
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
139
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
140
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
body.push(` _state = ${isCyclic ? "STATE_0" : "STATE_DONE"};`);
|
|
144
|
-
}
|
|
212
|
+
body.push(...runSegment(` `));
|
|
145
213
|
body.push(` } else {`);
|
|
146
214
|
body.push(` ${prevVar} = _cur;`);
|
|
147
215
|
body.push(` }`);
|
|
148
216
|
body.push(` }`);
|
|
149
217
|
}
|
|
150
|
-
else if (edgeSetup) {
|
|
151
|
-
// Setup state: capture initial pin state before polling
|
|
152
|
-
const prevVar = `_edgePrev_p${edgeSetup.pin}`;
|
|
153
|
-
if (i === 0) {
|
|
154
|
-
for (const stmt of seg.preStatements)
|
|
155
|
-
body.push(` ${renderStmt(stmt)}`);
|
|
156
|
-
body.push(` ${prevVar} = digitalRead(${edgeSetup.pin});`);
|
|
157
|
-
if (edgeSetup.timeout !== null) {
|
|
158
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${edgeSetup.timeout};`);
|
|
159
|
-
}
|
|
160
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
body.push(` if (${strategy.currentTimeMillis()} >= _waitUntil) {`);
|
|
164
|
-
for (const stmt of seg.preStatements)
|
|
165
|
-
body.push(` ${renderStmt(stmt)}`);
|
|
166
|
-
body.push(` ${prevVar} = digitalRead(${edgeSetup.pin});`);
|
|
167
|
-
if (edgeSetup.timeout !== null) {
|
|
168
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${edgeSetup.timeout};`);
|
|
169
|
-
}
|
|
170
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
171
|
-
body.push(` }`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
218
|
else if (tapPoll) {
|
|
175
219
|
// Tap-poll state: a previous segment ended with `await ui.onTap()`.
|
|
176
220
|
// Wait until __ui_tap_seq bumps from the captured snapshot. A per-node
|
|
@@ -181,92 +225,42 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
181
225
|
? `__ui_tap_seq != ${prevVar}`
|
|
182
226
|
: `(__ui_tap_seq != ${prevVar}) && (__ui_tap_node == ${nodeFilter})`;
|
|
183
227
|
body.push(` if (${cond}) {`);
|
|
184
|
-
|
|
185
|
-
body.push(` ${renderStmt(stmt)}`);
|
|
186
|
-
// What does the NEXT await (ending this segment) look like? Re-derive
|
|
187
|
-
// from the maps via a fresh lookup so TS doesn't narrow edgeSetup away.
|
|
188
|
-
const nextEdge = edgeInfoMap.get(i) ?? null;
|
|
189
|
-
if (tapSetup) {
|
|
190
|
-
// Next await is also a tap — capture its snapshot now.
|
|
191
|
-
body.push(` _tapPrev_${i} = __ui_tap_seq;`);
|
|
192
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
193
|
-
}
|
|
194
|
-
else if (nextEdge) {
|
|
195
|
-
body.push(` _edgePrev_p${nextEdge.pin} = digitalRead(${nextEdge.pin});`);
|
|
196
|
-
if (nextEdge.timeout !== null) {
|
|
197
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${nextEdge.timeout};`);
|
|
198
|
-
}
|
|
199
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
200
|
-
}
|
|
201
|
-
else if (!isTerminal) {
|
|
202
|
-
// Next await is a normal timed wait (delay) — arm its deadline.
|
|
203
|
-
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
204
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
205
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
body.push(` _state = ${isCyclic ? "STATE_0" : "STATE_DONE"};`);
|
|
209
|
-
}
|
|
228
|
+
body.push(...runSegment(` `));
|
|
210
229
|
body.push(` }`);
|
|
211
230
|
}
|
|
212
|
-
else if (
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
body.push(` }`);
|
|
229
|
-
}
|
|
231
|
+
else if (netPoll) {
|
|
232
|
+
// Net-poll state: a previous segment ended with an awaited WiFi/HTTP
|
|
233
|
+
// op. Wait until its poll condition fires (or its deadline expires).
|
|
234
|
+
const deadline = `${strategy.currentTimeMillis()} >= _waitUntil`;
|
|
235
|
+
const cond = netPoll.pollCond === null
|
|
236
|
+
? deadline
|
|
237
|
+
: netPoll.timeoutExpr !== null
|
|
238
|
+
? `(${netPoll.pollCond}) || ${deadline}`
|
|
239
|
+
: netPoll.pollCond;
|
|
240
|
+
body.push(` if (${cond}) {`);
|
|
241
|
+
body.push(...runSegment(` `));
|
|
242
|
+
body.push(` }`);
|
|
243
|
+
}
|
|
244
|
+
else if (i === 0) {
|
|
245
|
+
// First state runs immediately.
|
|
246
|
+
body.push(...runSegment(` `));
|
|
230
247
|
}
|
|
231
248
|
else {
|
|
232
|
-
//
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (!isTerminal) {
|
|
237
|
-
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
238
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
239
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
body.push(` _state = ${isCyclic ? "STATE_0" : "STATE_DONE"};`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
body.push(` if (${strategy.currentTimeMillis()} >= _waitUntil) {`);
|
|
247
|
-
for (const stmt of seg.preStatements)
|
|
248
|
-
body.push(` ${renderStmt(stmt)}`);
|
|
249
|
-
if (!isTerminal) {
|
|
250
|
-
const ms = seg.awaitedArgs[0] ? renderExpression(seg.awaitedArgs[0], strategy) : "0";
|
|
251
|
-
body.push(` _waitUntil = ${strategy.currentTimeMillis()} + ${ms};`);
|
|
252
|
-
body.push(` _state = STATE_${i + 1};`);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
body.push(` _state = ${isCyclic ? "STATE_0" : "STATE_DONE"};`);
|
|
256
|
-
}
|
|
257
|
-
body.push(` }`);
|
|
258
|
-
}
|
|
249
|
+
// Previous await was a plain timed wait — poll its deadline.
|
|
250
|
+
body.push(` if (${strategy.currentTimeMillis()} >= _waitUntil) {`);
|
|
251
|
+
body.push(...runSegment(` `));
|
|
252
|
+
body.push(` }`);
|
|
259
253
|
}
|
|
260
254
|
caseLines.push(` case ${stateName}:`, ` {`, ...body, ` }`, ` break;`);
|
|
261
255
|
}
|
|
262
256
|
if (!isCyclic)
|
|
263
|
-
caseLines.push(` case
|
|
257
|
+
caseLines.push(` case ${Q_DONE}:`, ` break;`);
|
|
264
258
|
const stateEnumList = stateNames.join(", ");
|
|
265
|
-
const isCompleteExpr = isCyclic ? "false" :
|
|
259
|
+
const isCompleteExpr = isCyclic ? "false" : `_state == ${Q_DONE}`;
|
|
266
260
|
// Build constructor initializer list and edge/tap member declarations
|
|
267
261
|
const edgeMemberArr = Array.from(edgeMembers);
|
|
268
262
|
const tapMemberArr = Array.from(tapMembers.keys());
|
|
269
|
-
const inits = [`_state(
|
|
263
|
+
const inits = [`_state(${Q_0})`, `_waitUntil(0)`];
|
|
270
264
|
for (const m of edgeMemberArr)
|
|
271
265
|
inits.push(`${m}(LOW)`);
|
|
272
266
|
for (const m of tapMemberArr)
|
|
@@ -281,7 +275,7 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
281
275
|
`// Async state machine for ${fnName}`,
|
|
282
276
|
`class ${className} {`,
|
|
283
277
|
`public:`,
|
|
284
|
-
` enum State { ${stateEnumList} };`,
|
|
278
|
+
` enum class State { ${stateEnumList} };`,
|
|
285
279
|
` ${className}() : ${ctorInitList} {}`,
|
|
286
280
|
` void run() {`,
|
|
287
281
|
` switch (_state) {`,
|
|
@@ -289,7 +283,7 @@ export function generateAsyncTaskClass(fnName, fnStatements, strategy, knownFunc
|
|
|
289
283
|
` }`,
|
|
290
284
|
` }`,
|
|
291
285
|
` bool isComplete() const { return ${isCompleteExpr}; }`,
|
|
292
|
-
` void reset() { _state =
|
|
286
|
+
` void reset() { _state = ${Q_0}; _waitUntil = 0;${edgeResetList}${tapResetList} }`,
|
|
293
287
|
`private:`,
|
|
294
288
|
` State _state;`,
|
|
295
289
|
` unsigned long _waitUntil;`,
|
|
@@ -331,6 +325,108 @@ function renderExpression(expr, strategy) {
|
|
|
331
325
|
return "/* complex expr */";
|
|
332
326
|
}
|
|
333
327
|
}
|
|
328
|
+
/** Numeric-or-rendered HAL field → C++ text. */
|
|
329
|
+
function ms(v) {
|
|
330
|
+
return String(v);
|
|
331
|
+
}
|
|
332
|
+
/** True when a timeout field is the literal 0 (wait forever, no deadline). */
|
|
333
|
+
function isZeroTimeout(v) {
|
|
334
|
+
return v === undefined || v === null || String(v).trim() === "0";
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Map an awaitable HAL op to its start statements + poll condition, lowering
|
|
338
|
+
* the start op and poll predicate through the platform strategy. Keep the set
|
|
339
|
+
* of handled ops in sync with AWAITABLE_HAL_OPS in ir/transformers/expressions.ts.
|
|
340
|
+
*
|
|
341
|
+
* Falls back to running the blocking form as the "start" with an immediate
|
|
342
|
+
* completion when the strategy can't lower the split ops.
|
|
343
|
+
*/
|
|
344
|
+
function netWaitInfo(op, strategy) {
|
|
345
|
+
const o = op;
|
|
346
|
+
const route = (routedOp) => routeHALOp(routedOp, strategy);
|
|
347
|
+
const expr = (operation) => route({ operation })?.expression ?? null;
|
|
348
|
+
switch (op.operation) {
|
|
349
|
+
case "timing.delay":
|
|
350
|
+
return { startLines: [], pollCond: null, timeoutExpr: ms(o.ms) };
|
|
351
|
+
case "wifi.connect": {
|
|
352
|
+
const start = route({ operation: "wifi.connect_start", ssid: o.ssid, password: o.password });
|
|
353
|
+
const poll = expr("wifi.is_connected");
|
|
354
|
+
if (start?.code && poll) {
|
|
355
|
+
return {
|
|
356
|
+
startLines: [start.code],
|
|
357
|
+
pollCond: poll,
|
|
358
|
+
timeoutExpr: isZeroTimeout(o.timeoutMs) ? null : ms(o.timeoutMs),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case "wifi.wait_connected": {
|
|
364
|
+
const poll = expr("wifi.is_connected");
|
|
365
|
+
if (poll) {
|
|
366
|
+
return {
|
|
367
|
+
startLines: [],
|
|
368
|
+
pollCond: poll,
|
|
369
|
+
timeoutExpr: isZeroTimeout(o.timeoutMs) ? null : ms(o.timeoutMs),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
case "wifi.wait_disconnected": {
|
|
375
|
+
const poll = expr("wifi.is_connected");
|
|
376
|
+
if (poll) {
|
|
377
|
+
return { startLines: [], pollCond: `!${poll}`, timeoutExpr: null };
|
|
378
|
+
}
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case "wifi.scan": {
|
|
382
|
+
const start = route({ operation: "wifi.scan_start" });
|
|
383
|
+
const poll = expr("wifi.scan_done");
|
|
384
|
+
if (start?.code && poll) {
|
|
385
|
+
return { startLines: [start.code], pollCond: poll, timeoutExpr: null };
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
case "http.send": {
|
|
390
|
+
const start = route({ operation: "http.send_start" });
|
|
391
|
+
const poll = expr("http.done");
|
|
392
|
+
if (start?.code && poll) {
|
|
393
|
+
return { startLines: [start.code], pollCond: poll, timeoutExpr: null };
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
case "ble.until_connected": {
|
|
398
|
+
const start = route({ operation: "ble.until_connected_start" });
|
|
399
|
+
const poll = expr("ble.is_connected");
|
|
400
|
+
if (start?.code && poll) {
|
|
401
|
+
return {
|
|
402
|
+
startLines: [start.code],
|
|
403
|
+
pollCond: poll,
|
|
404
|
+
timeoutExpr: isZeroTimeout(o.timeoutMs) ? null : ms(o.timeoutMs),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case "worker.submit": {
|
|
410
|
+
// Worker offload (Phase 3). `await worker.submit(fn, arg)` rewrites to:
|
|
411
|
+
// submit the worker (start), then poll worker.done on the same handle.
|
|
412
|
+
// The submit op is emitted verbatim as the start line; the poll predicate
|
|
413
|
+
// is worker.done(handle) carrying the SAME handleId. This is a predicate-
|
|
414
|
+
// poll resume (same per-frame cost as net/timer awaits), NOT an ISR-driven
|
|
415
|
+
// resume — that defers with DMA-backed awaits.
|
|
416
|
+
const submit = routeHALOp(op, strategy);
|
|
417
|
+
// Carry the handle through to the poll so submit + done agree on the slot.
|
|
418
|
+
const poll = route({ operation: "worker.done", handleId: o.handleId })?.expression ?? null;
|
|
419
|
+
if (submit?.code && poll) {
|
|
420
|
+
return { startLines: [submit.code], pollCond: poll, timeoutExpr: null };
|
|
421
|
+
}
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Fallback: run the blocking form immediately and complete on the next tick.
|
|
426
|
+
const blocking = routeHALOp(op, strategy);
|
|
427
|
+
const line = blocking?.code ?? (blocking?.expression ? `${blocking.expression};` : `/* unhandled awaited hal-op: ${op.operation} */`);
|
|
428
|
+
return { startLines: [line], pollCond: null, timeoutExpr: "0" };
|
|
429
|
+
}
|
|
334
430
|
function parseEdgeMarker(argText) {
|
|
335
431
|
const match = argText.match(/^__EDGE_(RISING|FALLING)__([a-zA-Z0-9_]+)__T(.*)$/);
|
|
336
432
|
if (!match)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort C++ return type for a HAL expression op.
|
|
3
|
+
* Used by snprintf format inference and expression type inference so
|
|
4
|
+
* string-returning ops (e.g. wifi.scan_ssid → const char*) use %s, not %d.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cppTypeForHalOp(operation: string): string | undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort C++ return type for a HAL expression op.
|
|
3
|
+
* Used by snprintf format inference and expression type inference so
|
|
4
|
+
* string-returning ops (e.g. wifi.scan_ssid → const char*) use %s, not %d.
|
|
5
|
+
*/
|
|
6
|
+
export function cppTypeForHalOp(operation) {
|
|
7
|
+
switch (operation) {
|
|
8
|
+
case "wifi.local_ip":
|
|
9
|
+
case "wifi.mac":
|
|
10
|
+
case "wifi.ap_ip":
|
|
11
|
+
case "wifi.scan_ssid":
|
|
12
|
+
case "http.body":
|
|
13
|
+
case "http.response_header":
|
|
14
|
+
return "const char*";
|
|
15
|
+
case "wifi.is_connected":
|
|
16
|
+
case "wifi.connect":
|
|
17
|
+
case "wifi.wait_connected":
|
|
18
|
+
case "wifi.scan_done":
|
|
19
|
+
case "wifi.ap_start":
|
|
20
|
+
case "wifi.connect_saved":
|
|
21
|
+
case "http.ok":
|
|
22
|
+
case "http.done":
|
|
23
|
+
return "bool";
|
|
24
|
+
case "wifi.status":
|
|
25
|
+
case "wifi.rssi":
|
|
26
|
+
case "wifi.scan":
|
|
27
|
+
case "wifi.scan_count":
|
|
28
|
+
case "wifi.scan_rssi":
|
|
29
|
+
case "wifi.scan_encryption":
|
|
30
|
+
case "wifi.scan_channel":
|
|
31
|
+
case "wifi.ap_client_count":
|
|
32
|
+
case "http.status":
|
|
33
|
+
case "http.content_length":
|
|
34
|
+
return "int";
|
|
35
|
+
case "http.send":
|
|
36
|
+
return "bool";
|
|
37
|
+
default:
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -50,6 +50,8 @@ export function loadFrameworkPackage(packageName, fromDir = process.cwd()) {
|
|
|
50
50
|
} : undefined,
|
|
51
51
|
classNameMapBuilder: typeof mod.buildClassNameMap === "function" ? mod.buildClassNameMap : undefined,
|
|
52
52
|
libDeclGenerator: typeof mod.tryGenerateLibDecl === "function" ? mod.tryGenerateLibDecl : undefined,
|
|
53
|
+
doctor: typeof mod.doctor === "function" ? mod.doctor : undefined,
|
|
54
|
+
licenses: typeof mod.licenses === "function" ? mod.licenses : undefined,
|
|
53
55
|
};
|
|
54
56
|
setLoadedFramework(framework);
|
|
55
57
|
registerPlatformStrategy(strategy);
|
|
@@ -14,6 +14,16 @@ export interface LoadedFramework {
|
|
|
14
14
|
classNameMapBuilder?: (imports: any[]) => Map<string, string>;
|
|
15
15
|
/** Framework library .d.ts declaration generation */
|
|
16
16
|
libDeclGenerator?: (modulePath: string, file: string) => string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Optional subcommand presenters owned by the framework. Cuttlefish dispatches
|
|
19
|
+
* `cuttlefish doctor` / `cuttlefish licenses` to these when the loaded
|
|
20
|
+
* framework provides them; otherwise it prints a no-support message. Each
|
|
21
|
+
* framework decides what (if anything) these do — e.g. framework-arduino
|
|
22
|
+
* checks arduino-cli + board core (doctor) and scans Arduino library licenses
|
|
23
|
+
* (licenses).
|
|
24
|
+
*/
|
|
25
|
+
doctor?: () => void;
|
|
26
|
+
licenses?: (strict: boolean, all: boolean) => void;
|
|
17
27
|
}
|
|
18
28
|
/**
|
|
19
29
|
* Resolves framework-specific library imports (e.g., Arduino libraries).
|
|
@@ -32,6 +42,13 @@ export interface FrameworkToolchain {
|
|
|
32
42
|
compile(options: ToolchainOptions): CompileResult;
|
|
33
43
|
upload?(options: ToolchainOptions): UploadResult;
|
|
34
44
|
monitor?(options: ToolchainOptions): void;
|
|
45
|
+
/**
|
|
46
|
+
* Optional: launch an interactive debugger session for the last build
|
|
47
|
+
* (e.g. `west debug`). Not invoked by the standard build/compile flow;
|
|
48
|
+
* powers an explicit debug-attach entry point. Frameworks that support
|
|
49
|
+
* source-level debugging implement this to spawn their native debugger.
|
|
50
|
+
*/
|
|
51
|
+
debug?(options: ToolchainOptions): void;
|
|
35
52
|
}
|
|
36
53
|
export declare function setLoadedFramework(framework: LoadedFramework): void;
|
|
37
54
|
export declare function getLoadedFramework(): LoadedFramework;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** A framework family that ships as an installable @typecad/framework-* package. */
|
|
2
|
+
export interface FrameworkCatalogEntry {
|
|
3
|
+
/** Short id, e.g. "arduino". Matches the suffix of @typecad/framework-<id>. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Full npm package name, e.g. "@typecad/framework-arduino". */
|
|
6
|
+
packageName: string;
|
|
7
|
+
/** Human-readable label shown in the install prompt. */
|
|
8
|
+
label: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a real package exists on the registry. Used to filter the prompt:
|
|
11
|
+
* "esp-idf" is a known family with no published package yet, so it must not
|
|
12
|
+
* be offered for install (it would fail at the package-manager step).
|
|
13
|
+
*/
|
|
14
|
+
installable: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The installable frameworks. Kept aligned with the packages/ directory:
|
|
18
|
+
* framework-arduino, framework-native, framework-zephyr all ship real packages.
|
|
19
|
+
* esp-idf is intentionally absent (no published package in this repo).
|
|
20
|
+
*/
|
|
21
|
+
export declare const FRAMEWORK_CATALOG: readonly FrameworkCatalogEntry[];
|
|
22
|
+
/** Structural shape we need from a board/target. Keeps this module decoupled
|
|
23
|
+
* from the create/ module's KnownTarget (and trivially testable with literals). */
|
|
24
|
+
export interface BoardLike {
|
|
25
|
+
isNative?: boolean;
|
|
26
|
+
architecture?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Look up a catalog entry by framework id (e.g. "arduino"). */
|
|
29
|
+
export declare function frameworkCatalogEntry(id: string): FrameworkCatalogEntry | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The framework ids compatible with a board. Native boards map to ["native"];
|
|
32
|
+
* embedded boards map via ARCHITECTURE_FRAMEWORKS (falling back to arduino for
|
|
33
|
+
* unknown architectures). Order is preserved as the catalog order so the most
|
|
34
|
+
* common framework is offered first in the prompt.
|
|
35
|
+
*/
|
|
36
|
+
export declare function frameworksForTarget(target: BoardLike): FrameworkCatalogEntry[];
|
|
37
|
+
export type PackageManager = "npm" | "pnpm" | "yarn";
|
|
38
|
+
/**
|
|
39
|
+
* Detect the package manager for a directory. Priority:
|
|
40
|
+
* 1. package.json#packageManager field (the strongest signal, Corepack-style)
|
|
41
|
+
* 2. lockfile presence (pnpm-lock.yaml / yarn.lock → otherwise npm)
|
|
42
|
+
* 3. default "npm"
|
|
43
|
+
* Never throws — unreadable/missing files fall through to the next signal.
|
|
44
|
+
*/
|
|
45
|
+
export declare function detectPackageManager(cwd: string): PackageManager;
|
|
46
|
+
/**
|
|
47
|
+
* Build the package-manager invocation that installs `packageName` into the
|
|
48
|
+
* current project (project-local, never global — per the install command spec).
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildInstallCommand(pm: PackageManager, packageName: string): {
|
|
51
|
+
bin: string;
|
|
52
|
+
args: string[];
|
|
53
|
+
};
|