@nodegx/mcp 0.2.3 → 0.2.4
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/dist/backend/cli.js +39 -12
- package/dist/kit-extract.cjs +41 -14
- package/dist/noodl-mcp.cjs +1320 -218
- package/dist/package.json +1 -1
- package/dist/render-harness/drive-steps.js +90 -0
- package/dist/render-harness/measure-from-disk.js +90 -0
- package/dist/render-harness/render-from-disk.js +20 -1
- package/dist/src/external/viewer/noodl.viewer.js +1 -1
- package/package.json +4 -6
package/dist/noodl-mcp.cjs
CHANGED
|
@@ -104563,6 +104563,52 @@ var require_src3 = __commonJS({
|
|
|
104563
104563
|
.filter((x) => x.box > 0 && x.width > x.box + 1)
|
|
104564
104564
|
.map((x) => ({ text: x.el.textContent.trim().slice(0, 80), width: round(x.width), parentWidth: round(x.box), cls: cls(x.el) }));
|
|
104565
104565
|
|
|
104566
|
+
// P112-OWN020-OFFSYSTEM \u2014 the controls, measured against the project's type scale. A control nobody styled draws
|
|
104567
|
+
// the runtime's own look (a Radio Button is a 32px circle with a 2px black ring; a native select or button keeps
|
|
104568
|
+
// the browser's 13.33px text), and that is the "chunky" control beside a page on tokens. A radio or checkbox input
|
|
104569
|
+
// is opacity 0, so its box is read on the first ancestor a person can see. The scale is the project's --text-*
|
|
104570
|
+
// tokens, read off :root; the fake DOMs in the tests have no getPropertyValue, so it is guarded.
|
|
104571
|
+
const pxOf = (value) => {
|
|
104572
|
+
const s = String(value || '').trim();
|
|
104573
|
+
if (!s) return NaN;
|
|
104574
|
+
if (s.slice(-3) === 'rem') return parseFloat(s) * (parseFloat(styleOf(document.documentElement).fontSize) || 16);
|
|
104575
|
+
return parseFloat(s);
|
|
104576
|
+
};
|
|
104577
|
+
const textScale = [];
|
|
104578
|
+
const rootStyle = styleOf(document.documentElement);
|
|
104579
|
+
if (rootStyle && typeof rootStyle.getPropertyValue === 'function') {
|
|
104580
|
+
for (const step of ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl']) {
|
|
104581
|
+
const px = pxOf(rootStyle.getPropertyValue('--text-' + step));
|
|
104582
|
+
if (px > 0) textScale.push(Math.round(px * 100) / 100);
|
|
104583
|
+
}
|
|
104584
|
+
}
|
|
104585
|
+
const controlKind = (el) => {
|
|
104586
|
+
const type = String(el.type || '').toLowerCase();
|
|
104587
|
+
if (el.tagName === 'INPUT' && type === 'radio') return 'Radio Button';
|
|
104588
|
+
if (el.tagName === 'INPUT' && type === 'checkbox') return 'Checkbox';
|
|
104589
|
+
if (el.tagName === 'SELECT') return 'Dropdown';
|
|
104590
|
+
if (el.tagName === 'TEXTAREA' || (el.tagName === 'INPUT' && ['', 'text', 'email', 'password', 'search', 'number', 'tel', 'url'].indexOf(type) !== -1)) return 'Text Input';
|
|
104591
|
+
if (el.tagName === 'BUTTON') return 'Button';
|
|
104592
|
+
return null;
|
|
104593
|
+
};
|
|
104594
|
+
const controls = [];
|
|
104595
|
+
for (const el of all) {
|
|
104596
|
+
const kind = controlKind(el);
|
|
104597
|
+
if (!kind) continue;
|
|
104598
|
+
if (kind === 'Radio Button' || kind === 'Checkbox') {
|
|
104599
|
+
let shown = el;
|
|
104600
|
+
for (let hops = 0; shown && hops < 3 && (parseFloat(styleOf(shown).opacity) < 0.05 || shown.getBoundingClientRect().width < 2); hops++) shown = shown.parentElement;
|
|
104601
|
+
if (!shown || shown.offsetParent === null) continue;
|
|
104602
|
+
const r = shown.getBoundingClientRect();
|
|
104603
|
+
let label = null;
|
|
104604
|
+
if (el.id && typeof document.querySelector === 'function') label = document.querySelector('label[for="' + el.id + '"]');
|
|
104605
|
+
controls.push({ kind, box: round(Math.max(r.width, r.height)), textSize: label ? pxOf(styleOf(label).fontSize) : NaN });
|
|
104606
|
+
} else {
|
|
104607
|
+
if (visible.indexOf(el) === -1) continue;
|
|
104608
|
+
controls.push({ kind, box: 0, textSize: pxOf(styleOf(el).fontSize) });
|
|
104609
|
+
}
|
|
104610
|
+
}
|
|
104611
|
+
|
|
104566
104612
|
// AWP-004 \u2014 what is on screen, as opposed to what is in the DOM. The page is
|
|
104567
104613
|
// never scrolled when this runs, so a rect is already a document position.
|
|
104568
104614
|
// Kimi K3's storefront was 83 texts in the DOM and about three on screen, and
|
|
@@ -105021,6 +105067,7 @@ var require_src3 = __commonJS({
|
|
|
105021
105067
|
overflowingCount: overflowing.length,
|
|
105022
105068
|
textsWiderThanBox: textsWiderThanBox.slice(0, 10),
|
|
105023
105069
|
textsWiderThanBoxCount: textsWiderThanBox.length,
|
|
105070
|
+
controls: { textScale, items: controls.slice(0, 60), count: controls.length },
|
|
105024
105071
|
text: {
|
|
105025
105072
|
elements: textEls.length,
|
|
105026
105073
|
onScreen: textEls.filter(onScreen).length,
|
|
@@ -105110,6 +105157,13 @@ var require_src3 = __commonJS({
|
|
|
105110
105157
|
* page-level finding stayed quiet because the page itself kept its width.
|
|
105111
105158
|
*/
|
|
105112
105159
|
TextWiderThanItsBox: "text-wider-than-its-box",
|
|
105160
|
+
/**
|
|
105161
|
+
* P112-OWN020-OFFSYSTEM — a control drawn at the runtime's own size beside a page on its token scale: a radio or
|
|
105162
|
+
* checkbox box much bigger than its label, or control text off the project's --text-* scale (a native select or
|
|
105163
|
+
* button keeps the browser's 13.33px). Mistral (SP1) and DeepSeek (OpenTune) both shipped one — the "chunky"
|
|
105164
|
+
* Sort-by circles — because a control placed in passing is the one an agent never styles.
|
|
105165
|
+
*/
|
|
105166
|
+
ControlOffScale: "control-off-scale",
|
|
105113
105167
|
SingleColumnGrid: "single-column-grid",
|
|
105114
105168
|
MinimumLayoutWidth: "minimum-layout-width",
|
|
105115
105169
|
HorizontalOverflow: "horizontal-overflow",
|
|
@@ -105178,9 +105232,30 @@ var require_src3 = __commonJS({
|
|
|
105178
105232
|
const named = widest.cls ? `${widest.tag.toLowerCase()}.${widest.cls.split(/\s+/)[0]}` : widest.tag.toLowerCase();
|
|
105179
105233
|
return `${named} at ${widest.width}px`;
|
|
105180
105234
|
}
|
|
105235
|
+
function controlsOffScale(controls) {
|
|
105236
|
+
const scale = controls.textScale || [];
|
|
105237
|
+
const onScale = (px3) => scale.length === 0 || !(px3 > 0) || scale.some((s) => Math.abs(s - px3) < 0.6);
|
|
105238
|
+
const byKind = {};
|
|
105239
|
+
for (const c of controls.items || []) {
|
|
105240
|
+
const oversized = (c.kind === "Radio Button" || c.kind === "Checkbox") && c.box > Math.max(20, 1.5 * (c.textSize > 0 ? c.textSize : 0));
|
|
105241
|
+
const offText = !onScale(c.textSize);
|
|
105242
|
+
if (!oversized && !offText) continue;
|
|
105243
|
+
const k = byKind[c.kind] || (byKind[c.kind] = { kind: c.kind, count: 0, box: c.box, textSize: c.textSize, oversized, offText });
|
|
105244
|
+
k.count += 1;
|
|
105245
|
+
}
|
|
105246
|
+
const fmt = (n) => String(Math.round(n * 100) / 100);
|
|
105247
|
+
return Object.keys(byKind).map((kind) => {
|
|
105248
|
+
const k = byKind[kind];
|
|
105249
|
+
const parts = [];
|
|
105250
|
+
if (k.oversized) parts.push(fmt(k.box) + "px box" + (k.textSize > 0 ? " beside a " + fmt(k.textSize) + "px label" : ""));
|
|
105251
|
+
if (k.offText) parts.push(fmt(k.textSize) + "px text, off the type scale (" + scale.map(fmt).join("/") + "px)");
|
|
105252
|
+
return { kind, count: k.count, box: k.box, textSize: k.textSize, sentence: kind + " x" + k.count + ": " + parts.join(", ") };
|
|
105253
|
+
});
|
|
105254
|
+
}
|
|
105181
105255
|
function summarise(viewports, diagnosis, overridden = {}) {
|
|
105182
105256
|
const findings = [];
|
|
105183
105257
|
const add = (f) => findings.push(f);
|
|
105258
|
+
let controlsJudged = false;
|
|
105184
105259
|
for (const [name, v] of Object.entries(viewports)) {
|
|
105185
105260
|
if (!v || v.error) continue;
|
|
105186
105261
|
const isDesktop = v.requested.width >= DESKTOP_WIDTH;
|
|
@@ -105329,6 +105404,19 @@ var require_src3 = __commonJS({
|
|
|
105329
105404
|
evidence: v.textsWiderThanBox
|
|
105330
105405
|
});
|
|
105331
105406
|
}
|
|
105407
|
+
if (!controlsJudged && v.controls && v.controls.count > 0) {
|
|
105408
|
+
controlsJudged = true;
|
|
105409
|
+
const off = controlsOffScale(v.controls);
|
|
105410
|
+
if (off.length > 0) {
|
|
105411
|
+
add({
|
|
105412
|
+
code: RenderFinding.ControlOffScale,
|
|
105413
|
+
severity: "warning",
|
|
105414
|
+
viewport: name,
|
|
105415
|
+
message: "Controls drawn at a size the rest of the page is not: " + off.map((o) => o.sentence).join("; ") + `. A control nobody styled keeps the runtime's own look. Give the type one look for the whole app with set_element_style (find_tools group "theme"), or set_style_preset to add on-system styles for every control; a node that sets the parameter itself keeps its own value.`,
|
|
105416
|
+
evidence: off.map((o) => ({ kind: o.kind, count: o.count, box: o.box, textSize: o.textSize, textScale: v.controls.textScale }))
|
|
105417
|
+
});
|
|
105418
|
+
}
|
|
105419
|
+
}
|
|
105332
105420
|
if (v.text.elements >= 10) {
|
|
105333
105421
|
const weights = Object.keys(v.text.fontWeights);
|
|
105334
105422
|
if (weights.length <= 1 || v.text.distinctFontSizes <= 2) {
|
|
@@ -105499,6 +105587,8 @@ var require_src3 = __commonJS({
|
|
|
105499
105587
|
plural: plural3,
|
|
105500
105588
|
widestOffender,
|
|
105501
105589
|
describeSites,
|
|
105590
|
+
// P112-OWN020-OFFSYSTEM — exported so its spec grades the judgement on readings, not only through a fake DOM.
|
|
105591
|
+
controlsOffScale,
|
|
105502
105592
|
TEXT_BEARING_PORTS,
|
|
105503
105593
|
placeholderStringsFromCatalog
|
|
105504
105594
|
};
|
|
@@ -105510,7 +105600,7 @@ var require_package = __commonJS({
|
|
|
105510
105600
|
"package.json"(exports2, module2) {
|
|
105511
105601
|
module2.exports = {
|
|
105512
105602
|
name: "@nodegx/mcp",
|
|
105513
|
-
version: "0.2.
|
|
105603
|
+
version: "0.2.4",
|
|
105514
105604
|
description: "Model Context Protocol server exposing NodeGX (OpenNoodl) v2 projects to external AI agents.",
|
|
105515
105605
|
license: "GPL-3.0-only",
|
|
105516
105606
|
homepage: "https://nodegx.io",
|
|
@@ -105546,17 +105636,15 @@ var require_package = __commonJS({
|
|
|
105546
105636
|
test: "jest",
|
|
105547
105637
|
typecheck: "tsc --noEmit"
|
|
105548
105638
|
},
|
|
105549
|
-
dependencies: {
|
|
105550
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
105551
|
-
zod: "^3.25.76"
|
|
105552
|
-
},
|
|
105553
105639
|
devDependencies: {
|
|
105640
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
105554
105641
|
"@nodegx/kit-catalog": "*",
|
|
105555
105642
|
"@nodegx/kit-scaffold": "*",
|
|
105556
105643
|
"@nodegx/module-inject": "*",
|
|
105557
105644
|
"@types/jest": "^29.0.1",
|
|
105558
105645
|
jest: "^29.7.0",
|
|
105559
|
-
"ts-jest": "^29.4.1"
|
|
105646
|
+
"ts-jest": "^29.4.1",
|
|
105647
|
+
zod: "^3.25.76"
|
|
105560
105648
|
},
|
|
105561
105649
|
publishConfig: {
|
|
105562
105650
|
access: "public"
|
|
@@ -121151,7 +121239,7 @@ var TOOL_GROUPS = Object.freeze([
|
|
|
121151
121239
|
// looks — so the one instruction the design doctrine gives most often led to
|
|
121152
121240
|
// an empty answer.
|
|
121153
121241
|
keywords: ["theme", "colour", "color", "palette", "styling", "style guide", "branding", "appearance", "dark mode"],
|
|
121154
|
-
tools: ["set_project_tokens", "set_style_preset"],
|
|
121242
|
+
tools: ["set_project_tokens", "set_style_preset", "set_element_style"],
|
|
121155
121243
|
resident: false
|
|
121156
121244
|
}
|
|
121157
121245
|
]);
|
|
@@ -121237,6 +121325,8 @@ var WRITE_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
121237
121325
|
"set_project_settings",
|
|
121238
121326
|
"set_project_tokens",
|
|
121239
121327
|
"set_style_preset",
|
|
121328
|
+
// P112-OWN020-OFFSYSTEM — writes nodegx.styles.json.
|
|
121329
|
+
"set_element_style",
|
|
121240
121330
|
// P109 ISL-005.
|
|
121241
121331
|
"write_script_file",
|
|
121242
121332
|
"edit_script_file",
|
|
@@ -225717,13 +225807,13 @@ var styles_schema_default = {
|
|
|
225717
225807
|
},
|
|
225718
225808
|
variant: {
|
|
225719
225809
|
type: "object",
|
|
225720
|
-
required: ["
|
|
225810
|
+
required: ["typename"],
|
|
225721
225811
|
additionalProperties: true,
|
|
225722
225812
|
properties: {
|
|
225723
225813
|
name: {
|
|
225724
225814
|
type: "string",
|
|
225725
225815
|
minLength: 1,
|
|
225726
|
-
description: "Variant name"
|
|
225816
|
+
description: "Variant name. Absent: the element style, which dresses every node of this type that names no variant (the node's own parameters still win)"
|
|
225727
225817
|
},
|
|
225728
225818
|
typename: {
|
|
225729
225819
|
type: "string",
|
|
@@ -226129,6 +226219,14 @@ var V2ProjectStore = class {
|
|
|
226129
226219
|
readStyles() {
|
|
226130
226220
|
return this.readJson(STYLES_FILE);
|
|
226131
226221
|
}
|
|
226222
|
+
/**
|
|
226223
|
+
* P112-OWN020-OFFSYSTEM — the styles file had a reader and no writer, so the one layer that dresses every node of
|
|
226224
|
+
* a type (a variant with no `name`) could not be written by anything that writes through the format. The whole
|
|
226225
|
+
* file is replaced: a caller reads it, changes what it owns, and writes it back.
|
|
226226
|
+
*/
|
|
226227
|
+
writeStyles(styles) {
|
|
226228
|
+
this.writeJson(STYLES_FILE, styles);
|
|
226229
|
+
}
|
|
226132
226230
|
readRegistry() {
|
|
226133
226231
|
const registry2 = this.readJson(REGISTRY_FILE);
|
|
226134
226232
|
if (!registry2) throw new FormatRefusal("io-error", "missing-registry", "Missing components/_registry.json.");
|
|
@@ -231175,6 +231273,13 @@ Two things are settled BEFORE the first component exists, and in this order:
|
|
|
231175
231273
|
choice into the project rather than carrying it in your head \u2014 \`set_style_preset\` adopts a whole
|
|
231176
231274
|
coherent identity, \`set_project_tokens\` overrides individual values. Both sit in the deferred
|
|
231177
231275
|
\`theme\` group: call \`find_tools({group:"theme"})\` and they arrive in your next tool list.
|
|
231276
|
+
**Controls are styled once, for the whole app.** An ELEMENT STYLE is the look every node of a type
|
|
231277
|
+
wears unless it sets the parameter itself (\`get_style_vocabulary\` \u2192 \`elementStyles\`). A control
|
|
231278
|
+
type with none draws the runtime's own look, which matches no page. So before you place the first
|
|
231279
|
+
Radio Button, Checkbox, Text Input, Dropdown, Slider or Button, call
|
|
231280
|
+
\`set_element_style({ type, starter: true })\` for that type (also in \`theme\`), and change it there
|
|
231281
|
+
to change every one. Style a single node only for what is particular to it: a control styled by
|
|
231282
|
+
hand, one node at a time, is the one that drifts.
|
|
231178
231283
|
2. **The screens.** Name every page the app has and say, in one sentence each, what a person does on
|
|
231179
231284
|
it. That list is what the component tree is a decomposition OF.
|
|
231180
231285
|
|
|
@@ -243452,7 +243557,10 @@ var RUN_TASKS_CONTRACT = {
|
|
|
243452
243557
|
failure: { parameter: "taskFailureOutput", default: "Failure" },
|
|
243453
243558
|
error: { parameter: "taskErrorOutput", default: "Error" }
|
|
243454
243559
|
};
|
|
243560
|
+
var SET_VARIABLE_CHAIN_OUTPUTS = ["done", "completed"];
|
|
243455
243561
|
var OWN_CHAIN_OUTPUTS = {
|
|
243562
|
+
// P18-S127-SETDONE (EXP-011 §95).
|
|
243563
|
+
"Set Variable": SET_VARIABLE_CHAIN_OUTPUTS,
|
|
243456
243564
|
[RUN_TASKS_TYPE]: RUN_TASKS_OUTPUTS,
|
|
243457
243565
|
// EXP-011 §54. The boundary's Error is its listener, compiled by its own registration.
|
|
243458
243566
|
[ON_APP_ERROR_TYPE]: ["error"],
|
|
@@ -243751,6 +243859,7 @@ var SESSION_READS = {
|
|
|
243751
243859
|
username: { field: "username", maybeUndefined: true },
|
|
243752
243860
|
email: { field: "email", maybeUndefined: true }
|
|
243753
243861
|
};
|
|
243862
|
+
var INPUT_PULSE_TS_TYPE = "{ sent: number; taken: { current: number } }";
|
|
243754
243863
|
function isDefer(value) {
|
|
243755
243864
|
return typeof value === "object" && value !== null && "defer" in value;
|
|
243756
243865
|
}
|
|
@@ -243840,9 +243949,11 @@ function planProject(ir, catalog2) {
|
|
|
243840
243949
|
const child = byLegacyNow(pending.targetLegacy);
|
|
243841
243950
|
const lifted6 = child?.liftedOutputProps.find((l) => l.port === pending.port);
|
|
243842
243951
|
if (!lifted6) {
|
|
243843
|
-
|
|
243844
|
-
|
|
243845
|
-
)
|
|
243952
|
+
const reason2 = `instance output "${pending.port}" is not lifted by ${pending.targetLegacy} \u2014 its feed defers there`;
|
|
243953
|
+
plan.notes.push(`wire ${pending.connectionKey} dropped: ${reason2}`);
|
|
243954
|
+
if (pending.intoInstance === true) {
|
|
243955
|
+
plan.droppedWires.push({ key: pending.connectionKey, fromId: pending.instanceId, fromProperty: pending.port, toId: pending.toNodeId, toProperty: pending.toProperty, reason: reason2 });
|
|
243956
|
+
}
|
|
243846
243957
|
continue;
|
|
243847
243958
|
}
|
|
243848
243959
|
const varKey = `${pending.instanceId}:${pending.port}`;
|
|
@@ -243872,6 +243983,23 @@ function planProject(ir, catalog2) {
|
|
|
243872
243983
|
kind: "computed",
|
|
243873
243984
|
expr: { kind: "state-get", name, maybeUndefined: true }
|
|
243874
243985
|
};
|
|
243986
|
+
for (const pulse of plan.instancePulses) {
|
|
243987
|
+
if (pulse.nodeId !== pending.toNodeId || pulse.delivered !== void 0) continue;
|
|
243988
|
+
const taken = takenNamesOf(plan);
|
|
243989
|
+
const base = `${pulse.stateName.replace(/Pulses$/, "")}Delivered`;
|
|
243990
|
+
let delivered = base;
|
|
243991
|
+
for (let n = 2; taken.has(delivered) || taken.has(setterNameOf(delivered)); n++) delivered = `${base}${n}`;
|
|
243992
|
+
plan.stateVars.push({
|
|
243993
|
+
name: delivered,
|
|
243994
|
+
setterName: setterNameOf(delivered),
|
|
243995
|
+
tsType: "number",
|
|
243996
|
+
boot: 0,
|
|
243997
|
+
originNodeId: pulse.nodeId,
|
|
243998
|
+
origin: "counter",
|
|
243999
|
+
comment: `"${pulse.port}" as the placed component is handed it: the count, one commit after it is sent, so the values lifted with it have landed (P18-S127-SIBLINGVALUE).`
|
|
244000
|
+
});
|
|
244001
|
+
pulse.delivered = { name: delivered, setterName: setterNameOf(delivered) };
|
|
244002
|
+
}
|
|
243875
244003
|
}
|
|
243876
244004
|
plan.pendingLifted = [];
|
|
243877
244005
|
};
|
|
@@ -243943,6 +244071,7 @@ function planProject(ir, catalog2) {
|
|
|
243943
244071
|
kitDuplicates
|
|
243944
244072
|
};
|
|
243945
244073
|
}
|
|
244074
|
+
var UNCARRIED_PAGE_GROUP = "the page's own Group,";
|
|
243946
244075
|
function undrawnReasonOf(plan, component, id) {
|
|
243947
244076
|
const nodeById = new Map(component.nodes.map((n) => [n.id, n]));
|
|
243948
244077
|
const labelOf = (nodeId) => {
|
|
@@ -243970,6 +244099,14 @@ function undrawnReasonOf(plan, component, id) {
|
|
|
243970
244099
|
}
|
|
243971
244100
|
return null;
|
|
243972
244101
|
}
|
|
244102
|
+
function rowlessReasonOf(plan, repeaterId) {
|
|
244103
|
+
const repeater = plan.repeaters[repeaterId];
|
|
244104
|
+
if (repeater === void 0 || repeater.templatePath === null) return "it draws no rows in the export (it has no template component)";
|
|
244105
|
+
const fed = plan.queries.some((q) => q.nodeId === repeater.itemsQueryId) || repeater.itemsCollectionName != null || repeater.itemsExpr !== void 0 || plan.staticData.some((sd) => sd.nodeId === repeater.itemsStaticId);
|
|
244106
|
+
if (!fed) return "it draws no rows in the export (its items are not fed by a query, a named array, or a statically-known list)";
|
|
244107
|
+
if (repeater.mapping === null) return "it draws no rows in the export (its rows are mapped by a script, which is not a static map)";
|
|
244108
|
+
return null;
|
|
244109
|
+
}
|
|
243973
244110
|
function unreachedTriggersOf(plan, component) {
|
|
243974
244111
|
const found2 = { owners: /* @__PURE__ */ new Map(), mounts: /* @__PURE__ */ new Map(), rows: /* @__PURE__ */ new Map() };
|
|
243975
244112
|
if (plan.file === null) return found2;
|
|
@@ -243982,25 +244119,22 @@ function unreachedTriggersOf(plan, component) {
|
|
|
243982
244119
|
const owners = /* @__PURE__ */ new Set([...Object.keys(plan.handlers), ...Object.keys(plan.changeHandlers), ...plan.drags.map((d) => d.nodeId)]);
|
|
243983
244120
|
for (const owner of owners) {
|
|
243984
244121
|
if (plan.roleOf[owner] === void 0) continue;
|
|
243985
|
-
const why = undrawnReasonOf(plan, component, owner);
|
|
244122
|
+
const why = undrawnReasonOf(plan, component, owner) ?? (nodeById.get(owner)?.type === "For Each" ? rowlessReasonOf(plan, owner) : null);
|
|
243986
244123
|
if (why === null) continue;
|
|
243987
244124
|
whyUndrawn.set(owner, why);
|
|
243988
244125
|
found2.owners.set(owner, `its trigger, ${labelOf(owner)}, is not drawn: ${why}`);
|
|
243989
244126
|
}
|
|
243990
244127
|
const whyUnmounted = /* @__PURE__ */ new Map();
|
|
243991
244128
|
for (const [owner, byPort] of Object.entries(plan.handlers)) {
|
|
243992
|
-
if (byPort["didMount"] === void 0 || whyUndrawn.has(owner)) continue;
|
|
243993
|
-
|
|
243994
|
-
|
|
243995
|
-
|
|
243996
|
-
|
|
243997
|
-
|
|
243998
|
-
|
|
243999
|
-
|
|
244000
|
-
|
|
244001
|
-
}
|
|
244002
|
-
if (at === plan.rootId) break;
|
|
244003
|
-
}
|
|
244129
|
+
if (byPort["didMount"] === void 0 || whyUndrawn.has(owner) || plan.collapsedGroupId === void 0) continue;
|
|
244130
|
+
const bound = plan.bindings[plan.collapsedGroupId]?.["mounted"];
|
|
244131
|
+
if (bound === void 0 || bound.kind === "computed" && bound.expr.kind === "literal") continue;
|
|
244132
|
+
let inside = false;
|
|
244133
|
+
for (let at = owner; at !== void 0; at = nodeById.get(at)?.parent) if (at === plan.collapsedGroupId) inside = true;
|
|
244134
|
+
if (!inside) continue;
|
|
244135
|
+
const why = `${UNCARRIED_PAGE_GROUP} ${labelOf(plan.collapsedGroupId)} is mounted by a value the export does not carry (it draws the page), so when it mounts is not known here`;
|
|
244136
|
+
whyUnmounted.set(owner, why);
|
|
244137
|
+
found2.mounts.set(owner, `its trigger, the Did Mount of ${labelOf(owner)}, is not run: ${why}`);
|
|
244004
244138
|
}
|
|
244005
244139
|
const writers = /* @__PURE__ */ new Map();
|
|
244006
244140
|
const walk2 = (value, owner) => {
|
|
@@ -244081,6 +244215,8 @@ function planComponent(component, ir, catalog2, registry2, pageByLegacy, pageFil
|
|
|
244081
244215
|
handlers: {},
|
|
244082
244216
|
changeHandlers: {},
|
|
244083
244217
|
receivers: [],
|
|
244218
|
+
inputPulses: [],
|
|
244219
|
+
instancePulses: [],
|
|
244084
244220
|
popups: [],
|
|
244085
244221
|
closesPopup: false,
|
|
244086
244222
|
queries: [],
|
|
@@ -244098,6 +244234,7 @@ function planComponent(component, ir, catalog2, registry2, pageByLegacy, pageFil
|
|
|
244098
244234
|
stateVars: [],
|
|
244099
244235
|
syncEffects: [],
|
|
244100
244236
|
pushEffects: [],
|
|
244237
|
+
rowRelays: [],
|
|
244101
244238
|
branchEffects: [],
|
|
244102
244239
|
recordEffects: [],
|
|
244103
244240
|
variableSeeds: [],
|
|
@@ -244303,6 +244440,7 @@ function planComponent(component, ir, catalog2, registry2, pageByLegacy, pageFil
|
|
|
244303
244440
|
if (roots.length > 1) {
|
|
244304
244441
|
notes.push(`component has ${roots.length} visual roots; only the first renders in step 4`);
|
|
244305
244442
|
}
|
|
244443
|
+
const routerMounted = (toNode, toProperty) => toProperty === "mounted" && toNode.id === plan.rootId && toNode.type === "Page";
|
|
244306
244444
|
const root = roots[0];
|
|
244307
244445
|
if (root !== void 0) {
|
|
244308
244446
|
plan.rootId = root.id;
|
|
@@ -245279,7 +245417,17 @@ function planComponent(component, ir, catalog2, registry2, pageByLegacy, pageFil
|
|
|
245279
245417
|
}
|
|
245280
245418
|
};
|
|
245281
245419
|
const usedStateVarNames = /* @__PURE__ */ new Set();
|
|
245282
|
-
|
|
245420
|
+
let componentStoreNames;
|
|
245421
|
+
const storeNameTaken = (name) => {
|
|
245422
|
+
componentStoreNames ??= new Set(
|
|
245423
|
+
component.nodes.filter((n) => n.type === "Variable2" || n.type === "Set Variable").map((n) => {
|
|
245424
|
+
const variable = literalParam(n, "name");
|
|
245425
|
+
return typeof variable === "string" ? registry2.variables.get(variable)?.exportName : void 0;
|
|
245426
|
+
}).filter((n) => n !== void 0)
|
|
245427
|
+
);
|
|
245428
|
+
return componentStoreNames.has(name);
|
|
245429
|
+
};
|
|
245430
|
+
const stateNameTaken = (name) => usedStateVarNames.has(name) || storeNameTaken(name) || usedJsFnNames.has(name) || plan.props.some((p) => p.name === name) || plan.outputProps.some((o) => o.prop === name) || outputInterface.valueProps.some((v) => v.prop === name) || name === plan.file?.symbol || /**
|
|
245283
245431
|
* `error` joined this list with EXP-011 Tier 1.2, and it is the only one here that is not
|
|
245284
245432
|
* simply an identifier the file already prints.
|
|
245285
245433
|
*
|
|
@@ -245358,6 +245506,9 @@ function planComponent(component, ir, catalog2, registry2, pageByLegacy, pageFil
|
|
|
245358
245506
|
return stateVar;
|
|
245359
245507
|
};
|
|
245360
245508
|
const httpChainScope = /* @__PURE__ */ new Map();
|
|
245509
|
+
const rowChainScopes = [];
|
|
245510
|
+
const relayTouches = [];
|
|
245511
|
+
const relayTouchedKeys = /* @__PURE__ */ new Map();
|
|
245361
245512
|
const httpNames = /* @__PURE__ */ new Map();
|
|
245362
245513
|
const httpNamesOf = (node) => {
|
|
245363
245514
|
let names2 = httpNames.get(node.id);
|
|
@@ -246312,7 +246463,8 @@ ${body}
|
|
|
246312
246463
|
};
|
|
246313
246464
|
const CONTROL_ACTION_PORTS = {
|
|
246314
246465
|
checkbox: ["check", "uncheck"],
|
|
246315
|
-
|
|
246466
|
+
// P18-S128-FIELDSET. A Set writes the field's text as a Clear does, so it earns the row too.
|
|
246467
|
+
input: ["clear", "set"]
|
|
246316
246468
|
};
|
|
246317
246469
|
const LIST_PRODUCERS = /* @__PURE__ */ new Set(["Collection2", "Filter Collection", "Map Collection", "FilterDBModels"]);
|
|
246318
246470
|
const collectionReadEligible = (node) => {
|
|
@@ -246846,8 +246998,52 @@ ${body}
|
|
|
246846
246998
|
}
|
|
246847
246999
|
return cases;
|
|
246848
247000
|
};
|
|
247001
|
+
const relayedInputOf = (forEach, output) => {
|
|
247002
|
+
const repeater = plan.repeaters[forEach.id];
|
|
247003
|
+
const templatePath = repeater?.templatePath ?? null;
|
|
247004
|
+
const template = templatePath === null ? void 0 : ir.components.find((tc) => `/${tc.path}` === templatePath);
|
|
247005
|
+
if (repeater === void 0 || templatePath === null || template === void 0) return { defer: `the repeater relaying "${output}" has no template component` };
|
|
247006
|
+
if (repeater.mapping === null) return { defer: `the repeater relaying "${output}" maps its rows with a script, which is not a static map` };
|
|
247007
|
+
const tById = new Map(template.nodes.map((n) => [n.id, n]));
|
|
247008
|
+
const feeds = template.connections.filter((c) => c.toProperty === output && tById.get(c.toId)?.type === "Component Outputs");
|
|
247009
|
+
if (feeds.length !== 1) {
|
|
247010
|
+
return {
|
|
247011
|
+
defer: feeds.length === 0 ? `nothing in ${templatePath} feeds the row output "${output}" that the repeater relays` : `${feeds.length} wires feed ${templatePath}'s row output "${output}" \u2014 last-writer-wins is not statically ordered`
|
|
247012
|
+
};
|
|
247013
|
+
}
|
|
247014
|
+
const feeder = tById.get(feeds[0].fromId);
|
|
247015
|
+
if (feeder?.type !== "Component Inputs") {
|
|
247016
|
+
return {
|
|
247017
|
+
defer: `${templatePath}'s row output "${output}" is computed inside the row (by ${feeder?.type ?? "a missing node"}.${feeds[0].fromProperty}) \u2014 only a row input passed straight through is relayed in this slice`
|
|
247018
|
+
};
|
|
247019
|
+
}
|
|
247020
|
+
const input = feeds[0].fromProperty;
|
|
247021
|
+
if (repeater.mapping !== "template-inputs" && !repeater.mapping.some((m) => m.input === input)) {
|
|
247022
|
+
return { defer: `the repeater maps nothing into ${templatePath}'s input "${input}", which its row output "${output}" passes through` };
|
|
247023
|
+
}
|
|
247024
|
+
const declared = componentOutputInterface(template).valueProps.find((v) => v.port === output)?.tsType ?? "unknown";
|
|
247025
|
+
return { input, templatePath, declared };
|
|
247026
|
+
};
|
|
246849
247027
|
const resolveExpr = (fromNode, fromProperty, ctx) => {
|
|
246850
247028
|
if (!fromNode) return null;
|
|
247029
|
+
if (fromNode.type === "For Each" && fromProperty.startsWith(ITEM_OUTPUT_VALUE)) {
|
|
247030
|
+
const output = fromProperty.slice(ITEM_OUTPUT_VALUE.length);
|
|
247031
|
+
relayTouches[relayTouches.length - 1]?.add(fromNode.id);
|
|
247032
|
+
if (jsResolving.size > 0) {
|
|
247033
|
+
ctx.defer = `it reads the repeater ${fromNode.id}'s relayed row value "${output}" as a standing input, read whenever the node runs \u2014 there it is the value of whichever row fired last, which the export does not hold; it reads that value only in a chain a row's relayed signal runs, as the firing row's own`;
|
|
247034
|
+
return null;
|
|
247035
|
+
}
|
|
247036
|
+
if (rowChainScopes[rowChainScopes.length - 1] !== fromNode.id) {
|
|
247037
|
+
ctx.defer = `it reads the repeater ${fromNode.id}'s relayed row value "${output}" in a chain no row's relayed signal of that repeater starts in the export \u2014 there it is the value of whichever row fired last, which the export does not hold (in a row's chain it is the firing row's own)`;
|
|
247038
|
+
return null;
|
|
247039
|
+
}
|
|
247040
|
+
const relayed = relayedInputOf(fromNode, output);
|
|
247041
|
+
if ("defer" in relayed) {
|
|
247042
|
+
ctx.defer = relayed.defer;
|
|
247043
|
+
return null;
|
|
247044
|
+
}
|
|
247045
|
+
return { kind: "row-relay", repeaterId: fromNode.id, output, input: relayed.input, tsType: relayed.declared };
|
|
247046
|
+
}
|
|
246851
247047
|
if (LIST_PRODUCERS.has(fromNode.type) && fromProperty === "items") return listReadOf(fromNode, ctx);
|
|
246852
247048
|
if (LIST_PRODUCERS.has(fromNode.type) && fromProperty === "count") {
|
|
246853
247049
|
const source = listReadOf(fromNode, ctx);
|
|
@@ -247719,6 +247915,9 @@ ${body}
|
|
|
247719
247915
|
return expr.viaState !== void 0 && expr.seeded !== true;
|
|
247720
247916
|
case "state-get":
|
|
247721
247917
|
return expr.maybeUndefined === true;
|
|
247918
|
+
// EXP-011 §100. The row's field: a row whose item lacks it is handed nothing. Must agree with component.ts maybeUndefined.
|
|
247919
|
+
case "row-relay":
|
|
247920
|
+
return true;
|
|
247722
247921
|
// FLD-015. A hoisted Static Data constant is a frozen module-scope literal: it exists from
|
|
247723
247922
|
// module load and nothing writes it, so it is never undefined for a stronger reason than
|
|
247724
247923
|
// the named arrays below — there is no boot value to be before.
|
|
@@ -247862,7 +248061,7 @@ ${body}
|
|
|
247862
248061
|
reason2 = `its ${w.fromProperty} feeds ${sink?.type ?? "a missing node"}.${w.toProperty} \u2014 a snapshot of the last Evaluate lands only in a Mounted or a Visible in this slice`;
|
|
247863
248062
|
} else if (!sink || !rendered2.has(sink.id)) {
|
|
247864
248063
|
reason2 = `its ${w.fromProperty} feeds an element that is not drawn \u2014 a snapshot lands only in a drawn element's Mounted or Visible in this slice`;
|
|
247865
|
-
} else if (w.toProperty
|
|
248064
|
+
} else if (routerMounted(sink, w.toProperty)) {
|
|
247866
248065
|
reason2 = "a mounted wire into the component root is a router concern \u2014 not translated in this slice";
|
|
247867
248066
|
}
|
|
247868
248067
|
if (reason2 !== null) break;
|
|
@@ -248093,6 +248292,9 @@ ${body}
|
|
|
248093
248292
|
return "unknown";
|
|
248094
248293
|
case "state-get":
|
|
248095
248294
|
return plan.stateVars.find((v) => v.name === expr.name)?.tsType.replace(" | undefined", "") ?? "unknown";
|
|
248295
|
+
// EXP-011 §100. The row template's declared output type.
|
|
248296
|
+
case "row-relay":
|
|
248297
|
+
return expr.tsType;
|
|
248096
248298
|
case "control-event":
|
|
248097
248299
|
return expr.form === "checked" ? "boolean" : expr.form === "number" ? "number" : "string";
|
|
248098
248300
|
case "logical":
|
|
@@ -248193,15 +248395,26 @@ ${body}
|
|
|
248193
248395
|
"net.noodl.user.SetUserProperties": "store",
|
|
248194
248396
|
"net.noodl.user.RequestMagicLink": "send"
|
|
248195
248397
|
};
|
|
248196
|
-
const isTriggerWire = (type, toProperty) => TRIGGER_PORTS[type] === toProperty || type === "NavigationShowPopup" && toProperty === "show" || type === "NavigationClosePopup" && (toProperty === "close" || toProperty.startsWith("closeAction-")) || jsNodeKindOf(type) !== null && toProperty === "run" || isLatchType(type) && (LATCH_TRIGGERS[type] ?? []).includes(toProperty) || (type === "net.noodl.controls.checkbox" || type === "Checkbox") && (toProperty === "check" || toProperty === "uncheck") ||
|
|
248398
|
+
const isTriggerWire = (type, toProperty) => TRIGGER_PORTS[type] === toProperty || type === "NavigationShowPopup" && toProperty === "show" || type === "NavigationClosePopup" && (toProperty === "close" || toProperty.startsWith("closeAction-")) || jsNodeKindOf(type) !== null && toProperty === "run" || isLatchType(type) && (LATCH_TRIGGERS[type] ?? []).includes(toProperty) || (type === "net.noodl.controls.checkbox" || type === "Checkbox") && (toProperty === "check" || toProperty === "uncheck") || // P18-S128-FIELDSET. A text field's four actions: Clear, Set, Focus, Blur.
|
|
248399
|
+
isTextInputType(type) && (toProperty === "clear" || toProperty === "set" || toProperty === "focus" || toProperty === "blur") || // EXP-011 §39. A Delay's Start/Restart/Stop — one node, three action ports.
|
|
248197
248400
|
type === TIMER_TYPE && isTimerTrigger(toProperty) || // EXP-011 §49. A States' Toggle and its `to-<state>` family.
|
|
248198
248401
|
type === STATES_TYPE && isStatesTrigger(toProperty) || // EXP-011 §53. A Run Tasks' Do and Abort — one node, two action ports.
|
|
248199
248402
|
type === RUN_TASKS_TYPE && (toProperty === "run" || toProperty === "abort") || // EXP-011 §58. The trio's action ports — Parse/Clear, Add/Flush/Clear, Add/Clear.
|
|
248200
248403
|
STREAM_NODES[type] !== void 0 && STREAM_NODES[type].actions[toProperty] !== void 0 || // EXP-011 §63. A Drag's two snap Dos — one node, two action ports.
|
|
248201
248404
|
type === DRAG_TYPE && DRAG_SNAP_TRIGGERS[toProperty] !== void 0 || // EXP-011 §61. The Pop's Navigate and its `backAction-<a>` family; the stack's Reset.
|
|
248202
248405
|
type === STACK_POP_TYPE && (toProperty === "navigate" || toProperty.startsWith("backAction-")) || type === PAGE_STACK_TYPE && toProperty === "reset";
|
|
248203
|
-
const isTriggerInto = (node, toProperty) => isTriggerWire(node.type, toProperty) || node.type === SCRIPT_TYPE && scriptPortsOf(node).signalInputs.includes(toProperty) || kitSignalInputOf(node, toProperty) !== void 0;
|
|
248406
|
+
const isTriggerInto = (node, toProperty) => isTriggerWire(node.type, toProperty) || node.type === SCRIPT_TYPE && scriptPortsOf(node).signalInputs.includes(toProperty) || kitSignalInputOf(node, toProperty) !== void 0 || instanceSignalInputOf(node, toProperty);
|
|
248204
248407
|
const kitSignalInputOf = (node, port5) => plan.roleOf[node.id] === "custom" ? plan.customNodes[node.id]?.def.inputs.find((input) => input.name === port5 && input.type === "signal") : void 0;
|
|
248408
|
+
const instanceSignalInputOf = (node, port5) => {
|
|
248409
|
+
if (plan.roleOf[node.id] !== "instance") return false;
|
|
248410
|
+
const target = ir.components.find((c) => `/${c.path}` === node.type);
|
|
248411
|
+
if (target === void 0 || !signalInputPortsOf(target).has(port5)) return false;
|
|
248412
|
+
return target.nodes.some((n) => {
|
|
248413
|
+
if (n.parent !== void 0) return false;
|
|
248414
|
+
const role = renderRole(n, catalog2, kits);
|
|
248415
|
+
return role !== null && role !== "unsupported" && role !== "radio";
|
|
248416
|
+
});
|
|
248417
|
+
};
|
|
248205
248418
|
const popupTargetLegacies = /* @__PURE__ */ new Set();
|
|
248206
248419
|
for (const comp of ir.components) {
|
|
248207
248420
|
for (const n of comp.nodes) {
|
|
@@ -248456,6 +248669,7 @@ ${body}
|
|
|
248456
248669
|
if ("defer" in ticked3) return { defer: ticked3.defer };
|
|
248457
248670
|
for (const c of component.connections) {
|
|
248458
248671
|
if (c.fromId !== node.id || c.fromProperty === "done" || isJsValueOutput(node, c.fromProperty)) continue;
|
|
248672
|
+
if (node.type === JS_FUNCTION && c.fromProperty === "success") continue;
|
|
248459
248673
|
if (node.type === JS_FUNCTION && !c.fromProperty.startsWith("out-")) {
|
|
248460
248674
|
const builtIn = ["success", "failure", "unchanged", "completed", "error"].includes(c.fromProperty);
|
|
248461
248675
|
if (!builtIn) continue;
|
|
@@ -248470,19 +248684,22 @@ ${body}
|
|
|
248470
248684
|
}
|
|
248471
248685
|
const done = doneChainOf(node);
|
|
248472
248686
|
if ("defer" in done) return done;
|
|
248687
|
+
const success = node.type === JS_FUNCTION ? doneChainOf(node, "success") : { then: [], consumes: [], collapses: [], subscribes: [] };
|
|
248688
|
+
if ("defer" in success) return success;
|
|
248473
248689
|
const pulsed = pulseChainsOf(node, record2.def);
|
|
248474
248690
|
if ("defer" in pulsed) pulseRefusals.set(node.id, pulsed.defer);
|
|
248475
248691
|
const pulses = "defer" in pulsed ? { then: [], consumes: [], collapses: [], subscribes: [] } : pulsed;
|
|
248476
|
-
|
|
248692
|
+
const tickedThen = [...pulses.then, ...success.then];
|
|
248693
|
+
if (ticked3.watch.length > 0 && !actionsValidIn([{ kind: "jsfun-run", nodeId: node.id, then: tickedThen }], { kind: "render" })) {
|
|
248477
248694
|
return {
|
|
248478
248695
|
defer: "a ticked input runs it again from no handler, and its signals' chains read values that exist only inside a handler"
|
|
248479
248696
|
};
|
|
248480
248697
|
}
|
|
248481
248698
|
const chain = {
|
|
248482
|
-
then: [...pulses.then, ...done.then],
|
|
248483
|
-
consumes: [...pulses.consumes, ...done.consumes],
|
|
248484
|
-
collapses: [...pulses.collapses, ...done.collapses],
|
|
248485
|
-
subscribes: [...pulses.subscribes, ...done.subscribes]
|
|
248699
|
+
then: [...pulses.then, ...success.then, ...done.then],
|
|
248700
|
+
consumes: [...pulses.consumes, ...success.consumes, ...done.consumes],
|
|
248701
|
+
collapses: [...pulses.collapses, ...success.collapses, ...done.collapses],
|
|
248702
|
+
subscribes: [...pulses.subscribes, ...success.subscribes, ...done.subscribes]
|
|
248486
248703
|
};
|
|
248487
248704
|
const strays = component.connections.filter(
|
|
248488
248705
|
(c) => c.fromId === node.id && isJsValueOutput(node, c.fromProperty) && !chain.consumes.includes(c.key)
|
|
@@ -248551,10 +248768,10 @@ ${body}
|
|
|
248551
248768
|
return { defer: "its Run drives nothing this slice translates \u2014 no done-chain action consumes its work" };
|
|
248552
248769
|
}
|
|
248553
248770
|
if (pulses.then.length > 0 || materialize !== void 0 && record2.def.kind === "function") record2.def.pulsed = true;
|
|
248554
|
-
if (ticked3.watch.length > 0 && (
|
|
248771
|
+
if (ticked3.watch.length > 0 && (tickedThen.length > 0 || materialize !== void 0)) {
|
|
248555
248772
|
tickedRunPieces.set(node.id, {
|
|
248556
248773
|
watch: ticked3.watch,
|
|
248557
|
-
action: { kind: "jsfun-run", nodeId: node.id, then:
|
|
248774
|
+
action: { kind: "jsfun-run", nodeId: node.id, then: tickedThen, ...materialize !== void 0 ? { materialize } : {}, ticked: true }
|
|
248558
248775
|
});
|
|
248559
248776
|
}
|
|
248560
248777
|
return {
|
|
@@ -248596,6 +248813,31 @@ ${body}
|
|
|
248596
248813
|
}
|
|
248597
248814
|
return { action: { kind: "state-set", name: stateVar.name, op: "inc" }, consumes: [] };
|
|
248598
248815
|
};
|
|
248816
|
+
const instancePulseVars = /* @__PURE__ */ new Map();
|
|
248817
|
+
const compileInstanceSignal = (node, port5) => {
|
|
248818
|
+
const key2 = `${node.id}\0${port5}`;
|
|
248819
|
+
let stateVar = instancePulseVars.get(key2);
|
|
248820
|
+
if (stateVar === void 0) {
|
|
248821
|
+
stateVar = allocStateVar(
|
|
248822
|
+
`${node.authoredLabel ?? "instance"} ${port5} pulses`,
|
|
248823
|
+
`${port5}Pulses`,
|
|
248824
|
+
"number",
|
|
248825
|
+
0,
|
|
248826
|
+
node.id,
|
|
248827
|
+
"counter",
|
|
248828
|
+
`How many times "${port5}" on the placed ${node.type} ${node.id} has been signalled \u2014 the count it reads (P18-S126-INSIGNAL).`
|
|
248829
|
+
);
|
|
248830
|
+
instancePulseVars.set(key2, stateVar);
|
|
248831
|
+
const base = `${stateVar.name.replace(/Pulses$/, "")}Taken`;
|
|
248832
|
+
let takenLocal = base;
|
|
248833
|
+
for (let n = 2; stateNameTaken(takenLocal); n++) takenLocal = `${base}${n}`;
|
|
248834
|
+
usedStateVarNames.add(takenLocal);
|
|
248835
|
+
plan.instancePulses.push({ nodeId: node.id, port: port5, stateName: stateVar.name, takenLocal });
|
|
248836
|
+
plan.bindings[node.id] = plan.bindings[node.id] ?? {};
|
|
248837
|
+
plan.bindings[node.id][port5] = { kind: "computed", expr: { kind: "state-get", name: stateVar.name } };
|
|
248838
|
+
}
|
|
248839
|
+
return { action: { kind: "state-set", name: stateVar.name, op: "inc" }, consumes: [] };
|
|
248840
|
+
};
|
|
248599
248841
|
const compileControlAction = (node, port5) => {
|
|
248600
248842
|
const consumedOutcome = component.connections.find(
|
|
248601
248843
|
(c) => c.fromId === node.id && (c.fromProperty === "done" || c.fromProperty === "unchanged")
|
|
@@ -248609,12 +248851,51 @@ ${body}
|
|
|
248609
248851
|
if (stateVar === void 0) {
|
|
248610
248852
|
return { defer: `its ${port5} action writes control state nothing else observes \u2014 no state row is minted` };
|
|
248611
248853
|
}
|
|
248854
|
+
if (port5 === "set") return compileFieldSet(node, stateVar);
|
|
248612
248855
|
const action = port5 === "check" ? { kind: "state-set", name: stateVar.name, expr: { kind: "literal", value: true } } : port5 === "uncheck" ? { kind: "state-set", name: stateVar.name, expr: { kind: "literal", value: false } } : (
|
|
248613
|
-
// clear → the field type's empty value, projected onto the DOM string (FB-026)
|
|
248614
|
-
|
|
248856
|
+
// clear → the field type's empty value, projected onto the DOM string (FB-026); P18-S127-CLEAROUT: then the
|
|
248857
|
+
// field's change chain, with the empty text (the runtime's `clear()` calls `setText('')`).
|
|
248858
|
+
{ kind: "state-set", name: stateVar.name, expr: { kind: "literal", value: "" }, field: { inputId: node.id, port: "clear" } }
|
|
248615
248859
|
);
|
|
248616
248860
|
return { action, consumes: [] };
|
|
248617
248861
|
};
|
|
248862
|
+
const compileFieldSet = (node, stateVar) => {
|
|
248863
|
+
const type = literalParam(node, "type");
|
|
248864
|
+
if (wiredPorts.has(`${node.id}:type`) || type !== void 0 && typeof type !== "string") {
|
|
248865
|
+
return { defer: "its Type is wired, so whether its Set writes text or a number is not known here" };
|
|
248866
|
+
}
|
|
248867
|
+
if (type === "number") {
|
|
248868
|
+
return { defer: "a Number field's Set sends a number or null, and the exported field carries text (P18-S118-NUMBERFIELD)" };
|
|
248869
|
+
}
|
|
248870
|
+
const authored = literalParam(node, "startValue");
|
|
248871
|
+
const start3 = authored === void 0 || authored === null ? "" : typeof authored === "object" ? void 0 : String(authored);
|
|
248872
|
+
if (start3 === void 0) return { defer: "its typed-in Value is not text, a number or a boolean" };
|
|
248873
|
+
const field2 = { inputId: node.id, port: "set", start: start3 };
|
|
248874
|
+
const wires = component.connections.filter((c) => c.toId === node.id && c.toProperty === "startValue");
|
|
248875
|
+
if (wires.length > 1) return { defer: "two wires feed its Value \u2014 which one its Set writes is not statically ordered" };
|
|
248876
|
+
if (wires.length === 0) {
|
|
248877
|
+
return { action: { kind: "state-set", name: stateVar.name, expr: { kind: "literal", value: start3 }, field: field2 }, consumes: [] };
|
|
248878
|
+
}
|
|
248879
|
+
const ctx = newCtx();
|
|
248880
|
+
const expr = resolveExpr(nodeById.get(wires[0].fromId), wires[0].fromProperty, ctx);
|
|
248881
|
+
if (expr === null) return { defer: ctx.defer ?? "its Value wire has no statically known source, and its Set writes that Value" };
|
|
248882
|
+
if (isBooleanExpr(expr)) return { defer: "its Value wire carries a logic truth value, and its Set writes it as text" };
|
|
248883
|
+
return {
|
|
248884
|
+
action: { kind: "state-set", name: stateVar.name, expr, field: field2 },
|
|
248885
|
+
consumes: [...ctx.consumes],
|
|
248886
|
+
collapses: ctx.logicNodeIds,
|
|
248887
|
+
subscribes: ctx.subscriberIds
|
|
248888
|
+
};
|
|
248889
|
+
};
|
|
248890
|
+
const compileFieldFocus = (node, port5) => {
|
|
248891
|
+
const consumedOutcome = component.connections.find(
|
|
248892
|
+
(c) => c.fromId === node.id && (c.fromProperty === "done" || c.fromProperty === "unchanged")
|
|
248893
|
+
);
|
|
248894
|
+
if (consumedOutcome) {
|
|
248895
|
+
return { defer: `its ${consumedOutcome.fromProperty} outcome is consumed \u2014 change-conditional pulses are not translated in this slice` };
|
|
248896
|
+
}
|
|
248897
|
+
return { action: { kind: "field-focus", inputId: node.id, focus: port5 === "focus" }, consumes: [] };
|
|
248898
|
+
};
|
|
248618
248899
|
const compileRecordOp = (node) => {
|
|
248619
248900
|
const verb = RECORD_VERBS[node.type];
|
|
248620
248901
|
const authoredOrWired = (name) => node.parameters.some((p) => p.name === name) || wiredPorts.has(`${node.id}:${name}`);
|
|
@@ -251450,9 +251731,11 @@ ${body}
|
|
|
251450
251731
|
if (jsNodeKindOf(node.type) !== null && port5 === "run") return compileJsRun(node);
|
|
251451
251732
|
if (isLatchType(node.type)) return compileLatch(node, port5);
|
|
251452
251733
|
if (kitSignalInputOf(node, port5) !== void 0) return compileKitSignal(node, port5);
|
|
251734
|
+
if (instanceSignalInputOf(node, port5)) return compileInstanceSignal(node, port5);
|
|
251453
251735
|
if ((plan.roleOf[node.id] === "checkbox" || plan.roleOf[node.id] === "input") && (CONTROL_ACTION_PORTS[plan.roleOf[node.id]] ?? []).includes(port5)) {
|
|
251454
251736
|
return compileControlAction(node, port5);
|
|
251455
251737
|
}
|
|
251738
|
+
if (plan.roleOf[node.id] === "input" && (port5 === "focus" || port5 === "blur")) return compileFieldFocus(node, port5);
|
|
251456
251739
|
if (node.type === EXTERNAL_LINK_TYPE) return compileExternalLink(node);
|
|
251457
251740
|
if (node.type === LOG_TYPE) return compileLog(node);
|
|
251458
251741
|
if (node.type === TIMER_TYPE && isTimerTrigger(port5)) return compileDelay(node, port5);
|
|
@@ -251479,25 +251762,25 @@ ${body}
|
|
|
251479
251762
|
if (propagation !== "global") {
|
|
251480
251763
|
return { defer: `propagation "${String(propagation)}" scopes the event to the component tree` };
|
|
251481
251764
|
}
|
|
251482
|
-
const
|
|
251765
|
+
const ctx = newCtx();
|
|
251483
251766
|
const payload = [];
|
|
251484
251767
|
const consumes = [];
|
|
251485
251768
|
for (const key2 of payloadKeysOf(node)) {
|
|
251486
|
-
const
|
|
251487
|
-
if (!
|
|
251488
|
-
const
|
|
251489
|
-
if (
|
|
251490
|
-
if (isBooleanExpr(
|
|
251769
|
+
const wire = component.connections.find((c) => c.toId === node.id && c.toProperty === key2);
|
|
251770
|
+
if (!wire) continue;
|
|
251771
|
+
const expr = resolveExpr(nodeById.get(wire.fromId), wire.fromProperty, ctx);
|
|
251772
|
+
if (expr === null) return { defer: ctx.defer ?? `payload "${key2}" has no statically known source` };
|
|
251773
|
+
if (isBooleanExpr(expr)) {
|
|
251491
251774
|
return { defer: `payload "${key2}" is fed a logic truth value \u2014 only truthiness sinks take one in this slice` };
|
|
251492
251775
|
}
|
|
251493
|
-
payload.push({ key: key2, expr
|
|
251494
|
-
consumes.push(
|
|
251776
|
+
payload.push({ key: key2, expr });
|
|
251777
|
+
consumes.push(wire.key);
|
|
251495
251778
|
}
|
|
251496
251779
|
return {
|
|
251497
251780
|
action: { kind: "emit", channelName, payload },
|
|
251498
|
-
consumes: [...consumes, ...
|
|
251499
|
-
collapses:
|
|
251500
|
-
subscribes:
|
|
251781
|
+
consumes: [...consumes, ...ctx.consumes],
|
|
251782
|
+
collapses: ctx.logicNodeIds,
|
|
251783
|
+
subscribes: ctx.subscriberIds
|
|
251501
251784
|
};
|
|
251502
251785
|
}
|
|
251503
251786
|
if (node.type === GLOBAL_STORE_SET) {
|
|
@@ -251518,26 +251801,26 @@ ${body}
|
|
|
251518
251801
|
if (keyType === "number" || keyType === "boolean") {
|
|
251519
251802
|
return { defer: `key "${key2}" is ${keyType}-typed by the initial state; only string writes translate in this slice` };
|
|
251520
251803
|
}
|
|
251521
|
-
const
|
|
251522
|
-
if (
|
|
251804
|
+
const wire = lastValueWireOf(node);
|
|
251805
|
+
if (wire === void 0 && valueWiresInto(node).length > 1) {
|
|
251523
251806
|
return { defer: "two wires feed its Value input \u2014 last-writer-wins is not statically ordered" };
|
|
251524
251807
|
}
|
|
251525
|
-
if (!
|
|
251808
|
+
if (!wire) {
|
|
251526
251809
|
const authored = literalParam(node, "value");
|
|
251527
251810
|
if (authored === void 0) return { defer: "nothing is wired into value" };
|
|
251528
251811
|
return { action: { kind: "globalstore-set", storeName: store2.name, key: key2, expr: { kind: "literal", value: authored } }, consumes: [], collapses: [], subscribes: [] };
|
|
251529
251812
|
}
|
|
251530
|
-
const
|
|
251531
|
-
const
|
|
251532
|
-
if (
|
|
251533
|
-
if (isBooleanExpr(
|
|
251813
|
+
const ctx = newCtx();
|
|
251814
|
+
const expr = resolveExpr(nodeById.get(wire.fromId), wire.fromProperty, ctx);
|
|
251815
|
+
if (expr === null) return { defer: ctx.defer ?? "the value wire has no statically known source" };
|
|
251816
|
+
if (isBooleanExpr(expr)) {
|
|
251534
251817
|
return { defer: "the value wire carries a logic truth value \u2014 only truthiness sinks take one in this slice" };
|
|
251535
251818
|
}
|
|
251536
251819
|
return {
|
|
251537
|
-
action: { kind: "globalstore-set", storeName: store2.name, key: key2, expr
|
|
251538
|
-
consumes: [
|
|
251539
|
-
collapses:
|
|
251540
|
-
subscribes:
|
|
251820
|
+
action: { kind: "globalstore-set", storeName: store2.name, key: key2, expr, ...typedInUnderWire(node, "value", wire, expr) },
|
|
251821
|
+
consumes: [wire.key, ...ctx.consumes],
|
|
251822
|
+
collapses: ctx.logicNodeIds,
|
|
251823
|
+
subscribes: ctx.subscriberIds
|
|
251541
251824
|
};
|
|
251542
251825
|
}
|
|
251543
251826
|
if (node.type === "NewModel") {
|
|
@@ -251545,16 +251828,16 @@ ${body}
|
|
|
251545
251828
|
if (result === void 0) return { defer: "the created object is never inserted into a translated array" };
|
|
251546
251829
|
if ("defer" in result) return { defer: result.defer };
|
|
251547
251830
|
const chain = result.chain;
|
|
251548
|
-
const
|
|
251831
|
+
const ctx = newCtx();
|
|
251549
251832
|
const entries2 = [];
|
|
251550
251833
|
for (const property of chain.properties) {
|
|
251551
251834
|
if (property.wire) {
|
|
251552
|
-
const
|
|
251553
|
-
if (
|
|
251554
|
-
if (isBooleanExpr(
|
|
251835
|
+
const expr = resolveExpr(nodeById.get(property.wire.fromId), property.wire.fromProperty, ctx);
|
|
251836
|
+
if (expr === null) return { defer: ctx.defer ?? `property "${property.key}" has no statically known source` };
|
|
251837
|
+
if (isBooleanExpr(expr)) {
|
|
251555
251838
|
return { defer: `property "${property.key}" is fed a logic truth value \u2014 only truthiness sinks take one in this slice` };
|
|
251556
251839
|
}
|
|
251557
|
-
entries2.push({ key: property.key, expr
|
|
251840
|
+
entries2.push({ key: property.key, expr });
|
|
251558
251841
|
} else if (property.literal !== void 0) {
|
|
251559
251842
|
entries2.push({ key: property.key, expr: { kind: "literal", value: property.literal } });
|
|
251560
251843
|
}
|
|
@@ -251563,25 +251846,25 @@ ${body}
|
|
|
251563
251846
|
const insertNode = nodeById.get(chain.insertId);
|
|
251564
251847
|
const conflict = mintScopeConflict(node, "new", { kind: "minted", nodeId: chain.minted, wireKey: "" });
|
|
251565
251848
|
if (conflict !== void 0) return { defer: conflict };
|
|
251566
|
-
for (const
|
|
251567
|
-
if (
|
|
251568
|
-
return { defer: `the insert's ${
|
|
251849
|
+
for (const wire of component.connections.filter((c) => c.fromId === insertNode.id)) {
|
|
251850
|
+
if (wire.fromProperty === "failure") continue;
|
|
251851
|
+
return { defer: `the insert's ${wire.fromProperty} output is consumed \u2014 into a minted array this slice translates the Failure chain only` };
|
|
251569
251852
|
}
|
|
251570
251853
|
const fail2 = doneChainOf(insertNode, "failure");
|
|
251571
251854
|
if ("defer" in fail2) return { defer: fail2.defer };
|
|
251572
251855
|
const target = { kind: "minted", nodeId: chain.minted, wireKey: "" };
|
|
251573
251856
|
return {
|
|
251574
251857
|
action: { kind: "collection-add", collectionName: "", entries: entries2, minted: mintedTargetFor(target, chain.insertId, "insert", fail2.then) },
|
|
251575
|
-
consumes: [...chain.consumes, ...
|
|
251576
|
-
collapses: [chain.insertId, ...
|
|
251577
|
-
subscribes: [...
|
|
251858
|
+
consumes: [...chain.consumes, ...ctx.consumes, ...fail2.consumes],
|
|
251859
|
+
collapses: [chain.insertId, ...ctx.logicNodeIds, ...fail2.collapses],
|
|
251860
|
+
subscribes: [...ctx.subscriberIds, ...fail2.subscribes]
|
|
251578
251861
|
};
|
|
251579
251862
|
}
|
|
251580
251863
|
return {
|
|
251581
251864
|
action: { kind: "collection-add", collectionName: chain.collectionName, entries: entries2 },
|
|
251582
|
-
consumes: [...chain.consumes, ...
|
|
251583
|
-
collapses: [chain.insertId, ...
|
|
251584
|
-
subscribes:
|
|
251865
|
+
consumes: [...chain.consumes, ...ctx.consumes],
|
|
251866
|
+
collapses: [chain.insertId, ...ctx.logicNodeIds],
|
|
251867
|
+
subscribes: ctx.subscriberIds
|
|
251585
251868
|
};
|
|
251586
251869
|
}
|
|
251587
251870
|
if (node.type === "CollectionClear") return compileCollectionClear(node);
|
|
@@ -251601,6 +251884,30 @@ ${body}
|
|
|
251601
251884
|
if (node.type === "Condition") return compileCondition2(node);
|
|
251602
251885
|
if (node.type === SET_OBJECT_PROPERTIES_TYPE) return compileSetObjectProperties(node);
|
|
251603
251886
|
if (node.type === SET_COMPONENT_OBJECT_TYPE || node.type === SET_PARENT_COMPONENT_OBJECT_TYPE) return compileComponentObjectSet(node);
|
|
251887
|
+
return withSetDone(node, compileSetVariableWrite(node));
|
|
251888
|
+
};
|
|
251889
|
+
const withSetDone = (node, write) => {
|
|
251890
|
+
if ("defer" in write || write.action.kind !== "store-set") return write;
|
|
251891
|
+
const then2 = [];
|
|
251892
|
+
const consumes = [...write.consumes];
|
|
251893
|
+
const collapses = [...write.collapses ?? []];
|
|
251894
|
+
const subscribes = [...write.subscribes ?? []];
|
|
251895
|
+
for (const port5 of SET_VARIABLE_CHAIN_OUTPUTS) {
|
|
251896
|
+
if (!component.connections.some((c) => c.fromId === node.id && c.fromProperty === port5)) continue;
|
|
251897
|
+
const chain = doneChainOf(node, port5);
|
|
251898
|
+
if ("defer" in chain) {
|
|
251899
|
+
setDoneRefusals.set(`${node.id}:${port5}`, chain.defer);
|
|
251900
|
+
continue;
|
|
251901
|
+
}
|
|
251902
|
+
then2.push(...chain.then);
|
|
251903
|
+
consumes.push(...chain.consumes);
|
|
251904
|
+
collapses.push(...chain.collapses);
|
|
251905
|
+
subscribes.push(...chain.subscribes);
|
|
251906
|
+
}
|
|
251907
|
+
if (then2.length === 0) return { ...write, consumes, collapses, subscribes };
|
|
251908
|
+
return { action: { ...write.action, then: then2 }, consumes, collapses, subscribes };
|
|
251909
|
+
};
|
|
251910
|
+
const compileSetVariableWrite = (node) => {
|
|
251604
251911
|
const variableName = variableNameOf(node);
|
|
251605
251912
|
if (variableName === void 0) return { defer: "variable name is not a literal" };
|
|
251606
251913
|
const setWith = literalParam(node, "setWith");
|
|
@@ -251882,12 +252189,28 @@ ${body}
|
|
|
251882
252189
|
return { action: { kind: "branch", cond, whenTrue, whenFalse }, consumes, collapses, subscribes };
|
|
251883
252190
|
};
|
|
251884
252191
|
const compiledSinks = /* @__PURE__ */ new Map();
|
|
252192
|
+
const setDoneRefusals = /* @__PURE__ */ new Map();
|
|
251885
252193
|
const reactiveConditionDefers = /* @__PURE__ */ new Map();
|
|
251886
252194
|
const recordEffectDefers = /* @__PURE__ */ new Map();
|
|
251887
252195
|
const compiling = /* @__PURE__ */ new Set();
|
|
251888
252196
|
const compiledOf = (node, port5) => {
|
|
251889
252197
|
const valueWires = (node.type === "Set Variable" || node.type === GLOBAL_STORE_SET) && valueWiresInto(node).length > 1;
|
|
251890
|
-
const
|
|
252198
|
+
const basePlain = `${node.id}:${port5}${valueWires ? `@value:${lastValueWireOf(node)?.key ?? "none"}` : ""}`;
|
|
252199
|
+
const rowScope = rowChainScopes[rowChainScopes.length - 1] ?? null;
|
|
252200
|
+
const scopedKey = `${basePlain}@row:${rowScope ?? "none"}`;
|
|
252201
|
+
const scopes = relayTouchedKeys.get(basePlain);
|
|
252202
|
+
const key2 = scopes !== void 0 ? scopedKey : basePlain;
|
|
252203
|
+
relayTouches.push(/* @__PURE__ */ new Set());
|
|
252204
|
+
const result = compiledOfKeyed(node, port5, key2);
|
|
252205
|
+
const touched = relayTouches.pop();
|
|
252206
|
+
if (touched.size > 0) {
|
|
252207
|
+
relayTouchedKeys.set(basePlain, /* @__PURE__ */ new Set([...scopes ?? [], rowScope]));
|
|
252208
|
+
if (key2 === basePlain) compiledSinks.set(scopedKey, result);
|
|
252209
|
+
}
|
|
252210
|
+
if (touched.size > 0 || relayTouchedKeys.has(basePlain)) relayTouches[relayTouches.length - 1]?.add(rowScope ?? "none");
|
|
252211
|
+
return result;
|
|
252212
|
+
};
|
|
252213
|
+
const compiledOfKeyed = (node, port5, plainKey) => {
|
|
251891
252214
|
const scoped = [...verbChainScope].filter(([, s]) => s === "failure").map(([id]) => id);
|
|
251892
252215
|
const scopeKey = scoped.length > 0 ? `${plainKey}@failure:${[...scoped].sort().join(",")}` : void 0;
|
|
251893
252216
|
const readsScoped = (result2, form) => {
|
|
@@ -251962,6 +252285,9 @@ ${body}
|
|
|
251962
252285
|
return exprValidIn(expr.source, context, invokedScope) && whereExprs(expr.where).every((e) => exprValidIn(e, context, invokedScope));
|
|
251963
252286
|
case "input-text":
|
|
251964
252287
|
return context.kind === "dom" && context.nodeId === expr.inputId;
|
|
252288
|
+
// EXP-011 §100. Only in that repeater's row callbacks, which close over the firing row's item.
|
|
252289
|
+
case "row-relay":
|
|
252290
|
+
return context.kind === "dom" && context.nodeId === expr.repeaterId && (context.port?.startsWith(ITEM_OUTPUT_SIGNAL) ?? false);
|
|
251965
252291
|
case "control-event":
|
|
251966
252292
|
return context.kind === "dom" && context.nodeId === expr.controlId;
|
|
251967
252293
|
case "payload":
|
|
@@ -252077,7 +252403,9 @@ ${body}
|
|
|
252077
252403
|
*/
|
|
252078
252404
|
case "collection-remove":
|
|
252079
252405
|
return context.kind === "dom" && context.nodeId === action.repeaterId && context.port !== void 0 && context.port.startsWith(ITEM_OUTPUT_SIGNAL) && actionsValidIn(action.then, context, invokedScope);
|
|
252406
|
+
// P18-S127-SETDONE. The value, then the Done chain.
|
|
252080
252407
|
case "store-set":
|
|
252408
|
+
return exprValidIn(action.expr, context, invokedScope) && actionsValidIn(action.then ?? [], context, invokedScope);
|
|
252081
252409
|
case "globalstore-set":
|
|
252082
252410
|
return exprValidIn(action.expr, context, invokedScope);
|
|
252083
252411
|
// EXP-011 §47. Every value in the patch, then the chain.
|
|
@@ -252210,6 +252538,9 @@ ${body}
|
|
|
252210
252538
|
// EXP-011 §63. A call on the handle; the Done listener is validated where it compiles (the render context).
|
|
252211
252539
|
case "drag-snap":
|
|
252212
252540
|
return true;
|
|
252541
|
+
// P18-S128-FIELDSET. A call on the field's element, reading nothing.
|
|
252542
|
+
case "field-focus":
|
|
252543
|
+
return true;
|
|
252213
252544
|
// EXP-011 §52. A call on the handle; the node's listeners are validated where they compile.
|
|
252214
252545
|
case "script-signal":
|
|
252215
252546
|
return true;
|
|
@@ -252337,10 +252668,14 @@ ${body}
|
|
|
252337
252668
|
const wires = component.connections.filter((c) => c.toId === node.id && c.toProperty === spec.statePort);
|
|
252338
252669
|
if (wires.length === 0) continue;
|
|
252339
252670
|
if (spec.coerce === "textinput" && literalParam(node, "runOnChange-startValue") === false) {
|
|
252671
|
+
const viaSet = wiredPorts.has(`${node.id}:set`);
|
|
252340
252672
|
for (const w of wires) {
|
|
252341
252673
|
consumed.add(w.key);
|
|
252342
252674
|
notes.push(
|
|
252343
|
-
wireNote(
|
|
252675
|
+
wireNote(
|
|
252676
|
+
w,
|
|
252677
|
+
viaSet ? "Value is unticked under Run On Value Change \u2014 it reaches the field only when its Set is sent (P18-S128-FIELDSET)" : "Value is unticked under Run On Value Change \u2014 arrivals wait for a Set pulse, and nothing sends this field one"
|
|
252678
|
+
)
|
|
252344
252679
|
);
|
|
252345
252680
|
}
|
|
252346
252681
|
continue;
|
|
@@ -252410,6 +252745,37 @@ ${body}
|
|
|
252410
252745
|
}
|
|
252411
252746
|
}
|
|
252412
252747
|
}
|
|
252748
|
+
const rowRelays = /* @__PURE__ */ new Map();
|
|
252749
|
+
const relayedSignalsOf = (forEach) => [
|
|
252750
|
+
...new Set(
|
|
252751
|
+
component.connections.filter((c) => c.fromId === forEach.id && c.fromProperty.startsWith(ITEM_OUTPUT_SIGNAL)).map((c) => c.fromProperty.slice(ITEM_OUTPUT_SIGNAL.length))
|
|
252752
|
+
)
|
|
252753
|
+
].join(", ") || "relayed signal";
|
|
252754
|
+
const rowRelayOf = (forEach, fromProperty) => {
|
|
252755
|
+
if (!fromProperty.startsWith(ITEM_OUTPUT_VALUE)) {
|
|
252756
|
+
return { defer: `a repeater's "${fromProperty}" is not a row's relayed value \u2014 those register as "${ITEM_OUTPUT_VALUE}<name>"` };
|
|
252757
|
+
}
|
|
252758
|
+
const output = fromProperty.slice(ITEM_OUTPUT_VALUE.length);
|
|
252759
|
+
const key2 = `${forEach.id}\0${output}`;
|
|
252760
|
+
const known = rowRelays.get(key2);
|
|
252761
|
+
if (known !== void 0) return { stateName: known.name };
|
|
252762
|
+
const relayed = relayedInputOf(forEach, output);
|
|
252763
|
+
if ("defer" in relayed) return relayed;
|
|
252764
|
+
const { input, declared } = relayed;
|
|
252765
|
+
const tsType = declared === "unknown" || declared === "any" ? declared : `${declared} | undefined`;
|
|
252766
|
+
const stateVar = allocStateVar(
|
|
252767
|
+
`relayed ${output}`,
|
|
252768
|
+
"relayed",
|
|
252769
|
+
tsType,
|
|
252770
|
+
null,
|
|
252771
|
+
forEach.id,
|
|
252772
|
+
"relay",
|
|
252773
|
+
`What the row whose "${relayedSignalsOf(forEach)}" fired last holds on its "${output}" \u2014 undefined until one fires (foreach.tsx itemOutputSignalTriggered, EXP-011 \xA799).`
|
|
252774
|
+
);
|
|
252775
|
+
rowRelays.set(key2, stateVar);
|
|
252776
|
+
plan.rowRelays.push({ repeaterId: forEach.id, output, input, stateName: stateVar.name });
|
|
252777
|
+
return { stateName: stateVar.name };
|
|
252778
|
+
};
|
|
252413
252779
|
{
|
|
252414
252780
|
const valuePropByPort = new Map(outputInterface.valueProps.map((v) => [v.port, v]));
|
|
252415
252781
|
const wiresByPort = /* @__PURE__ */ new Map();
|
|
@@ -252434,15 +252800,17 @@ ${body}
|
|
|
252434
252800
|
}
|
|
252435
252801
|
const wire = wires[0];
|
|
252436
252802
|
const from = nodeById.get(wire.fromId);
|
|
252803
|
+
let relayExpr;
|
|
252437
252804
|
if (from?.type === "For Each") {
|
|
252438
|
-
|
|
252439
|
-
|
|
252440
|
-
|
|
252441
|
-
|
|
252442
|
-
|
|
252805
|
+
const relay = rowRelayOf(from, wire.fromProperty);
|
|
252806
|
+
if ("defer" in relay) {
|
|
252807
|
+
fail2(wire.key, relay.defer);
|
|
252808
|
+
continue;
|
|
252809
|
+
}
|
|
252810
|
+
relayExpr = { kind: "state-get", name: relay.stateName };
|
|
252443
252811
|
}
|
|
252444
252812
|
const ctx = newCtx();
|
|
252445
|
-
const expr = from === void 0 ? null : resolveExpr(from, wire.fromProperty, ctx);
|
|
252813
|
+
const expr = relayExpr ?? (from === void 0 ? null : resolveExpr(from, wire.fromProperty, ctx));
|
|
252446
252814
|
if (expr === null) {
|
|
252447
252815
|
fail2(
|
|
252448
252816
|
wire.key,
|
|
@@ -252477,6 +252845,11 @@ ${body}
|
|
|
252477
252845
|
}
|
|
252478
252846
|
}
|
|
252479
252847
|
}
|
|
252848
|
+
const siblingValueInputOf = (toNode, port5) => {
|
|
252849
|
+
if (plan.roleOf[toNode.id] !== "instance") return false;
|
|
252850
|
+
const target = ir.components.find((tc) => `/${tc.path}` === toNode.type);
|
|
252851
|
+
return target !== void 0 && target.nodes.some((n) => n.type === "Component Inputs" && n.declaredPorts.some((p) => p.plug === "output" && p.name === port5 && p.kind !== "signal"));
|
|
252852
|
+
};
|
|
252480
252853
|
for (const c of component.connections) {
|
|
252481
252854
|
if (consumed.has(c.key)) continue;
|
|
252482
252855
|
const fromNode = nodeById.get(c.fromId);
|
|
@@ -252496,7 +252869,7 @@ ${body}
|
|
|
252496
252869
|
const contentRole = (CONTENT_PARAMS[toNode.type] ?? {})[c.toProperty];
|
|
252497
252870
|
const truthinessSink = c.toProperty === "visible" || c.toProperty === "mounted";
|
|
252498
252871
|
const bindable = truthinessSink || contentRole === "children" || contentRole === "attr-not:disabled" || contentRole !== void 0 && contentRole.startsWith("attr:") || // EXP-011 §49. A wired style parameter the emitter prints inline.
|
|
252499
|
-
styleSinkOf(toNode, c.toProperty) !== void 0;
|
|
252872
|
+
styleSinkOf(toNode, c.toProperty) !== void 0 || siblingValueInputOf(toNode, c.toProperty);
|
|
252500
252873
|
if (!bindable) {
|
|
252501
252874
|
consumed.add(c.key);
|
|
252502
252875
|
notes.push(
|
|
@@ -252504,7 +252877,7 @@ ${body}
|
|
|
252504
252877
|
);
|
|
252505
252878
|
continue;
|
|
252506
252879
|
}
|
|
252507
|
-
if (c.toProperty
|
|
252880
|
+
if (routerMounted(toNode, c.toProperty)) {
|
|
252508
252881
|
consumed.add(c.key);
|
|
252509
252882
|
notes.push(wireNote(c, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
252510
252883
|
continue;
|
|
@@ -252516,7 +252889,8 @@ ${body}
|
|
|
252516
252889
|
targetLegacy: fromNode.type,
|
|
252517
252890
|
port: c.fromProperty,
|
|
252518
252891
|
toNodeId: toNode.id,
|
|
252519
|
-
toProperty: c.toProperty
|
|
252892
|
+
toProperty: c.toProperty,
|
|
252893
|
+
...siblingValueInputOf(toNode, c.toProperty) ? { intoInstance: true } : {}
|
|
252520
252894
|
});
|
|
252521
252895
|
}
|
|
252522
252896
|
const customLiftedVarByKey = /* @__PURE__ */ new Map();
|
|
@@ -252549,7 +252923,7 @@ ${body}
|
|
|
252549
252923
|
notes.push(wireNote(c, `custom node output "${c.fromProperty}" ${bindable}`));
|
|
252550
252924
|
continue;
|
|
252551
252925
|
}
|
|
252552
|
-
if (c.toProperty
|
|
252926
|
+
if (routerMounted(toNode, c.toProperty)) {
|
|
252553
252927
|
consumed.add(c.key);
|
|
252554
252928
|
notes.push(wireNote(c, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
252555
252929
|
continue;
|
|
@@ -252785,13 +253159,22 @@ ${body}
|
|
|
252785
253159
|
if (action.expr !== void 0) {
|
|
252786
253160
|
const e = snapExpr(action.expr, snap2);
|
|
252787
253161
|
if ("defer" in e) return e;
|
|
252788
|
-
snap2.set(action.name, e);
|
|
253162
|
+
snap2.set(action.name, action.field?.port === "set" ? "op" : e);
|
|
252789
253163
|
return { ...action, expr: e };
|
|
252790
253164
|
}
|
|
252791
253165
|
snap2.set(action.name, "op");
|
|
252792
253166
|
return action;
|
|
252793
253167
|
}
|
|
252794
|
-
|
|
253168
|
+
// P18-S127-SETDONE. The value is read where the handler is; the Done chain follows, one arm always taken, so it
|
|
253169
|
+
// carries the map onward (an Object's Set's treatment).
|
|
253170
|
+
case "store-set": {
|
|
253171
|
+
const e = snapExpr(action.expr, snap2);
|
|
253172
|
+
if ("defer" in e) return e;
|
|
253173
|
+
if (action.then === void 0) return { ...action, expr: e };
|
|
253174
|
+
const then2 = snapActionList(action.then, snap2);
|
|
253175
|
+
if (!Array.isArray(then2)) return then2;
|
|
253176
|
+
return { ...action, expr: e, then: then2 };
|
|
253177
|
+
}
|
|
252795
253178
|
case "globalstore-set": {
|
|
252796
253179
|
const e = snapExpr(action.expr, snap2);
|
|
252797
253180
|
if ("defer" in e) return e;
|
|
@@ -253409,6 +253792,7 @@ ${body}
|
|
|
253409
253792
|
}
|
|
253410
253793
|
}
|
|
253411
253794
|
const receiverActions = /* @__PURE__ */ new Map();
|
|
253795
|
+
const inputPulseActions = /* @__PURE__ */ new Map();
|
|
253412
253796
|
const unreachedOwnerOf = (connection) => excluded.owners.get(connection.fromId) ?? (connection.fromProperty === "didMount" ? excluded.mounts.get(connection.fromId) : void 0);
|
|
253413
253797
|
const refuseUnreached = (connection, toNode, compiled2, reason2, outputsSink) => {
|
|
253414
253798
|
notes.push(wireNote(connection, reason2));
|
|
@@ -253419,6 +253803,11 @@ ${body}
|
|
|
253419
253803
|
if (dispositions[id] === void 0) dispositions[id] = { kind: "deferred", to: "EXP-003", reason: reason2 };
|
|
253420
253804
|
}
|
|
253421
253805
|
};
|
|
253806
|
+
const instanceBeforeAttach = /* @__PURE__ */ new Map();
|
|
253807
|
+
const isRunSuccessWire = (connection) => connection.fromProperty === "success" && nodeById.get(connection.fromId)?.type === JS_FUNCTION && wiredPorts.has(`${connection.fromId}:run`);
|
|
253808
|
+
for (const node of component.nodes) {
|
|
253809
|
+
if (plan.roleOf[node.id] === "instance" || plan.roleOf[node.id] === "input") instanceBeforeAttach.set(node.id, dispositions[node.id]);
|
|
253810
|
+
}
|
|
253422
253811
|
for (const connection of component.connections) {
|
|
253423
253812
|
if (consumed.has(connection.key)) continue;
|
|
253424
253813
|
const toNode = nodeById.get(connection.toId);
|
|
@@ -253438,6 +253827,7 @@ ${body}
|
|
|
253438
253827
|
if (fromNode !== void 0 && jsNodeKindOf(fromNode.type) !== null && connection.fromProperty === "done") {
|
|
253439
253828
|
continue;
|
|
253440
253829
|
}
|
|
253830
|
+
if (isRunSuccessWire(connection)) continue;
|
|
253441
253831
|
consumed.add(connection.key);
|
|
253442
253832
|
if (fromNode?.type === "For Each" && connection.fromProperty.startsWith(ITEM_OUTPUT_SIGNAL)) {
|
|
253443
253833
|
const relayed = connection.fromProperty.slice(ITEM_OUTPUT_SIGNAL.length);
|
|
@@ -253462,7 +253852,13 @@ ${body}
|
|
|
253462
253852
|
}
|
|
253463
253853
|
compiled2 = sink;
|
|
253464
253854
|
} else {
|
|
253465
|
-
|
|
253855
|
+
const rowScope = fromNode?.type === "For Each" && connection.fromProperty.startsWith(ITEM_OUTPUT_SIGNAL) ? fromNode.id : void 0;
|
|
253856
|
+
if (rowScope !== void 0) rowChainScopes.push(rowScope);
|
|
253857
|
+
try {
|
|
253858
|
+
compiled2 = compiledOf(toNode, connection.toProperty);
|
|
253859
|
+
} finally {
|
|
253860
|
+
if (rowScope !== void 0) rowChainScopes.pop();
|
|
253861
|
+
}
|
|
253466
253862
|
}
|
|
253467
253863
|
if ("defer" in compiled2) {
|
|
253468
253864
|
if (outputsSink) {
|
|
@@ -253555,6 +253951,40 @@ ${body}
|
|
|
253555
253951
|
for (const id of compiled2.subscribes ?? []) boundSubscribers.add(id);
|
|
253556
253952
|
continue;
|
|
253557
253953
|
}
|
|
253954
|
+
if (fromNode?.type === "Component Inputs" && signalInputPortsOf(component).has(connection.fromProperty)) {
|
|
253955
|
+
const refuse = (reason2) => {
|
|
253956
|
+
if (outputsSink) {
|
|
253957
|
+
if (!failedOutputsNodes.has(toNode.id)) failedOutputsNodes.set(toNode.id, reason2);
|
|
253958
|
+
} else {
|
|
253959
|
+
dispositions[toNode.id] = { kind: "deferred", to: "EXP-003", reason: reason2 };
|
|
253960
|
+
}
|
|
253961
|
+
notes.push(wireNote(connection, reason2));
|
|
253962
|
+
};
|
|
253963
|
+
if (!plan.file) {
|
|
253964
|
+
refuse("the component emits no file to host the effect a signal input runs");
|
|
253965
|
+
continue;
|
|
253966
|
+
}
|
|
253967
|
+
if (!actionsValidIn([compiled2.action], { kind: "render" })) {
|
|
253968
|
+
refuse("the action reads values that only exist inside a handler");
|
|
253969
|
+
continue;
|
|
253970
|
+
}
|
|
253971
|
+
const snappedIn = snapAction(compiled2.action, chainSnapshotFor(`input:${connection.fromProperty}`));
|
|
253972
|
+
const lateInWrite = "defer" in snappedIn ? void 0 : lateQueryFetchWrite([...inputPulseActions.get(connection.fromProperty) ?? [], snappedIn]);
|
|
253973
|
+
if (lateInWrite !== void 0) {
|
|
253974
|
+
refuse(lateInWrite);
|
|
253975
|
+
continue;
|
|
253976
|
+
}
|
|
253977
|
+
if ("defer" in snappedIn) {
|
|
253978
|
+
refuse(snappedIn.defer);
|
|
253979
|
+
continue;
|
|
253980
|
+
}
|
|
253981
|
+
inputPulseActions.set(connection.fromProperty, [...inputPulseActions.get(connection.fromProperty) ?? [], snappedIn]);
|
|
253982
|
+
if (!outputsSink) dispositions[toNode.id] = { kind: "collapsed", into: fromNode.id };
|
|
253983
|
+
for (const id of compiled2.collapses ?? []) dispositions[id] = { kind: "collapsed", into: fromNode.id };
|
|
253984
|
+
for (const key2 of compiled2.consumes) consumed.add(key2);
|
|
253985
|
+
for (const id of compiled2.subscribes ?? []) boundSubscribers.add(id);
|
|
253986
|
+
continue;
|
|
253987
|
+
}
|
|
253558
253988
|
if (fromNode?.type === "Event Receiver" && connection.fromProperty === "eventReceived") {
|
|
253559
253989
|
const eligible = receiverEligible(fromNode);
|
|
253560
253990
|
if ("defer" in eligible) {
|
|
@@ -253605,6 +254035,79 @@ ${body}
|
|
|
253605
254035
|
}
|
|
253606
254036
|
notes.push(wireNote(connection, pulseRefusal ?? `the trigger is not a rendered element event or a receiver`));
|
|
253607
254037
|
}
|
|
254038
|
+
const inSenderRowScope = (senderId, port5, fn) => {
|
|
254039
|
+
const scopes = [...relayTouchedKeys].find(([key2]) => key2 === `${senderId}:${port5}` || key2.startsWith(`${senderId}:${port5}@`))?.[1];
|
|
254040
|
+
const scope = [...scopes ?? []].find((s) => s !== null) ?? null;
|
|
254041
|
+
if (scope !== null) rowChainScopes.push(scope);
|
|
254042
|
+
try {
|
|
254043
|
+
return fn();
|
|
254044
|
+
} finally {
|
|
254045
|
+
if (scope !== null) rowChainScopes.pop();
|
|
254046
|
+
}
|
|
254047
|
+
};
|
|
254048
|
+
for (const connection of component.connections) {
|
|
254049
|
+
if (consumed.has(connection.key)) continue;
|
|
254050
|
+
const fromNode = nodeById.get(connection.fromId);
|
|
254051
|
+
if (fromNode?.type !== "Set Variable" || !SET_VARIABLE_CHAIN_OUTPUTS.includes(connection.fromProperty)) continue;
|
|
254052
|
+
const toNode = nodeById.get(connection.toId);
|
|
254053
|
+
if (!toNode) continue;
|
|
254054
|
+
const outputsSink = toNode.type === "Component Outputs";
|
|
254055
|
+
if (!outputsSink && !isTriggerInto(toNode, connection.toProperty)) continue;
|
|
254056
|
+
consumed.add(connection.key);
|
|
254057
|
+
const sink = outputsSink ? outputSinkOf(connection.toProperty, fromNode, connection.fromProperty) : inSenderRowScope(fromNode.id, "do", () => compiledOf(toNode, connection.toProperty));
|
|
254058
|
+
if ("drop" in sink) {
|
|
254059
|
+
notes.push(wireNote(connection, sink.drop));
|
|
254060
|
+
continue;
|
|
254061
|
+
}
|
|
254062
|
+
if ("defer" in sink) {
|
|
254063
|
+
if (outputsSink) {
|
|
254064
|
+
if (!failedOutputsNodes.has(toNode.id)) failedOutputsNodes.set(toNode.id, sink.defer);
|
|
254065
|
+
} else if (dispositions[toNode.id]?.kind !== "collapsed" && dispositions[toNode.id]?.kind !== "static") {
|
|
254066
|
+
dispositions[toNode.id] = { kind: "deferred", to: "EXP-003", reason: sink.defer };
|
|
254067
|
+
}
|
|
254068
|
+
notes.push(wireNote(connection, sink.defer));
|
|
254069
|
+
continue;
|
|
254070
|
+
}
|
|
254071
|
+
const trigger = `trigger ${connection.fromId}.${connection.fromProperty}`;
|
|
254072
|
+
const chainRefusal = setDoneRefusals.get(`${connection.fromId}:${connection.fromProperty}`);
|
|
254073
|
+
const own2 = dispositions[fromNode.id];
|
|
254074
|
+
const compiled2 = [...compiledSinks].find(([key2]) => key2 === `${fromNode.id}:do` || key2.startsWith(`${fromNode.id}:do@`))?.[1];
|
|
254075
|
+
const reason2 = chainRefusal !== void 0 ? `${trigger}: the Set Variable's ${connection.fromProperty === "done" ? "Done" : "Completed"} chain is not exported \u2014 ${chainRefusal}` : own2?.kind === "deferred" ? `${trigger}: the Set Variable that sends it is not exported \u2014 ${own2.reason}` : compiled2 !== void 0 && "defer" in compiled2 ? `${trigger}: the Set Variable that sends it is not exported \u2014 ${compiled2.defer}` : `${trigger}: the Set Variable that sends it is never run by an exported trigger`;
|
|
254076
|
+
if (outputsSink) {
|
|
254077
|
+
if (!failedOutputsNodes.has(toNode.id)) failedOutputsNodes.set(toNode.id, reason2);
|
|
254078
|
+
} else if (dispositions[toNode.id] === void 0) {
|
|
254079
|
+
dispositions[toNode.id] = { kind: "deferred", to: "EXP-003", reason: reason2 };
|
|
254080
|
+
}
|
|
254081
|
+
notes.push(wireNote(connection, reason2));
|
|
254082
|
+
}
|
|
254083
|
+
for (const connection of component.connections) {
|
|
254084
|
+
if (consumed.has(connection.key) || !isRunSuccessWire(connection)) continue;
|
|
254085
|
+
const fromNode = nodeById.get(connection.fromId);
|
|
254086
|
+
const toNode = nodeById.get(connection.toId);
|
|
254087
|
+
if (!toNode) continue;
|
|
254088
|
+
const outputsSink = toNode.type === "Component Outputs";
|
|
254089
|
+
if (!outputsSink && !isTriggerInto(toNode, connection.toProperty)) continue;
|
|
254090
|
+
consumed.add(connection.key);
|
|
254091
|
+
const sink = outputsSink ? outputSinkOf(connection.toProperty, fromNode, connection.fromProperty) : inSenderRowScope(fromNode.id, "run", () => compiledOf(toNode, connection.toProperty));
|
|
254092
|
+
if ("drop" in sink) {
|
|
254093
|
+
notes.push(wireNote(connection, sink.drop));
|
|
254094
|
+
continue;
|
|
254095
|
+
}
|
|
254096
|
+
const run6 = compiledSinks.get(`${fromNode.id}:run`);
|
|
254097
|
+
const trigger = `trigger ${connection.fromId}.success`;
|
|
254098
|
+
const reason2 = "defer" in sink ? sink.defer : run6 !== void 0 && "defer" in run6 ? `${trigger}: the Function that sends it is not exported \u2014 ${run6.defer}` : `${trigger}: the Function that sends it is never run by an exported trigger`;
|
|
254099
|
+
if (outputsSink) {
|
|
254100
|
+
if (!failedOutputsNodes.has(toNode.id)) failedOutputsNodes.set(toNode.id, reason2);
|
|
254101
|
+
} else if (dispositions[toNode.id] === void 0 || "defer" in sink && dispositions[toNode.id]?.kind !== "collapsed" && dispositions[toNode.id]?.kind !== "static") {
|
|
254102
|
+
dispositions[toNode.id] = { kind: "deferred", to: "EXP-003", reason: reason2 };
|
|
254103
|
+
}
|
|
254104
|
+
notes.push(wireNote(connection, reason2));
|
|
254105
|
+
}
|
|
254106
|
+
for (const [id, before] of instanceBeforeAttach) {
|
|
254107
|
+
if (dispositions[id]?.kind !== "deferred" || before?.kind === "deferred") continue;
|
|
254108
|
+
if (before === void 0) delete dispositions[id];
|
|
254109
|
+
else dispositions[id] = before;
|
|
254110
|
+
}
|
|
253608
254111
|
for (const node of component.nodes) {
|
|
253609
254112
|
if (node.type !== "Component Outputs" || dispositions[node.id] !== void 0) continue;
|
|
253610
254113
|
const failure3 = failedOutputsNodes.get(node.id);
|
|
@@ -253639,6 +254142,7 @@ ${body}
|
|
|
253639
254142
|
plan.receivers.push({ nodeId: node.id, channelName: channelNameOf(node), actions });
|
|
253640
254143
|
dispositions[node.id] = { kind: "collapsed", into: `src/${plan.file.dir}/${plan.file.fileBase}.tsx` };
|
|
253641
254144
|
}
|
|
254145
|
+
for (const [port5, actions] of inputPulseActions) plan.inputPulses.push({ port: port5, actions });
|
|
253642
254146
|
{
|
|
253643
254147
|
const attachedSlotKeys = /* @__PURE__ */ new Set();
|
|
253644
254148
|
const attachedMutations = attachedRecordVerbs;
|
|
@@ -253711,6 +254215,8 @@ ${body}
|
|
|
253711
254215
|
scanActions(action.failThen);
|
|
253712
254216
|
} else if (action.kind === "log") {
|
|
253713
254217
|
scanActions(action.then);
|
|
254218
|
+
} else if (action.kind === "store-set") {
|
|
254219
|
+
scanActions(action.then ?? []);
|
|
253714
254220
|
} else if (action.kind === "delay") {
|
|
253715
254221
|
scanActions(action.then);
|
|
253716
254222
|
scanActions(action.unchangedThen);
|
|
@@ -253742,6 +254248,7 @@ ${body}
|
|
|
253742
254248
|
for (const byPort of Object.values(plan.handlers)) for (const actions of Object.values(byPort)) scanActions(actions);
|
|
253743
254249
|
for (const actions of Object.values(plan.changeHandlers)) scanActions(actions);
|
|
253744
254250
|
for (const receiver of plan.receivers) scanActions(receiver.actions);
|
|
254251
|
+
for (const pulse of plan.inputPulses) scanActions(pulse.actions);
|
|
253745
254252
|
for (const run6 of plan.runTasks) for (const chain of Object.values(run6.listeners)) if (chain !== void 0) scanActions(chain);
|
|
253746
254253
|
if (plan.task !== void 0) scanActions(plan.task.actions);
|
|
253747
254254
|
for (const item of plan.repeaterItems) scanActions(item.actions);
|
|
@@ -254097,7 +254604,7 @@ ${body}
|
|
|
254097
254604
|
notes.push(wireNote(connection, VARIABLE_FROZEN));
|
|
254098
254605
|
continue;
|
|
254099
254606
|
}
|
|
254100
|
-
if (connection.toProperty
|
|
254607
|
+
if (routerMounted(toNode, connection.toProperty)) {
|
|
254101
254608
|
notes.push(wireNote(connection, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
254102
254609
|
continue;
|
|
254103
254610
|
}
|
|
@@ -254117,7 +254624,7 @@ ${body}
|
|
|
254117
254624
|
notes.push(wireNote(connection, `${read2.defer}`));
|
|
254118
254625
|
continue;
|
|
254119
254626
|
}
|
|
254120
|
-
if (connection.toProperty
|
|
254627
|
+
if (routerMounted(toNode, connection.toProperty)) {
|
|
254121
254628
|
notes.push(wireNote(connection, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
254122
254629
|
continue;
|
|
254123
254630
|
}
|
|
@@ -254188,7 +254695,7 @@ ${body}
|
|
|
254188
254695
|
);
|
|
254189
254696
|
continue;
|
|
254190
254697
|
}
|
|
254191
|
-
if (connection.toProperty
|
|
254698
|
+
if (routerMounted(toNode, connection.toProperty)) {
|
|
254192
254699
|
notes.push(wireNote(connection, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
254193
254700
|
continue;
|
|
254194
254701
|
}
|
|
@@ -254214,7 +254721,7 @@ ${body}
|
|
|
254214
254721
|
const bindable = truthinessSink || contentRole === "children" || contentRole === "attr-not:disabled" || contentRole !== void 0 && contentRole.startsWith("attr:") || // EXP-011 §49. A wired style parameter the emitter prints inline.
|
|
254215
254722
|
styleSinkOf(toNode, connection.toProperty) !== void 0;
|
|
254216
254723
|
if (!bindable) continue;
|
|
254217
|
-
if (connection.toProperty
|
|
254724
|
+
if (routerMounted(toNode, connection.toProperty)) continue;
|
|
254218
254725
|
const ctx = newCtx();
|
|
254219
254726
|
const expr = resolveExpr(fromNode, connection.fromProperty, ctx);
|
|
254220
254727
|
if (expr === null) continue;
|
|
@@ -254251,7 +254758,7 @@ ${body}
|
|
|
254251
254758
|
const bindable = truthinessSink || contentRole === "children" || contentRole === "attr-not:disabled" || contentRole !== void 0 && contentRole.startsWith("attr:") || // EXP-011 §49. A wired style parameter the emitter prints inline.
|
|
254252
254759
|
styleSinkOf(toNode, connection.toProperty) !== void 0;
|
|
254253
254760
|
if (!bindable) continue;
|
|
254254
|
-
if (connection.toProperty
|
|
254761
|
+
if (routerMounted(toNode, connection.toProperty)) continue;
|
|
254255
254762
|
const deadRow = excluded.rows.get(fromNode.id);
|
|
254256
254763
|
if (deadRow !== void 0) {
|
|
254257
254764
|
consumed.add(connection.key);
|
|
@@ -254325,7 +254832,7 @@ ${body}
|
|
|
254325
254832
|
);
|
|
254326
254833
|
continue;
|
|
254327
254834
|
}
|
|
254328
|
-
if (connection.toProperty
|
|
254835
|
+
if (routerMounted(toNode, connection.toProperty)) {
|
|
254329
254836
|
consumed.add(connection.key);
|
|
254330
254837
|
notes.push(wireNote(connection, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
254331
254838
|
continue;
|
|
@@ -254364,7 +254871,7 @@ ${body}
|
|
|
254364
254871
|
const bindable = truthinessSink || contentRole === "children" || contentRole === "attr-not:disabled" || contentRole !== void 0 && contentRole.startsWith("attr:") || // EXP-011 §49. A wired style parameter the emitter prints inline.
|
|
254365
254872
|
styleSinkOf(toNode, connection.toProperty) !== void 0;
|
|
254366
254873
|
if (!bindable) continue;
|
|
254367
|
-
if (connection.toProperty
|
|
254874
|
+
if (routerMounted(toNode, connection.toProperty)) continue;
|
|
254368
254875
|
const objectStore = objectStoreNodes.get(fromNode.id);
|
|
254369
254876
|
if (objectStore !== void 0) {
|
|
254370
254877
|
consumed.add(connection.key);
|
|
@@ -254399,7 +254906,7 @@ ${body}
|
|
|
254399
254906
|
const bindable = truthinessSink || contentRole === "children" || contentRole === "attr-not:disabled" || contentRole !== void 0 && contentRole.startsWith("attr:") || // EXP-011 §49. A wired style parameter the emitter prints inline.
|
|
254400
254907
|
styleSinkOf(toNode, connection.toProperty) !== void 0;
|
|
254401
254908
|
if (!bindable) continue;
|
|
254402
|
-
if (connection.toProperty
|
|
254909
|
+
if (routerMounted(toNode, connection.toProperty)) continue;
|
|
254403
254910
|
const ctx = newCtx();
|
|
254404
254911
|
const expr = resolveExpr(fromNode, connection.fromProperty, ctx);
|
|
254405
254912
|
if (expr === null) {
|
|
@@ -254453,7 +254960,7 @@ ${body}
|
|
|
254453
254960
|
continue;
|
|
254454
254961
|
}
|
|
254455
254962
|
if (fromNode?.type === "Component Inputs" && toNode && rendered2.has(toNode.id)) {
|
|
254456
|
-
if (connection.toProperty
|
|
254963
|
+
if (routerMounted(toNode, connection.toProperty)) {
|
|
254457
254964
|
consumed.add(connection.key);
|
|
254458
254965
|
notes.push(wireNote(connection, `a mounted wire into the component root is a router concern \u2014 not translated in this slice`));
|
|
254459
254966
|
continue;
|
|
@@ -254893,6 +255400,9 @@ ${body}
|
|
|
254893
255400
|
// EXP-011 §63. Nothing to read; the options and listeners are walked off the plan below.
|
|
254894
255401
|
case "drag-snap":
|
|
254895
255402
|
break;
|
|
255403
|
+
// P18-S128-FIELDSET. Nothing to read.
|
|
255404
|
+
case "field-focus":
|
|
255405
|
+
break;
|
|
254896
255406
|
// EXP-011 §52. The same: the inputs and listeners are walked off the plan below.
|
|
254897
255407
|
case "script-signal":
|
|
254898
255408
|
break;
|
|
@@ -254933,6 +255443,10 @@ ${body}
|
|
|
254933
255443
|
case "state-set":
|
|
254934
255444
|
if (action.expr !== void 0) walkExpr(action.expr);
|
|
254935
255445
|
break;
|
|
255446
|
+
// P18-S127-SETDONE. A Set Variable's Done chain (its own value is not walked here, as before).
|
|
255447
|
+
case "store-set":
|
|
255448
|
+
walkActions(action.then ?? []);
|
|
255449
|
+
break;
|
|
254936
255450
|
default:
|
|
254937
255451
|
break;
|
|
254938
255452
|
}
|
|
@@ -254944,6 +255458,7 @@ ${body}
|
|
|
254944
255458
|
for (const byPort of Object.values(plan.handlers)) for (const actions of Object.values(byPort)) walkActions(actions);
|
|
254945
255459
|
for (const actions of Object.values(plan.changeHandlers)) walkActions(actions);
|
|
254946
255460
|
for (const receiver of plan.receivers) walkActions(receiver.actions);
|
|
255461
|
+
for (const pulse of plan.inputPulses) walkActions(pulse.actions);
|
|
254947
255462
|
for (const effect of plan.branchEffects) walkActions([effect.action]);
|
|
254948
255463
|
for (const effect of plan.valueChangedEffects) {
|
|
254949
255464
|
walkExpr(effect.watch);
|
|
@@ -255007,6 +255522,23 @@ ${body}
|
|
|
255007
255522
|
for (const id of wiring.collapses) dispositions[id] = { kind: "collapsed", into };
|
|
255008
255523
|
for (const id of wiring.subscribes) boundSubscribers.add(id);
|
|
255009
255524
|
}
|
|
255525
|
+
for (const relay of [...plan.rowRelays]) {
|
|
255526
|
+
const repeater = plan.repeaters[relay.repeaterId];
|
|
255527
|
+
const fed = repeater !== void 0 && (plan.queries.some((q) => q.nodeId === repeater.itemsQueryId) || repeater.itemsCollectionName != null || repeater.itemsExpr !== void 0 || plan.staticData.some((sd) => sd.nodeId === repeater.itemsStaticId));
|
|
255528
|
+
if (fed) continue;
|
|
255529
|
+
const reason2 = `the repeater relaying "${relay.output}" draws no rows in the export (its items are not fed by a query, a named array, or a statically-known list), so no row ever relays it`;
|
|
255530
|
+
plan.rowRelays.splice(plan.rowRelays.indexOf(relay), 1);
|
|
255531
|
+
plan.stateVars = plan.stateVars.filter((v) => v.name !== relay.stateName);
|
|
255532
|
+
plan.pushEffects = plan.pushEffects.filter((e) => !(e.expr.kind === "state-get" && e.expr.name === relay.stateName));
|
|
255533
|
+
for (const wire of component.connections) {
|
|
255534
|
+
if (wire.fromId !== relay.repeaterId || wire.fromProperty !== `${ITEM_OUTPUT_VALUE}${relay.output}`) continue;
|
|
255535
|
+
const outputs2 = nodeById.get(wire.toId);
|
|
255536
|
+
if (outputs2?.type !== "Component Outputs") continue;
|
|
255537
|
+
plan.liftedOutputProps = plan.liftedOutputProps.filter((l) => l.port !== wire.toProperty);
|
|
255538
|
+
notes.push(wireNote(wire, reason2));
|
|
255539
|
+
if (dispositions[outputs2.id]?.kind !== "deferred") dispositions[outputs2.id] = { kind: "deferred", to: "EXP-003", reason: reason2 };
|
|
255540
|
+
}
|
|
255541
|
+
}
|
|
255010
255542
|
for (const connection of component.connections) {
|
|
255011
255543
|
if (consumed.has(connection.key)) continue;
|
|
255012
255544
|
const kitEndpoint = undeclaredKitPortOf(connection, nodeById, kits);
|
|
@@ -255046,17 +255578,19 @@ ${body}
|
|
|
255046
255578
|
const label2 = node.authoredLabel ? `"${node.authoredLabel}"` : "The text input";
|
|
255047
255579
|
const refuse = (why) => notes.push(`node ${node.id} (${node.type}): its Value at mount is not announced \u2014 ${why}; typing still runs its chain (P18-S116-FIELDMOUNT)`);
|
|
255048
255580
|
let away;
|
|
255581
|
+
const gates = [];
|
|
255049
255582
|
for (let id = node.id; id !== void 0; id = parentOf.get(id)) {
|
|
255050
255583
|
const bound = plan.bindings[id]?.["mounted"];
|
|
255051
255584
|
if (bound !== void 0) {
|
|
255052
255585
|
const literal2 = bound.kind === "computed" && (bound.expr.kind === "literal" || bound.expr.kind === "undefined");
|
|
255053
|
-
if (!literal2) away =
|
|
255586
|
+
if (!literal2 && id === plan.collapsedGroupId) away = "uncarried";
|
|
255587
|
+
else if (!literal2) gates.push(id);
|
|
255054
255588
|
else if (bound.expr.kind === "undefined" || !bound.expr.value) away = "never";
|
|
255055
255589
|
} else if (literalParam(nodeById.get(id), "mounted") === false) away = "never";
|
|
255056
255590
|
}
|
|
255057
255591
|
if (away === "never") continue;
|
|
255058
|
-
if (away === "
|
|
255059
|
-
refuse("it
|
|
255592
|
+
if (away === "uncarried") {
|
|
255593
|
+
refuse("it sits in the page's own Group, whose Mounted is wired and not carried (the page is drawn), so when it appears is not known here");
|
|
255060
255594
|
continue;
|
|
255061
255595
|
}
|
|
255062
255596
|
const type = literalParam(node, "type");
|
|
@@ -255080,7 +255614,8 @@ ${body}
|
|
|
255080
255614
|
inputId: node.id,
|
|
255081
255615
|
start: raw === void 0 ? "" : String(raw),
|
|
255082
255616
|
...sync !== void 0 ? { source: sync.source } : {},
|
|
255083
|
-
|
|
255617
|
+
...gates.length > 0 ? { gates } : {},
|
|
255618
|
+
comment: gates.length > 0 ? `${label2} announces its Value the first time it mounts, and its chain runs with it (TextInput.tsx componentDidMount \u2192 setText(startValue)); it mounts when a value says.` : `${label2} announces its Value as it mounts, and its chain runs with it (TextInput.tsx componentDidMount \u2192 setText(startValue)).`
|
|
255084
255619
|
});
|
|
255085
255620
|
}
|
|
255086
255621
|
}
|
|
@@ -255340,6 +255875,8 @@ ${body}
|
|
|
255340
255875
|
// EXP-011 §63. Nothing nested; the listeners are filled off the plan below.
|
|
255341
255876
|
case "drag-snap":
|
|
255342
255877
|
break;
|
|
255878
|
+
case "field-focus":
|
|
255879
|
+
break;
|
|
255343
255880
|
case "script-signal":
|
|
255344
255881
|
break;
|
|
255345
255882
|
// EXP-011 §53. Nothing nested on the action; the listeners are filled off the plan below.
|
|
@@ -255467,6 +256004,10 @@ ${body}
|
|
|
255467
256004
|
fillMaterialize(action.then);
|
|
255468
256005
|
fillMaterialize(action.failThen);
|
|
255469
256006
|
break;
|
|
256007
|
+
// P18-S127-SETDONE. A Set Variable's Done chain.
|
|
256008
|
+
case "store-set":
|
|
256009
|
+
fillMaterialize(action.then ?? []);
|
|
256010
|
+
break;
|
|
255470
256011
|
default:
|
|
255471
256012
|
break;
|
|
255472
256013
|
}
|
|
@@ -255475,6 +256016,7 @@ ${body}
|
|
|
255475
256016
|
for (const byPort of Object.values(plan.handlers)) for (const actions of Object.values(byPort)) fillMaterialize(actions);
|
|
255476
256017
|
for (const actions of Object.values(plan.changeHandlers)) fillMaterialize(actions);
|
|
255477
256018
|
for (const receiver of plan.receivers) fillMaterialize(receiver.actions);
|
|
256019
|
+
for (const pulse of plan.inputPulses) fillMaterialize(pulse.actions);
|
|
255478
256020
|
for (const effect of plan.branchEffects) fillMaterialize([effect.action]);
|
|
255479
256021
|
for (const effect of plan.valueChangedEffects) fillMaterialize(effect.actions);
|
|
255480
256022
|
for (const ticked3 of plan.tickedRuns) fillMaterialize([ticked3.action]);
|
|
@@ -255574,11 +256116,45 @@ ${body}
|
|
|
255574
256116
|
if (node === void 0 || !component.connections.some((c) => c.toId === id && isTriggerInto(node, c.toProperty))) continue;
|
|
255575
256117
|
dispositions[id] = { kind: "static" };
|
|
255576
256118
|
}
|
|
256119
|
+
if (instancePulseVars.size > 0) {
|
|
256120
|
+
const pulsed = /* @__PURE__ */ new Set();
|
|
256121
|
+
const seen = /* @__PURE__ */ new Set();
|
|
256122
|
+
const visit = (value) => {
|
|
256123
|
+
if (value === null || typeof value !== "object" || seen.has(value)) return;
|
|
256124
|
+
seen.add(value);
|
|
256125
|
+
if (Array.isArray(value)) return value.forEach(visit);
|
|
256126
|
+
const v = value;
|
|
256127
|
+
if (v.kind === "state-set" && v.op === "inc" && typeof v.name === "string") pulsed.add(v.name);
|
|
256128
|
+
for (const child of Object.values(value)) visit(child);
|
|
256129
|
+
};
|
|
256130
|
+
for (const [key2, value] of Object.entries(plan)) if (key2 !== "stateVars" && key2 !== "bindings") visit(value);
|
|
256131
|
+
for (const [key2, stateVar] of instancePulseVars) {
|
|
256132
|
+
if (pulsed.has(stateVar.name)) continue;
|
|
256133
|
+
const [nodeId, port5] = key2.split("\0");
|
|
256134
|
+
plan.stateVars = plan.stateVars.filter((v) => v !== stateVar);
|
|
256135
|
+
plan.instancePulses = plan.instancePulses.filter((p) => p.stateName !== stateVar.name);
|
|
256136
|
+
if (plan.bindings[nodeId] !== void 0) delete plan.bindings[nodeId][port5];
|
|
256137
|
+
}
|
|
256138
|
+
}
|
|
255577
256139
|
sweepRefusedScripts();
|
|
255578
256140
|
sweepUnreportedDeferrals();
|
|
255579
256141
|
collectRefusals();
|
|
255580
256142
|
return plan;
|
|
255581
256143
|
}
|
|
256144
|
+
var signalInputPortsCache = /* @__PURE__ */ new WeakMap();
|
|
256145
|
+
function signalInputPortsOf(component) {
|
|
256146
|
+
if (component === void 0) return /* @__PURE__ */ new Set();
|
|
256147
|
+
let ports = signalInputPortsCache.get(component);
|
|
256148
|
+
if (ports === void 0) {
|
|
256149
|
+
ports = /* @__PURE__ */ new Set();
|
|
256150
|
+
for (const node of component.nodes) {
|
|
256151
|
+
if (node.type !== "Component Inputs") continue;
|
|
256152
|
+
for (const port5 of node.declaredPorts) if (port5.plug === "output" && port5.kind === "signal") ports.add(port5.name);
|
|
256153
|
+
}
|
|
256154
|
+
signalInputPortsCache.set(component, ports);
|
|
256155
|
+
}
|
|
256156
|
+
return ports;
|
|
256157
|
+
}
|
|
255582
256158
|
function componentOutputInterface(component) {
|
|
255583
256159
|
const takenProps = /* @__PURE__ */ new Set();
|
|
255584
256160
|
for (const node of component.nodes) {
|
|
@@ -255979,7 +256555,7 @@ function indexPageStacks(ir, catalog2, kits) {
|
|
|
255979
256555
|
return index;
|
|
255980
256556
|
}
|
|
255981
256557
|
function tsTypeOf(portType2, kind) {
|
|
255982
|
-
if (kind === "signal") return
|
|
256558
|
+
if (kind === "signal") return INPUT_PULSE_TS_TYPE;
|
|
255983
256559
|
switch (portType2) {
|
|
255984
256560
|
case "boolean":
|
|
255985
256561
|
return "boolean";
|
|
@@ -267978,6 +268554,23 @@ var GENERATED_TS3 = "// @nodegx:generated (visual \u2014 provenance markers comp
|
|
|
267978
268554
|
var GENERATED_CSS2 = "/* @nodegx:generated (visual) */\n";
|
|
267979
268555
|
var PRINT_WIDTH = 100;
|
|
267980
268556
|
var SIMPLE_REF = /^[A-Za-z_$][A-Za-z0-9_$.]*(\(\))?$/;
|
|
268557
|
+
function negatedCode(cond) {
|
|
268558
|
+
if (cond.startsWith("!!")) {
|
|
268559
|
+
const rest = cond.slice(2);
|
|
268560
|
+
if (SIMPLE_REF.test(rest)) return `!${rest}`;
|
|
268561
|
+
if (rest.startsWith("(") && closingParenOf(rest) === rest.length - 1) return `!${rest}`;
|
|
268562
|
+
}
|
|
268563
|
+
if (SIMPLE_REF.test(cond) || cond.startsWith("(") && closingParenOf(cond) === cond.length - 1) return `!${cond}`;
|
|
268564
|
+
return `!(${cond})`;
|
|
268565
|
+
}
|
|
268566
|
+
function closingParenOf(code) {
|
|
268567
|
+
let depth = 0;
|
|
268568
|
+
for (let i = 0; i < code.length; i++) {
|
|
268569
|
+
if (code[i] === "(") depth++;
|
|
268570
|
+
else if (code[i] === ")" && --depth === 0) return i;
|
|
268571
|
+
}
|
|
268572
|
+
return -1;
|
|
268573
|
+
}
|
|
267981
268574
|
var FILE_OUT_OPTIONAL_FIELDS2 = /* @__PURE__ */ new Set(["upload.contentType", "upload.size", "sign.expiresAt", "sign.ttlSeconds"]);
|
|
267982
268575
|
var SESSION_LOCAL = "session";
|
|
267983
268576
|
var PAGE_PARAMS_LOCAL = "pageParams";
|
|
@@ -268223,25 +268816,27 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268223
268816
|
const mountRefusals = /* @__PURE__ */ new Map();
|
|
268224
268817
|
{
|
|
268225
268818
|
const mountGateOf = (id) => {
|
|
268819
|
+
const gates = [];
|
|
268226
268820
|
for (let at = id; at !== void 0; at = nodeById.get(at)?.parent) {
|
|
268227
268821
|
const bound = plan.bindings[at]?.["mounted"];
|
|
268228
268822
|
const literal2 = nodeById.get(at)?.parameters.find((p) => p.name === "mounted")?.value;
|
|
268229
268823
|
const never2 = bound !== void 0 && bound.kind === "computed" && (bound.expr.kind === "undefined" || bound.expr.kind === "literal" && !bound.expr.value) || bound === void 0 && literal2?.kind === "literal" && literal2.value === false;
|
|
268230
268824
|
const whose = at === id ? "it is" : `an element above it (${at}) is`;
|
|
268231
|
-
if (never2) return `${whose} never mounted, so its Did Mount never fires
|
|
268232
|
-
if (bound !== void 0 && !(bound.kind === "computed" && bound.expr.kind === "literal")) {
|
|
268233
|
-
return `${
|
|
268825
|
+
if (never2) return { never: `${whose} never mounted, so its Did Mount never fires` };
|
|
268826
|
+
if (bound !== void 0 && !(bound.kind === "computed" && bound.expr.kind === "literal") && at === plan.collapsedGroupId) {
|
|
268827
|
+
return { never: `${UNCARRIED_PAGE_GROUP} ${at}, is mounted by a value the export does not carry (it draws the page), so when it mounts is not known here` };
|
|
268234
268828
|
}
|
|
268829
|
+
if (bound !== void 0 && !(bound.kind === "computed" && bound.expr.kind === "literal")) gates.push(at);
|
|
268235
268830
|
if (at === plan.rootId) break;
|
|
268236
268831
|
}
|
|
268237
|
-
return
|
|
268832
|
+
return { gates };
|
|
268238
268833
|
};
|
|
268239
268834
|
const consider = (id) => {
|
|
268240
268835
|
const actions = plan.handlers[id]?.["didMount"];
|
|
268241
268836
|
if (actions === void 0) return;
|
|
268242
|
-
const
|
|
268243
|
-
if (
|
|
268244
|
-
else mountRefusals.set(id,
|
|
268837
|
+
const gate = mountGateOf(id);
|
|
268838
|
+
if ("gates" in gate) mountChains.push({ nodeId: id, actions, gates: gate.gates });
|
|
268839
|
+
else mountRefusals.set(id, gate.never);
|
|
268245
268840
|
};
|
|
268246
268841
|
const visit = (id) => {
|
|
268247
268842
|
for (const child of plan.childrenOf[id] ?? []) visit(child);
|
|
@@ -268333,6 +268928,7 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268333
268928
|
if (action.kind === "store-set") {
|
|
268334
268929
|
usedVariableNames.add(action.variableName);
|
|
268335
268930
|
collectExprUse(action.expr);
|
|
268931
|
+
(action.then ?? []).forEach(collectActionUse);
|
|
268336
268932
|
}
|
|
268337
268933
|
if (action.kind === "globalstore-set") {
|
|
268338
268934
|
usedStoreNames.add(action.storeName);
|
|
@@ -268492,6 +269088,8 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268492
269088
|
...Object.values(plan.handlers).flatMap((byPort) => Object.values(byPort).flat()),
|
|
268493
269089
|
...Object.values(plan.changeHandlers).flat(),
|
|
268494
269090
|
...plan.receivers.flatMap((r) => r.actions),
|
|
269091
|
+
// P18-S126-INSIGNAL. A signal input's chain earns imports and references exactly as a handler's does.
|
|
269092
|
+
...plan.inputPulses.flatMap((p) => p.actions),
|
|
268495
269093
|
// A reactive Condition's branch earns imports, `navigate` and state references exactly as a
|
|
268496
269094
|
// handler's does — it just runs from an effect instead of an event (LOGIC-TARGET §10).
|
|
268497
269095
|
...plan.branchEffects.map((e) => e.action),
|
|
@@ -268557,7 +269155,10 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268557
269155
|
// two outcome arms and its minted Failure arm, an Insert's minted Failure arm, a Remove's Done.
|
|
268558
269156
|
a8.kind === "collection-clear" ? [a8, ...deepActions(a8.then), ...deepActions(a8.unchangedThen), ...deepActions(a8.minted?.failThen ?? [])] : a8.kind === "collection-add" ? [a8, ...deepActions(a8.minted?.failThen ?? [])] : a8.kind === "log" || a8.kind === "popup-show" || a8.kind === "popup-close" || a8.kind === "jsfun-run" || // EXP-011 §86. The pulse loop and each signal's arm.
|
|
268559
269157
|
a8.kind === "jsfun-pulses" || a8.kind === "jsfun-pulse" || a8.kind === "date-now-read" || a8.kind === "collection-remove" || a8.kind === "array-new" || // EXP-011 §61. A Reset's one chain.
|
|
268560
|
-
a8.kind === "stack-reset" ? [a8, ...deepActions(a8.then)] :
|
|
269158
|
+
a8.kind === "stack-reset" ? [a8, ...deepActions(a8.then)] : (
|
|
269159
|
+
// P18-S127-SETDONE. A Set Variable's Done chain.
|
|
269160
|
+
a8.kind === "store-set" && a8.then !== void 0 ? [a8, ...deepActions(a8.then)] : [a8]
|
|
269161
|
+
)
|
|
268561
269162
|
)
|
|
268562
269163
|
)
|
|
268563
269164
|
)
|
|
@@ -268719,6 +269320,7 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268719
269320
|
if (stateVar) referencedStateNames.add(stateVar.name);
|
|
268720
269321
|
}
|
|
268721
269322
|
}
|
|
269323
|
+
for (const pulse of plan.instancePulses) if (pulse.delivered !== void 0) referencedStateNames.add(pulse.delivered.name);
|
|
268722
269324
|
const referencedStateVars = plan.stateVars.filter((v) => referencedStateNames.has(v.name));
|
|
268723
269325
|
const liveRecordRows = plan.recordReads.flatMap((read2) => {
|
|
268724
269326
|
const row = read2.live && !read2.binds ? referencedStateVars.find((v) => v.originNodeId === read2.nodeId && v.origin === "record-row") : void 0;
|
|
@@ -268928,6 +269530,9 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
268928
269530
|
case "payload":
|
|
268929
269531
|
return true;
|
|
268930
269532
|
// payload keys are optional-typed
|
|
269533
|
+
case "row-relay":
|
|
269534
|
+
return true;
|
|
269535
|
+
// EXP-011 §100: the row's field, which a row's item may lack. Must agree with plan.ts maybeUndefinedExpr.
|
|
268931
269536
|
case "undefined":
|
|
268932
269537
|
return true;
|
|
268933
269538
|
// a Component Object property's boot value (COMPONENT-OBJECT-TARGET §3)
|
|
@@ -269120,12 +269725,15 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
269120
269725
|
return `${local}.${node === "hash" ? "digest" : "value"}`;
|
|
269121
269726
|
};
|
|
269122
269727
|
let announcedCode;
|
|
269728
|
+
let rowRelayCode;
|
|
269123
269729
|
const exprCode = (expr, mode) => {
|
|
269124
269730
|
switch (expr.kind) {
|
|
269125
269731
|
case "prop":
|
|
269126
269732
|
return propName(expr.name);
|
|
269127
269733
|
case "input-text":
|
|
269128
269734
|
return announcedCode?.inputId === expr.inputId ? announcedCode.code : "event.target.value";
|
|
269735
|
+
case "row-relay":
|
|
269736
|
+
return rowRelayCode?.repeaterId === expr.repeaterId ? rowRelayCode.read(expr.input) : "undefined";
|
|
269129
269737
|
/**
|
|
269130
269738
|
* A url parameter (EXP-011 Tier 2.5) — the query first, the path segment behind it.
|
|
269131
269739
|
*
|
|
@@ -269596,18 +270204,23 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
269596
270204
|
// on read, so the runtime's no-object Failure is unreachable — and `store.set()` is
|
|
269597
270205
|
// synchronous, so its Done chain is following statements too.
|
|
269598
270206
|
(a8.kind === "popup-show" || a8.kind === "collection-remove" || a8.kind === "log" || a8.kind === "object-set" || a8.kind === "delay" && a8.verb === "restart") && a8.then.length > 0 ? [{ ...a8, then: [] }, ...expandActions(a8.then)] : (
|
|
269599
|
-
//
|
|
269600
|
-
//
|
|
269601
|
-
a8.kind === "
|
|
269602
|
-
//
|
|
269603
|
-
//
|
|
269604
|
-
|
|
269605
|
-
|
|
269606
|
-
//
|
|
269607
|
-
//
|
|
269608
|
-
|
|
269609
|
-
|
|
269610
|
-
|
|
270207
|
+
// P18-S127-SETDONE. A Set Variable with a literal Name cannot fail and its write is synchronous: Done, then
|
|
270208
|
+
// Completed, are following statements (`withSetDone` put them in `then` in that order).
|
|
270209
|
+
a8.kind === "store-set" && a8.then !== void 0 ? [{ ...a8, then: void 0 }, ...expandActions(a8.then)] : (
|
|
270210
|
+
// EXP-011 §60. The self variant cannot miss and `set` is synchronous: Done then Completed are following
|
|
270211
|
+
// statements, in the order reportOutcome sends them. The parent form prints its own two arms.
|
|
270212
|
+
a8.kind === "component-object-set" && !a8.parent && (a8.then.length > 0 || a8.completedThen.length > 0) ? [{ ...a8, then: [], completedThen: [] }, ...expandActions(a8.then), ...expandActions(a8.completedThen)] : (
|
|
270213
|
+
// A jsfun-run is only its done-chain (EXP-003 §4 A2h): output reads inline the call
|
|
270214
|
+
// at their sinks, so the run itself needs no statement — unless it materializes its
|
|
270215
|
+
// output record (CONTROLLED-STATE §4f), which is one setter statement before the chain.
|
|
270216
|
+
// P18-S129-FNSUCCESS: or a Run of a Function a ticked input also runs, which records what it read first.
|
|
270217
|
+
a8.kind === "jsfun-run" ? a8.materialize !== void 0 || runIsPulsed(a8) || a8.ticked !== true && tickedRunIds.has(a8.nodeId) ? [{ ...a8, then: [] }, ...expandActions(a8.then)] : expandActions(a8.then) : (
|
|
270218
|
+
// P18-S116-QUERYFETCH. A Fetch into a query the export does not keep (nothing it renders reads the rows) asks
|
|
270219
|
+
// for nothing anyone sees; the query's own row in the report says why it is not kept.
|
|
270220
|
+
a8.kind === "query-fetch" && !plan.queries.some((q) => q.nodeId === a8.nodeId) ? [] : (
|
|
270221
|
+
// EXP-011 §87. A literal Condition's one side is following statements.
|
|
270222
|
+
a8.kind === "branch" && a8.flat === true ? expandActions(a8.whenTrue) : [a8]
|
|
270223
|
+
)
|
|
269611
270224
|
)
|
|
269612
270225
|
)
|
|
269613
270226
|
)
|
|
@@ -269644,7 +270257,9 @@ function emitComponent(plan, project2, ir, catalog2, kitBindings = /* @__PURE__
|
|
|
269644
270257
|
};
|
|
269645
270258
|
const inAction = (a8) => {
|
|
269646
270259
|
switch (a8.kind) {
|
|
270260
|
+
// P18-S127-SETDONE. The value, or anything in the Done chain.
|
|
269647
270261
|
case "store-set":
|
|
270262
|
+
return reads(a8.expr) || (a8.then ?? []).some(inAction);
|
|
269648
270263
|
case "globalstore-set":
|
|
269649
270264
|
return reads(a8.expr);
|
|
269650
270265
|
// EXP-011 §47. Any of the patch's values, or anything in the chain after it.
|
|
@@ -270101,6 +270716,7 @@ ${pad(indent)}${stateSetterOf(action.materialize)}({ ...${names2.run}.outputs })
|
|
|
270101
270716
|
if (action.materialize !== void 0) {
|
|
270102
270717
|
return `${record2}${stateSetterOf(action.materialize)}(${def.fnName}(${jsArgsObject(def, "handler")}))`;
|
|
270103
270718
|
}
|
|
270719
|
+
if (record2 !== "" && action.then.length === 0) return `tickedArrival(${tickedRecordArgs(action.nodeId)})`;
|
|
270104
270720
|
return expandActions(action.then).map(actionCode).join("; ");
|
|
270105
270721
|
}
|
|
270106
270722
|
/**
|
|
@@ -270129,6 +270745,7 @@ ${pad(indent)}${stateSetterOf(action.materialize)}({ ...${names2.run}.outputs })
|
|
|
270129
270745
|
// The functional updates are §3.3's closure-staleness-immune forms.
|
|
270130
270746
|
case "state-set": {
|
|
270131
270747
|
const setter = stateSetterOf(action.name);
|
|
270748
|
+
if (action.field !== void 0 && fieldWriteIsRun(action)) return fieldWriteCode(action, action.field, indent);
|
|
270132
270749
|
if (action.op === "toggle") return `${setter}((v) => !v)`;
|
|
270133
270750
|
if (action.op === "inc") return `${setter}((v) => v + 1)`;
|
|
270134
270751
|
if (action.op === "dec") return `${setter}((v) => v - 1)`;
|
|
@@ -270538,6 +271155,9 @@ ${at}`) : "new Date()";
|
|
|
270538
271155
|
// EXP-011 §63. One call on the handle; Done is the node's listener, passed once to the hook.
|
|
270539
271156
|
case "drag-snap":
|
|
270540
271157
|
return `${action.local}.snapTo(${tsLiteral(action.axis)})`;
|
|
271158
|
+
// P18-S128-FIELDSET. A call on the field's element; nothing when it is not on the page.
|
|
271159
|
+
case "field-focus":
|
|
271160
|
+
return `${fieldRefs.get(action.inputId)}.current?.${action.focus ? "focus" : "blur"}()`;
|
|
270541
271161
|
/**
|
|
270542
271162
|
* EXP-011 §61. A push, printed as the runtime's own call (`navigate.ts` `navigate()`): the target, the
|
|
270543
271163
|
* parameters by the target's own prop identifiers, the back callback (the results row written FIRST, then
|
|
@@ -270806,7 +271426,64 @@ ${pad(indent)}}`;
|
|
|
270806
271426
|
a8.kind === "id-new" && (idNewIsBlock(a8) || a8.materialize !== void 0 || a8.then.length > 0 || chainReadsIdLocal(a8.then, a8.nodeId)) || // EXP-011 §59. Always the block form — the Failure arm always raises.
|
|
270807
271427
|
a8.kind === "crypto-call" || // EXP-011 §60. The parent write is the block form (the miss arm raises); the own write is one expression.
|
|
270808
271428
|
a8.kind === "component-object-set" && a8.parent || // EXP-011 §61. A push is a multi-line call; a pop is always the block form; a Reset with a chain is a run of statements.
|
|
270809
|
-
a8.kind === "stack-push" || a8.kind === "stack-pop" || a8.kind === "stack-reset" && a8.then.length > 0
|
|
271429
|
+
a8.kind === "stack-push" || a8.kind === "stack-pop" || a8.kind === "stack-reset" && a8.then.length > 0 || // P18-S128-FIELDSET. A field's Set is an `if`; a Clear with a chain to run after it is a run of statements.
|
|
271430
|
+
a8.kind === "state-set" && fieldWriteIsRun(a8);
|
|
271431
|
+
}
|
|
271432
|
+
const fieldChainOf = (inputId) => [
|
|
271433
|
+
...plan.changeHandlers[inputId] ?? [],
|
|
271434
|
+
...(plan.handlers[inputId] ?? {})["onChange"] ?? []
|
|
271435
|
+
];
|
|
271436
|
+
const printingFieldChains = /* @__PURE__ */ new Set();
|
|
271437
|
+
function fieldWriteIsRun(a8) {
|
|
271438
|
+
if (a8.kind !== "state-set" || a8.field === void 0) return false;
|
|
271439
|
+
if (a8.field.port === "set") return true;
|
|
271440
|
+
return fieldClearedLocals.has(a8.field.inputId) || !printingFieldChains.has(a8.field.inputId) && fieldChainOf(a8.field.inputId).length > 0;
|
|
271441
|
+
}
|
|
271442
|
+
function fieldWriteCode(action, field2, indent) {
|
|
271443
|
+
const setter = stateSetterOf(action.name);
|
|
271444
|
+
const at = pad(indent);
|
|
271445
|
+
const cleared = fieldClearedLocals.get(field2.inputId);
|
|
271446
|
+
const chainLines = (code2, depth) => {
|
|
271447
|
+
if (printingFieldChains.has(field2.inputId)) return [];
|
|
271448
|
+
const chain = fieldChainOf(field2.inputId);
|
|
271449
|
+
if (chain.length === 0) return [];
|
|
271450
|
+
const outer = announcedCode;
|
|
271451
|
+
announcedCode = { inputId: field2.inputId, code: code2 };
|
|
271452
|
+
printingFieldChains.add(field2.inputId);
|
|
271453
|
+
const lines = effectBody(chain, depth);
|
|
271454
|
+
printingFieldChains.delete(field2.inputId);
|
|
271455
|
+
announcedCode = outer;
|
|
271456
|
+
return lines;
|
|
271457
|
+
};
|
|
271458
|
+
if (field2.port === "clear") {
|
|
271459
|
+
const lines = [`${at}${setter}('');`, ...cleared !== void 0 ? [`${at}${cleared}.current = true;`] : [], ...chainLines("''", indent)];
|
|
271460
|
+
return lines.join("\n").slice(at.length);
|
|
271461
|
+
}
|
|
271462
|
+
const ref = fieldRefs.get(field2.inputId);
|
|
271463
|
+
const inner = pad(indent + 2);
|
|
271464
|
+
const start3 = tsLiteral(field2.start ?? "");
|
|
271465
|
+
const body2 = [];
|
|
271466
|
+
let code;
|
|
271467
|
+
if (action.expr === void 0 || action.expr.kind === "literal") {
|
|
271468
|
+
const literal2 = tsLiteral(action.expr?.kind === "literal" ? String(action.expr.value) : field2.start ?? "");
|
|
271469
|
+
code = cleared !== void 0 ? `(${cleared}.current ? '' : ${literal2})` : literal2;
|
|
271470
|
+
} else {
|
|
271471
|
+
const blockLocal = (base) => {
|
|
271472
|
+
let candidate = base;
|
|
271473
|
+
for (let n = 2; reserved.has(candidate); n++) candidate = `${base}${n}`;
|
|
271474
|
+
return candidate;
|
|
271475
|
+
};
|
|
271476
|
+
const value = blockLocal(`${action.name}Value`);
|
|
271477
|
+
code = blockLocal(`${action.name}Text`);
|
|
271478
|
+
const asText2 = `String(${value} ?? '')`;
|
|
271479
|
+
const coerced = field2.start === void 0 || field2.start === "" ? asText2 : `${value} === undefined ? ${start3} : ${asText2}`;
|
|
271480
|
+
body2.push(
|
|
271481
|
+
`${inner}const ${value} = ${exprCode(action.expr, "handler")};`,
|
|
271482
|
+
`${inner}const ${code} = ${cleared !== void 0 ? `${cleared}.current ? '' : ${coerced}` : coerced};`
|
|
271483
|
+
);
|
|
271484
|
+
}
|
|
271485
|
+
body2.push(`${inner}${setter}(${code});`, ...chainLines(code, indent + 2));
|
|
271486
|
+
return [`if (${ref}.current !== document.activeElement) {`, ...body2, `${at}}`].join("\n");
|
|
270810
271487
|
}
|
|
270811
271488
|
function actionTakesNoTerminator(a8) {
|
|
270812
271489
|
if ("detached" in a8 && a8.detached === true) return false;
|
|
@@ -270822,7 +271499,8 @@ ${pad(indent)}}`;
|
|
|
270822
271499
|
a8.kind === "id-new" && idNewIsBlock(a8) || // EXP-011 §59. The block form ends in `}` too.
|
|
270823
271500
|
a8.kind === "crypto-call" || // EXP-011 §60. The parent write's `if` ends in `}` and takes no terminator.
|
|
270824
271501
|
a8.kind === "component-object-set" && a8.parent || // EXP-011 §61. The pop's block form ends in `}`; the push ends in `)` and takes its `;`, as does a Reset.
|
|
270825
|
-
a8.kind === "stack-pop"
|
|
271502
|
+
a8.kind === "stack-pop" || // P18-S128-FIELDSET. Each line of a field write ends its own statement.
|
|
271503
|
+
a8.kind === "state-set" && fieldWriteIsRun(a8)
|
|
270826
271504
|
);
|
|
270827
271505
|
}
|
|
270828
271506
|
function actionsAwait(actions) {
|
|
@@ -270834,7 +271512,10 @@ ${pad(indent)}}`;
|
|
|
270834
271512
|
}
|
|
270835
271513
|
function blockBody(expanded, indent) {
|
|
270836
271514
|
return expanded.map(
|
|
270837
|
-
(a8) =>
|
|
271515
|
+
(a8) => (
|
|
271516
|
+
// P18-S128-FIELDSET. A field write's lines take the column (`actionTakesNoTerminator` holds for it).
|
|
271517
|
+
actionTakesNoTerminator(a8) ? `${pad(indent)}${actionCode(a8, indent)}` : `${pad(indent)}${actionCode(a8, "detached" in a8 && a8.detached === true || a8.kind === "date-now-read" || a8.kind === "id-new" || a8.kind === "crypto-call" || a8.kind === "branch" || a8.kind === "array-new" || a8.kind === "component-object-set" || a8.kind === "stack-push" || a8.kind === "stack-reset" || a8.kind === "jsfun-run" ? indent : 0)};`
|
|
271518
|
+
)
|
|
270838
271519
|
);
|
|
270839
271520
|
}
|
|
270840
271521
|
function effectBody(actions, indent) {
|
|
@@ -270854,10 +271535,37 @@ ${pad(indent)}}`;
|
|
|
270854
271535
|
}
|
|
270855
271536
|
return statements.length === 1 ? `${head} => ${statements[0]}` : `${head} => { ${statements.join("; ")}; }`;
|
|
270856
271537
|
};
|
|
271538
|
+
const prependStatements = (arrow, statements, indent) => {
|
|
271539
|
+
if (statements.length === 0) return arrow;
|
|
271540
|
+
const head = arrow.slice(0, arrow.indexOf("=> ") + 3);
|
|
271541
|
+
const body2 = arrow.slice(head.length);
|
|
271542
|
+
if (body2.startsWith("{\n")) return `${head}{
|
|
271543
|
+
${statements.map((st) => `${pad(indent + 2)}${st};`).join("\n")}
|
|
271544
|
+
${body2.slice(2)}`;
|
|
271545
|
+
if (body2.startsWith("{ ")) return `${head}{ ${statements.join("; ")}; ${body2.slice(2)}`;
|
|
271546
|
+
return `${head}{ ${statements.join("; ")}; ${body2}; }`;
|
|
271547
|
+
};
|
|
270857
271548
|
const delayRefs = /* @__PURE__ */ new Map();
|
|
270858
271549
|
for (const a8 of deepActions(allActions)) {
|
|
270859
271550
|
if (a8.kind === "delay") delayRefs.set(a8.nodeId, a8.ref);
|
|
270860
271551
|
}
|
|
271552
|
+
const fieldRefs = /* @__PURE__ */ new Map();
|
|
271553
|
+
const fieldSetSources = /* @__PURE__ */ new Map();
|
|
271554
|
+
const fieldCleared = /* @__PURE__ */ new Set();
|
|
271555
|
+
for (const a8 of deepActions(allActions)) {
|
|
271556
|
+
const inputId = a8.kind === "field-focus" ? a8.inputId : a8.kind === "state-set" && a8.field?.port === "set" ? a8.field.inputId : void 0;
|
|
271557
|
+
if (inputId !== void 0 && !fieldRefs.has(inputId)) {
|
|
271558
|
+
const named = lowerFirst(pascalCase2(nodeById.get(inputId)?.authoredLabel ?? "")) || "field";
|
|
271559
|
+
fieldRefs.set(inputId, dedupeLocal(/^[A-Za-z_$]/.test(named) ? `${named}Field` : "field"));
|
|
271560
|
+
}
|
|
271561
|
+
if (a8.kind === "state-set" && a8.field?.port === "set" && a8.expr !== void 0) fieldSetSources.set(a8.field.inputId, a8.expr);
|
|
271562
|
+
if (a8.kind === "state-set" && a8.field?.port === "clear") fieldCleared.add(a8.field.inputId);
|
|
271563
|
+
}
|
|
271564
|
+
const fieldClearedLocals = /* @__PURE__ */ new Map();
|
|
271565
|
+
for (const inputId of fieldCleared) {
|
|
271566
|
+
if (!fieldSetSources.has(inputId)) continue;
|
|
271567
|
+
fieldClearedLocals.set(inputId, dedupeLocal(`${fieldRefs.get(inputId).replace(/Field$/, "")}Cleared`));
|
|
271568
|
+
}
|
|
270861
271569
|
const relRoot = "..";
|
|
270862
271570
|
const externalImports = [];
|
|
270863
271571
|
const coreHooks = [];
|
|
@@ -270880,7 +271588,9 @@ ${pad(indent)}}`;
|
|
|
270880
271588
|
if (referencedStateVars.length > 0 && !reactImports.includes("useState")) reactImports.push("useState");
|
|
270881
271589
|
if ((plan.syncEffects.length > 0 || plan.pushEffects.length > 0 || plan.branchEffects.length > 0 || // EXP-011 §39. A Value Changed is an effect; a Delay owes the unmount cleanup effect.
|
|
270882
271590
|
plan.valueChangedEffects.length > 0 || // P18-S119-TICKEDRUN. A ticked input's run is an effect.
|
|
270883
|
-
plan.tickedRuns.length > 0 || //
|
|
271591
|
+
plan.tickedRuns.length > 0 || // P18-S126-INSIGNAL. A signal input's chain is an effect on its count.
|
|
271592
|
+
plan.inputPulses.length > 0 || // P18-S127-SIBLINGVALUE. A delivered count follows the count sent from an effect.
|
|
271593
|
+
plan.instancePulses.some((p) => p.delivered !== void 0) || // EXP-011 §43, P18-S113-IDBINDREAD. A Record with Fetch unwired is a bind keyed on its Id.
|
|
270884
271594
|
recordBinds.length > 0 || // P18-S111-RECORDLIVE. A live Record's row subscribes to the client's copy of its record.
|
|
270885
271595
|
liveRecordRows.length > 0 || // EXP-011 §67. A Variable's authored Value is a mount effect.
|
|
270886
271596
|
plan.variableSeeds.length > 0 || // EXP-011 §71. An Object's own authored property values are a mount effect.
|
|
@@ -270891,7 +271601,8 @@ ${pad(indent)}}`;
|
|
|
270891
271601
|
reactImports.push("useEffect");
|
|
270892
271602
|
}
|
|
270893
271603
|
if (radioNameLocals.size > 0) reactImports.push("useId");
|
|
270894
|
-
if (delayRefs.size > 0 || plan.valueChangedEffects.length > 0 || usedKeptLocals.size > 0 || pulsedRunIds.size > 0 || plan.tickedRuns.length > 0) reactImports.push("useRef");
|
|
271604
|
+
if (delayRefs.size > 0 || plan.valueChangedEffects.length > 0 || usedKeptLocals.size > 0 || pulsedRunIds.size > 0 || plan.tickedRuns.length > 0 || plan.instancePulses.length > 0 || fieldRefs.size > 0) reactImports.push("useRef");
|
|
271605
|
+
if ([...fieldClearedLocals.keys()].some((id) => fieldSetSources.get(id)?.kind !== "literal") && !reactImports.includes("useEffect")) reactImports.push("useEffect");
|
|
270895
271606
|
if (plan.task !== void 0 || plan.repeaterItems.length > 0 || plan.fieldMounts.length > 0 || mountChains.length > 0) {
|
|
270896
271607
|
if (!reactImports.includes("useEffect")) reactImports.push("useEffect");
|
|
270897
271608
|
if (!reactImports.includes("useRef")) reactImports.push("useRef");
|
|
@@ -271414,6 +272125,16 @@ ${pad(indent)}}`;
|
|
|
271414
272125
|
defer(node.id, 'the "didMount" signal', mountRefusals.get(node.id));
|
|
271415
272126
|
continue;
|
|
271416
272127
|
}
|
|
272128
|
+
if (port5 === "onEnter" && plan.roleOf[node.id] === "input") {
|
|
272129
|
+
const expanded = expandActions(actions);
|
|
272130
|
+
attrs.push(
|
|
272131
|
+
`onKeyDown={${actionsAwait(expanded) ? "async " : ""}(event) => {
|
|
272132
|
+
${pad(attrIndent + 2)}if (event.key !== 'Enter') return;
|
|
272133
|
+
${blockBody(expanded, attrIndent + 2).join("\n")}
|
|
272134
|
+
${pad(attrIndent)}}}`
|
|
272135
|
+
);
|
|
272136
|
+
continue;
|
|
272137
|
+
}
|
|
271417
272138
|
const eventAttr = roleEvents[port5] ?? EVENT_ATTRS[port5];
|
|
271418
272139
|
if (!eventAttr) {
|
|
271419
272140
|
notes.push(`${plan.path}: signal ${node.id}.${port5} has no DOM event equivalent \u2014 dropped, reported`);
|
|
@@ -271459,6 +272180,19 @@ ${pad(indent)}}`;
|
|
|
271459
272180
|
if (boolTypedSource(source)) return base;
|
|
271460
272181
|
return SIMPLE_REF.test(base) ? `!!${base}` : `!!(${base})`;
|
|
271461
272182
|
};
|
|
272183
|
+
const mountedCondOf = (id, source) => {
|
|
272184
|
+
const cond = truthinessCode(source);
|
|
272185
|
+
if (cond === null) return null;
|
|
272186
|
+
const node = nodeById.get(id);
|
|
272187
|
+
const authored = node?.parameters.find((p) => p.name === "mounted")?.value;
|
|
272188
|
+
if (authored?.kind === "literal" && authored.value === false) return cond;
|
|
272189
|
+
const isExpressionAnswer = source.kind === "computed" && source.expr.kind === "jsfun-out" && source.expr.viaState === void 0 && nodeById.get(source.expr.nodeId)?.type === "Expression";
|
|
272190
|
+
const undefinedAtFirst = source.kind === "prop" || source.kind === "store" || source.kind === "store-key" || source.kind === "computed" && !isExpressionAnswer && maybeUndefined(source.expr);
|
|
272191
|
+
if (!undefinedAtFirst) return cond;
|
|
272192
|
+
const base = rawBindingExpr(source);
|
|
272193
|
+
if (base === null || !SIMPLE_REF.test(base) || base.endsWith("()")) return cond;
|
|
272194
|
+
return boolTypedSource(source) ? `(${base} === undefined || ${base})` : `(${base} === undefined || !!${base})`;
|
|
272195
|
+
};
|
|
271462
272196
|
const negatedVisibleCode = (source) => {
|
|
271463
272197
|
const base = bindingExpr(source, "truthy");
|
|
271464
272198
|
if (base === null) return null;
|
|
@@ -271565,6 +272299,7 @@ ${pad(indent)}}`;
|
|
|
271565
272299
|
return typeof def === "string" || typeof def === "number" || typeof def === "boolean" ? def : void 0;
|
|
271566
272300
|
};
|
|
271567
272301
|
const fontIconSetsNoted = /* @__PURE__ */ new Set();
|
|
272302
|
+
let rootMountedCond = null;
|
|
271568
272303
|
const render2 = (id, indent, radioCtx) => {
|
|
271569
272304
|
const node = nodeById.get(id);
|
|
271570
272305
|
const mountedBound = plan.bindings[id]?.["mounted"];
|
|
@@ -271579,8 +272314,12 @@ ${pad(indent)}}`;
|
|
|
271579
272314
|
}
|
|
271580
272315
|
const staticallyTrue = mountedBound.kind === "computed" && mountedBound.expr.kind === "literal";
|
|
271581
272316
|
if (!staticallyTrue) {
|
|
271582
|
-
const cond =
|
|
272317
|
+
const cond = mountedCondOf(id, mountedBound);
|
|
271583
272318
|
if (cond !== null) {
|
|
272319
|
+
if (id === plan.rootId) {
|
|
272320
|
+
rootMountedCond = cond;
|
|
272321
|
+
return renderCore(id, indent, radioCtx);
|
|
272322
|
+
}
|
|
271584
272323
|
const inner = renderCore(id, indent + 2, radioCtx);
|
|
271585
272324
|
return [`${pad(indent)}{${cond} && (`, ...inner, `${pad(indent)})}`];
|
|
271586
272325
|
}
|
|
@@ -271651,6 +272390,7 @@ ${pad(indent)}}`;
|
|
|
271651
272390
|
if (controlVar !== void 0) {
|
|
271652
272391
|
attrs.push(`${role === "checkbox" ? "checked" : "value"}={${controlVar.name}}`);
|
|
271653
272392
|
}
|
|
272393
|
+
if (role === "input" && fieldRefs.has(id)) attrs.push(`ref={${fieldRefs.get(id)}}`);
|
|
271654
272394
|
if (role === "input" || isControl) attrs.push(...changeAttrs(node, indent + 2));
|
|
271655
272395
|
attrs.push(...handlerAttrs(node, indent + 2));
|
|
271656
272396
|
if (role === "stack") return renderStack(node, attrs, indent);
|
|
@@ -271796,10 +272536,14 @@ ${pad(indent)}}`;
|
|
|
271796
272536
|
}
|
|
271797
272537
|
return out;
|
|
271798
272538
|
};
|
|
271799
|
-
const rowSignalAttrs = (attrIndent) => {
|
|
272539
|
+
const rowSignalAttrs = (attrIndent, itemRef, entries2) => {
|
|
271800
272540
|
const handlers = plan.handlers[node.id];
|
|
271801
272541
|
if (handlers === void 0) return [];
|
|
271802
272542
|
const propByPort = new Map((templatePlan?.outputProps ?? []).map((o) => [o.port, o.prop]));
|
|
272543
|
+
const relayWrites = plan.rowRelays.filter((r) => r.repeaterId === node.id).map((r) => {
|
|
272544
|
+
const entry = entries2.find((e) => e.input === r.input);
|
|
272545
|
+
return `${stateSetterOf(r.stateName)}(${entry === void 0 ? "undefined" : memberExpr(itemRef, entry.field)})`;
|
|
272546
|
+
});
|
|
271803
272547
|
const out = [];
|
|
271804
272548
|
for (const [port5, actions] of Object.entries(handlers)) {
|
|
271805
272549
|
if (!port5.startsWith(ITEM_OUTPUT_SIGNAL)) {
|
|
@@ -271822,7 +272566,16 @@ ${pad(indent)}}`;
|
|
|
271822
272566
|
defer(node.id, `the relayed row signal "${templatePort}"`, `has no callback prop on ${templateLegacy}`);
|
|
271823
272567
|
continue;
|
|
271824
272568
|
}
|
|
271825
|
-
|
|
272569
|
+
const outer = rowRelayCode;
|
|
272570
|
+
rowRelayCode = { repeaterId: node.id, read: (input) => {
|
|
272571
|
+
const entry = entries2.find((e) => e.input === input);
|
|
272572
|
+
return entry === void 0 ? "undefined" : memberExpr(itemRef, entry.field);
|
|
272573
|
+
} };
|
|
272574
|
+
try {
|
|
272575
|
+
out.push(`${prop}={${prependStatements(handlerArrow(actions, "()", attrIndent), relayWrites, attrIndent)}}`);
|
|
272576
|
+
} finally {
|
|
272577
|
+
rowRelayCode = outer;
|
|
272578
|
+
}
|
|
271826
272579
|
}
|
|
271827
272580
|
return out;
|
|
271828
272581
|
};
|
|
@@ -271851,7 +272604,7 @@ ${pad(indent)}}`;
|
|
|
271851
272604
|
);
|
|
271852
272605
|
}
|
|
271853
272606
|
const keyAttr = staticData.keyField ? `key={${itemLocal}.${staticData.keyField}}` : `key={${indexLocal}}`;
|
|
271854
|
-
const attrs2 = [keyAttr, ...rowAttrs(keptStatic, itemLocal), ...rowSignalAttrs(indent + 2)];
|
|
272607
|
+
const attrs2 = [keyAttr, ...rowAttrs(keptStatic, itemLocal), ...rowSignalAttrs(indent + 2, itemLocal, keptStatic)];
|
|
271855
272608
|
const lines2 = element(target.symbol, attrs2, null, indent + 2, false);
|
|
271856
272609
|
const params = staticData.keyField ? itemLocal : `${itemLocal}, ${indexLocal}`;
|
|
271857
272610
|
return [`${pad(indent)}{${staticData.constName}.map((${params}) => (`, ...lines2, `${pad(indent)}))}`];
|
|
@@ -271870,7 +272623,7 @@ ${pad(indent)}}`;
|
|
|
271870
272623
|
);
|
|
271871
272624
|
}
|
|
271872
272625
|
}
|
|
271873
|
-
const attrs2 = [`key={${indexLocal}}`, ...rowAttrs(keptExpr, itemLocal), ...rowSignalAttrs(indent + 2)];
|
|
272626
|
+
const attrs2 = [`key={${indexLocal}}`, ...rowAttrs(keptExpr, itemLocal), ...rowSignalAttrs(indent + 2, itemLocal, keptExpr)];
|
|
271874
272627
|
const lines2 = element(target.symbol, attrs2, null, indent + 2, false);
|
|
271875
272628
|
const srcCode = exprCode(itemsExpr, "render");
|
|
271876
272629
|
const source = itemsExpr.kind === "minted-array-get" || itemsExpr.kind === "record-filter" ? srcCode : SIMPLE_REF.test(srcCode) ? `(${srcCode} ?? [])` : `((${srcCode}) ?? [])`;
|
|
@@ -271890,13 +272643,13 @@ ${pad(indent)}}`;
|
|
|
271890
272643
|
);
|
|
271891
272644
|
}
|
|
271892
272645
|
if (collection) {
|
|
271893
|
-
const attrs2 = [`key={${indexLocal}}`, ...rowAttrs(kept, itemLocal), ...rowSignalAttrs(indent + 2)];
|
|
272646
|
+
const attrs2 = [`key={${indexLocal}}`, ...rowAttrs(kept, itemLocal), ...rowSignalAttrs(indent + 2, itemLocal, kept)];
|
|
271894
272647
|
const lines2 = element(target.symbol, attrs2, null, indent + 2, false);
|
|
271895
272648
|
const local = collectionLocals.get(collection.name);
|
|
271896
272649
|
return [`${pad(indent)}{${local}.map((${itemLocal}, ${indexLocal}) => (`, ...lines2, `${pad(indent)}))}`];
|
|
271897
272650
|
}
|
|
271898
272651
|
const item = query2.itemName;
|
|
271899
|
-
const attrs = [`key={${item}.id}`, ...rowAttrs(kept, item), ...rowSignalAttrs(indent + 2)];
|
|
272652
|
+
const attrs = [`key={${item}.id}`, ...rowAttrs(kept, item), ...rowSignalAttrs(indent + 2, item, kept)];
|
|
271900
272653
|
const lines = element(target.symbol, attrs, null, indent + 2, false);
|
|
271901
272654
|
return [`${pad(indent)}{${query2.stateName}.map((${item}) => (`, ...lines, `${pad(indent)}))}`];
|
|
271902
272655
|
};
|
|
@@ -272106,6 +272859,14 @@ ${pad(indent)}}`;
|
|
|
272106
272859
|
for (const [toProperty, source] of Object.entries(plan.bindings[node.id] ?? {})) {
|
|
272107
272860
|
claimBinding(node.id, toProperty);
|
|
272108
272861
|
if (toProperty === "mounted") continue;
|
|
272862
|
+
const pulse = plan.instancePulses.find((p) => p.nodeId === node.id && p.port === toProperty);
|
|
272863
|
+
if (pulse !== void 0) {
|
|
272864
|
+
const pulseAttr = targetPropName(node.type, toProperty);
|
|
272865
|
+
if (pulseAttr !== null) {
|
|
272866
|
+
attrs.push(`${pulseAttr}={{ sent: ${pulse.delivered?.name ?? pulse.stateName}, taken: ${pulse.takenLocal} }}`);
|
|
272867
|
+
continue;
|
|
272868
|
+
}
|
|
272869
|
+
}
|
|
272109
272870
|
if (toProperty === "visible") {
|
|
272110
272871
|
notes.push(
|
|
272111
272872
|
`${plan.path}: wire into instance ${node.id}.visible dropped \u2014 an instance has no element class to toggle in this slice`
|
|
@@ -272550,7 +273311,28 @@ ${pad(indent)}}`;
|
|
|
272550
273311
|
body.push(` const ${mintedLocals.get(nodeId)} = useCollection${rowType === "any" ? "" : `<${rowType}>`}(${mintStateName(nodeId)} ?? ${NO_ARRAY2});`);
|
|
272551
273312
|
}
|
|
272552
273313
|
for (const [, ref] of delayRefs) body.push(` const ${ref} = useRef<DelayHandle | null>(null);`);
|
|
273314
|
+
for (const [, ref] of fieldRefs) body.push(` const ${ref} = useRef<HTMLInputElement>(null);`);
|
|
273315
|
+
for (const [inputId, local] of fieldClearedLocals) {
|
|
273316
|
+
const source = fieldSetSources.get(inputId);
|
|
273317
|
+
body.push(` const ${local} = useRef(false);`);
|
|
273318
|
+
if (source.kind === "literal") continue;
|
|
273319
|
+
const read2 = exprCode(source, "handler");
|
|
273320
|
+
const store2 = /^([A-Za-z_$][A-Za-z0-9_$]*)\.get\(\)$/.exec(read2);
|
|
273321
|
+
body.push(
|
|
273322
|
+
...store2 !== null ? [` useEffect(() => ${store2[1]}.subscribe(() => { ${local}.current = false; }), []);`] : [" useEffect(() => {", ` ${local}.current = false;`, ` }, [${exprCode(source, "render")}]);`]
|
|
273323
|
+
);
|
|
273324
|
+
}
|
|
272553
273325
|
for (const effect of plan.valueChangedEffects) body.push(` const ${effect.lastLocal} = useRef<unknown>(undefined);`);
|
|
273326
|
+
for (const pulse of plan.instancePulses) body.push(` const ${pulse.takenLocal} = useRef(0);`);
|
|
273327
|
+
for (const pulse of plan.instancePulses) {
|
|
273328
|
+
if (pulse.delivered === void 0) continue;
|
|
273329
|
+
body.push(
|
|
273330
|
+
` // "${pulse.port}" is handed on one commit after it is sent: the values lifted with it arrive first, as the running app delivers them.`,
|
|
273331
|
+
" useEffect(() => {",
|
|
273332
|
+
` ${pulse.delivered.setterName}(${pulse.stateName});`,
|
|
273333
|
+
` }, [${pulse.stateName}]);`
|
|
273334
|
+
);
|
|
273335
|
+
}
|
|
272554
273336
|
for (const id of pulsedRunIds) {
|
|
272555
273337
|
const def = jsFunByNode[id];
|
|
272556
273338
|
body.push(` const ${pulseNamesOf(def).node} = useRef(functionNode<NonNullable<Parameters<typeof ${def.fnName}>[1]>>([${def.signals.map(tsLiteral).join(", ")}])).current;`);
|
|
@@ -272825,16 +273607,19 @@ ${pad(indent)}}`;
|
|
|
272825
273607
|
announcedCode = { inputId: mount2.inputId, code };
|
|
272826
273608
|
const chainLines = effectBody(chain, 4);
|
|
272827
273609
|
announcedCode = void 0;
|
|
273610
|
+
const gate = (mount2.gates ?? []).map((id) => mountedCondOf(id, plan.bindings[id]["mounted"])).filter((c) => c !== null);
|
|
273611
|
+
const when = gate.length === 0 ? null : gate.length === 1 ? gate[0] : gate.map((c) => SIMPLE_REF.test(c) ? c : `(${c})`).join(" && ");
|
|
272828
273612
|
body.push(
|
|
272829
273613
|
` // ${mount2.comment}`,
|
|
272830
273614
|
` const ${once} = useRef(false);`,
|
|
272831
273615
|
" useEffect(() => {",
|
|
272832
273616
|
` if (${once}.current) return;`,
|
|
273617
|
+
...when !== null ? [` if (${negatedCode(when)}) return;`] : [],
|
|
272833
273618
|
` ${once}.current = true;`,
|
|
272834
273619
|
...lines,
|
|
272835
273620
|
...chainLines,
|
|
272836
|
-
" // eslint-disable-next-line react-hooks/exhaustive-deps -- once, as the field mounts",
|
|
272837
|
-
|
|
273621
|
+
when !== null ? " // eslint-disable-next-line react-hooks/exhaustive-deps -- once, the first time the field mounts" : " // eslint-disable-next-line react-hooks/exhaustive-deps -- once, as the field mounts",
|
|
273622
|
+
` }, [${gate.join(", ")}]);`,
|
|
272838
273623
|
""
|
|
272839
273624
|
);
|
|
272840
273625
|
}
|
|
@@ -272917,6 +273702,18 @@ ${pad(indent)}}`;
|
|
|
272917
273702
|
""
|
|
272918
273703
|
);
|
|
272919
273704
|
}
|
|
273705
|
+
for (const pulse of plan.inputPulses) {
|
|
273706
|
+
const pair = exprCode({ kind: "prop", name: pulse.port }, "render");
|
|
273707
|
+
body.push(
|
|
273708
|
+
` // "${commentSafe(pulse.port)}", a signal the parent sends in: its chain runs once for each pulse sent, never twice.`,
|
|
273709
|
+
" useEffect(() => {",
|
|
273710
|
+
` if (${pair} === undefined || ${pair}.taken.current === ${pair}.sent) return;`,
|
|
273711
|
+
` ${pair}.taken.current = ${pair}.sent;`,
|
|
273712
|
+
...effectBody(pulse.actions, 4),
|
|
273713
|
+
` }, [${pair}?.sent]);`,
|
|
273714
|
+
""
|
|
273715
|
+
);
|
|
273716
|
+
}
|
|
272920
273717
|
for (const ticked3 of plan.tickedRuns) {
|
|
272921
273718
|
const deps = [...new Set(ticked3.watch.flatMap(effectDeps))].join(", ");
|
|
272922
273719
|
body.push(
|
|
@@ -272931,6 +273728,26 @@ ${pad(indent)}}`;
|
|
|
272931
273728
|
for (const mount2 of mountChains) {
|
|
272932
273729
|
const node = nodeById.get(mount2.nodeId);
|
|
272933
273730
|
const mounted = dedupeLocal("mounted");
|
|
273731
|
+
const gate = mount2.gates.map((id) => mountedCondOf(id, plan.bindings[id]["mounted"])).filter((c) => c !== null);
|
|
273732
|
+
if (gate.length > 0) {
|
|
273733
|
+
const when = gate.length === 1 ? gate[0] : gate.map((c) => SIMPLE_REF.test(c) ? c : `(${c})`).join(" && ");
|
|
273734
|
+
body.push(
|
|
273735
|
+
` // ${node.authoredLabel ?? node.type} \u2014 Did Mount: each time it mounts, as the running app sends it: when ${commentSafe(when)}.`,
|
|
273736
|
+
` const ${mounted} = useRef(false);`,
|
|
273737
|
+
" useEffect(() => {",
|
|
273738
|
+
` if (${negatedCode(when)}) {`,
|
|
273739
|
+
` ${mounted}.current = false;`,
|
|
273740
|
+
" return;",
|
|
273741
|
+
" }",
|
|
273742
|
+
` if (${mounted}.current) return;`,
|
|
273743
|
+
` ${mounted}.current = true;`,
|
|
273744
|
+
...effectBody(mount2.actions, 4),
|
|
273745
|
+
" // eslint-disable-next-line react-hooks/exhaustive-deps -- once per mount, like the element's own Did Mount",
|
|
273746
|
+
` }, [${gate.join(", ")}]);`,
|
|
273747
|
+
""
|
|
273748
|
+
);
|
|
273749
|
+
continue;
|
|
273750
|
+
}
|
|
272934
273751
|
body.push(
|
|
272935
273752
|
` // ${node.authoredLabel ?? node.type} \u2014 Did Mount: once, when it mounts, as the running app sends it.`,
|
|
272936
273753
|
` const ${mounted} = useRef(false);`,
|
|
@@ -272947,7 +273764,9 @@ ${pad(indent)}}`;
|
|
|
272947
273764
|
switch (a8.kind) {
|
|
272948
273765
|
case "emit":
|
|
272949
273766
|
return a8.payload.map((p) => p.expr);
|
|
273767
|
+
// P18-S127-SETDONE. The value, then the Done chain.
|
|
272950
273768
|
case "store-set":
|
|
273769
|
+
return [a8.expr, ...(a8.then ?? []).flatMap(actionExprsOf)];
|
|
272951
273770
|
case "globalstore-set":
|
|
272952
273771
|
return [a8.expr];
|
|
272953
273772
|
// EXP-011 §47. The patch's values, then the chain.
|
|
@@ -273061,6 +273880,9 @@ ${pad(indent)}}`;
|
|
|
273061
273880
|
// EXP-011 §63. The same.
|
|
273062
273881
|
case "drag-snap":
|
|
273063
273882
|
return [];
|
|
273883
|
+
// P18-S128-FIELDSET. The same.
|
|
273884
|
+
case "field-focus":
|
|
273885
|
+
return [];
|
|
273064
273886
|
// EXP-011 Tier 2.5. The link, the wired Open In New Tab, and both chains — `usesPayload`
|
|
273065
273887
|
// walks this list, so a link built from a received event's payload is found here or the
|
|
273066
273888
|
// emitted callback takes no argument and its body reads one.
|
|
@@ -273122,7 +273944,18 @@ ${pad(indent)}}`;
|
|
|
273122
273944
|
] : jsxLines;
|
|
273123
273945
|
if (jsxLines.length === 1 && jsxLines[0] === " null") body.push(...preReturnComment, " return null;", "}");
|
|
273124
273946
|
else if (removedRoot !== null) body.push(...preReturnComment, ` // ${removedRoot[1]}`, " return null;", "}");
|
|
273125
|
-
else
|
|
273947
|
+
else if (rootMountedCond !== null) {
|
|
273948
|
+
const guard = negatedCode(rootMountedCond);
|
|
273949
|
+
body.push(
|
|
273950
|
+
...preReturnComment,
|
|
273951
|
+
` // Its root is mounted when ${commentSafe(rootMountedCond)}; while it is not, the running app draws nothing for this instance.`,
|
|
273952
|
+
` if (${guard}) return null;`,
|
|
273953
|
+
" return (",
|
|
273954
|
+
...providedJsx,
|
|
273955
|
+
" );",
|
|
273956
|
+
"}"
|
|
273957
|
+
);
|
|
273958
|
+
} else body.push(...preReturnComment, " return (", ...providedJsx, " );", "}");
|
|
273126
273959
|
if (usesRowField) {
|
|
273127
273960
|
body.push(
|
|
273128
273961
|
"",
|
|
@@ -278721,6 +279554,10 @@ var ProjectStore = class {
|
|
|
278721
279554
|
readStyles() {
|
|
278722
279555
|
return this.viaFormat(() => this.format.readStyles());
|
|
278723
279556
|
}
|
|
279557
|
+
/** P112-OWN020-OFFSYSTEM — element styles (nameless variants) are written here; see `tools/elementStyles.ts`. */
|
|
279558
|
+
writeStyles(styles) {
|
|
279559
|
+
this.viaFormat(() => this.format.writeStyles(styles));
|
|
279560
|
+
}
|
|
278724
279561
|
get componentsDir() {
|
|
278725
279562
|
return path11.join(this.projectDir, "components");
|
|
278726
279563
|
}
|
|
@@ -279719,6 +280556,83 @@ function honestySection(h) {
|
|
|
279719
280556
|
].filter(Boolean).join("\n");
|
|
279720
280557
|
}
|
|
279721
280558
|
|
|
280559
|
+
// src/tools/unknownArguments.ts
|
|
280560
|
+
function declaredKeys(schema) {
|
|
280561
|
+
const def = schema?._def;
|
|
280562
|
+
if (!def || def.typeName !== "ZodObject") return void 0;
|
|
280563
|
+
if (def.unknownKeys !== "strip" && def.unknownKeys !== "strict") return void 0;
|
|
280564
|
+
if (def.catchall && def.catchall._def?.typeName !== "ZodNever") return void 0;
|
|
280565
|
+
return Object.keys(schema.shape);
|
|
280566
|
+
}
|
|
280567
|
+
var fold = (key2) => key2.toLowerCase().replace(/[_\-\s]/g, "");
|
|
280568
|
+
function distance2(a8, b) {
|
|
280569
|
+
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
280570
|
+
for (let i = 1; i <= a8.length; i++) {
|
|
280571
|
+
let prev = row[0];
|
|
280572
|
+
row[0] = i;
|
|
280573
|
+
for (let j = 1; j <= b.length; j++) {
|
|
280574
|
+
const cur = row[j];
|
|
280575
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a8[i - 1] === b[j - 1] ? 0 : 1));
|
|
280576
|
+
prev = cur;
|
|
280577
|
+
}
|
|
280578
|
+
}
|
|
280579
|
+
return row[b.length];
|
|
280580
|
+
}
|
|
280581
|
+
var ARGUMENT_SYNONYMS = {
|
|
280582
|
+
description: ["summary", "intent", "request"],
|
|
280583
|
+
desc: ["summary", "intent"],
|
|
280584
|
+
title: ["name"],
|
|
280585
|
+
folder: ["directory", "path"],
|
|
280586
|
+
dir: ["directory", "path"]
|
|
280587
|
+
};
|
|
280588
|
+
function closestArgument(key2, declared) {
|
|
280589
|
+
const same = declared.find((d) => fold(d) === fold(key2));
|
|
280590
|
+
if (same) return same;
|
|
280591
|
+
const synonym = (ARGUMENT_SYNONYMS[fold(key2)] ?? []).find((s) => declared.includes(s));
|
|
280592
|
+
if (synonym) return synonym;
|
|
280593
|
+
let best;
|
|
280594
|
+
let bestDistance = 3;
|
|
280595
|
+
for (const d of declared) {
|
|
280596
|
+
const n = distance2(fold(key2), fold(d));
|
|
280597
|
+
if (n < bestDistance) {
|
|
280598
|
+
best = d;
|
|
280599
|
+
bestDistance = n;
|
|
280600
|
+
}
|
|
280601
|
+
}
|
|
280602
|
+
return best;
|
|
280603
|
+
}
|
|
280604
|
+
function unknownArgumentKeys(schema, args) {
|
|
280605
|
+
const declared = declaredKeys(schema);
|
|
280606
|
+
if (!declared || !args || typeof args !== "object" || Array.isArray(args)) return [];
|
|
280607
|
+
return Object.keys(args).filter((key2) => !declared.includes(key2));
|
|
280608
|
+
}
|
|
280609
|
+
function unknownArgumentsRefusal(tool, schema, unknown3) {
|
|
280610
|
+
const declared = declaredKeys(schema) ?? [];
|
|
280611
|
+
const named = unknown3.map((key2) => {
|
|
280612
|
+
const meant = closestArgument(key2, declared);
|
|
280613
|
+
return meant ? `"${key2}" (did you mean "${meant}"?)` : `"${key2}"`;
|
|
280614
|
+
});
|
|
280615
|
+
return {
|
|
280616
|
+
error: {
|
|
280617
|
+
code: "invalid-argument",
|
|
280618
|
+
message: `${tool} has no argument ${named.join(", ")}. Nothing was done: an argument a tool does not have would be dropped without a word. Its arguments are: ${declared.join(", ")}.`,
|
|
280619
|
+
details: {
|
|
280620
|
+
unknownArguments: [...unknown3],
|
|
280621
|
+
didYouMean: Object.fromEntries(unknown3.map((key2) => [key2, closestArgument(key2, declared) ?? null]))
|
|
280622
|
+
}
|
|
280623
|
+
}
|
|
280624
|
+
};
|
|
280625
|
+
}
|
|
280626
|
+
function refuseUnknownArguments(server) {
|
|
280627
|
+
const s = server;
|
|
280628
|
+
const original = s.validateToolInput.bind(server);
|
|
280629
|
+
s.validateToolInput = async (tool, args, toolName) => {
|
|
280630
|
+
const unknown3 = unknownArgumentKeys(tool.inputSchema, args);
|
|
280631
|
+
if (unknown3.length > 0) throw new Error(JSON.stringify(unknownArgumentsRefusal(toolName, tool.inputSchema, unknown3), null, 2));
|
|
280632
|
+
return original(tool, args, toolName);
|
|
280633
|
+
};
|
|
280634
|
+
}
|
|
280635
|
+
|
|
279722
280636
|
// src/tools/util.ts
|
|
279723
280637
|
function jsonResult(payload) {
|
|
279724
280638
|
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
@@ -280069,6 +280983,11 @@ function registerCallTool(server, disclosure) {
|
|
|
280069
280983
|
}
|
|
280070
280984
|
let parsed2 = void 0;
|
|
280071
280985
|
if (schema) {
|
|
280986
|
+
const unknown3 = unknownArgumentKeys(schema, args.arguments ?? {});
|
|
280987
|
+
if (unknown3.length > 0) {
|
|
280988
|
+
const { error: error2 } = unknownArgumentsRefusal(args.name, schema, unknown3);
|
|
280989
|
+
throw new ToolError(error2.code, error2.message, error2.details);
|
|
280990
|
+
}
|
|
280072
280991
|
const result2 = await safeParseAsync3(schema, args.arguments ?? {});
|
|
280073
280992
|
if (!result2.success) {
|
|
280074
280993
|
throw new ToolError(
|
|
@@ -289504,6 +290423,248 @@ function renderImagery(report3) {
|
|
|
289504
290423
|
return lines.join("\n");
|
|
289505
290424
|
}
|
|
289506
290425
|
|
|
290426
|
+
// src/tools/elementStyles.ts
|
|
290427
|
+
var STYLES_SCHEMA = "https://opennoodl.dev/schemas/styles-v2.json";
|
|
290428
|
+
var ELEMENT_STATES = ["hover", "pressed", "focused", "disabled"];
|
|
290429
|
+
var LABEL = {
|
|
290430
|
+
labelfontSize: "var(--text-sm)",
|
|
290431
|
+
labelfontWeight: "var(--font-normal)",
|
|
290432
|
+
labelcolor: "var(--foreground)",
|
|
290433
|
+
labelSpacing: "var(--space-2)"
|
|
290434
|
+
};
|
|
290435
|
+
var FIELD_BORDER = {
|
|
290436
|
+
borderStyle: "solid",
|
|
290437
|
+
borderWidth: "var(--border-1)",
|
|
290438
|
+
borderColor: "var(--border-control)"
|
|
290439
|
+
};
|
|
290440
|
+
var ELEMENT_STYLE_STARTERS = {
|
|
290441
|
+
"net.noodl.controls.button": {
|
|
290442
|
+
parameters: {
|
|
290443
|
+
backgroundColor: "var(--primary)",
|
|
290444
|
+
color: "var(--primary-foreground)",
|
|
290445
|
+
fontFamily: "var(--font-sans)",
|
|
290446
|
+
fontSize: "var(--text-sm)",
|
|
290447
|
+
fontWeight: "var(--font-medium)",
|
|
290448
|
+
borderRadius: "var(--radius-md)",
|
|
290449
|
+
paddingTop: "var(--space-2)",
|
|
290450
|
+
paddingBottom: "var(--space-2)",
|
|
290451
|
+
paddingLeft: "var(--space-4)",
|
|
290452
|
+
paddingRight: "var(--space-4)"
|
|
290453
|
+
},
|
|
290454
|
+
stateParameters: { hover: { backgroundColor: "var(--primary-hover)" }, disabled: { opacity: 0.5 } }
|
|
290455
|
+
},
|
|
290456
|
+
"net.noodl.controls.checkbox": {
|
|
290457
|
+
parameters: {
|
|
290458
|
+
width: "var(--space-4)",
|
|
290459
|
+
height: "var(--space-4)",
|
|
290460
|
+
backgroundColor: "var(--background)",
|
|
290461
|
+
...FIELD_BORDER,
|
|
290462
|
+
borderRadius: "var(--radius-sm)",
|
|
290463
|
+
...LABEL
|
|
290464
|
+
},
|
|
290465
|
+
stateParameters: { hover: { borderColor: "var(--primary)" }, disabled: { opacity: 0.5 } }
|
|
290466
|
+
},
|
|
290467
|
+
"net.noodl.controls.radiobutton": {
|
|
290468
|
+
parameters: {
|
|
290469
|
+
width: "var(--space-4)",
|
|
290470
|
+
height: "var(--space-4)",
|
|
290471
|
+
backgroundColor: "var(--background)",
|
|
290472
|
+
...FIELD_BORDER,
|
|
290473
|
+
borderRadius: "var(--radius-full)",
|
|
290474
|
+
fillColor: "var(--primary)",
|
|
290475
|
+
fillSpacing: "var(--space-0-5)",
|
|
290476
|
+
...LABEL
|
|
290477
|
+
},
|
|
290478
|
+
stateParameters: { hover: { borderColor: "var(--primary)" }, disabled: { opacity: 0.5 } }
|
|
290479
|
+
},
|
|
290480
|
+
"net.noodl.controls.textinput": {
|
|
290481
|
+
parameters: {
|
|
290482
|
+
backgroundColor: "var(--background)",
|
|
290483
|
+
color: "var(--foreground)",
|
|
290484
|
+
fontFamily: "var(--font-sans)",
|
|
290485
|
+
fontSize: "var(--text-base)",
|
|
290486
|
+
...FIELD_BORDER,
|
|
290487
|
+
borderRadius: "var(--radius-md)",
|
|
290488
|
+
paddingTop: "var(--space-2)",
|
|
290489
|
+
paddingBottom: "var(--space-2)",
|
|
290490
|
+
paddingLeft: "var(--space-3)",
|
|
290491
|
+
paddingRight: "var(--space-3)",
|
|
290492
|
+
...LABEL
|
|
290493
|
+
},
|
|
290494
|
+
stateParameters: { hover: { borderColor: "var(--border-strong)" }, disabled: { opacity: 0.5 } }
|
|
290495
|
+
},
|
|
290496
|
+
"net.noodl.controls.options": {
|
|
290497
|
+
parameters: {
|
|
290498
|
+
backgroundColor: "var(--background)",
|
|
290499
|
+
color: "var(--foreground)",
|
|
290500
|
+
fontFamily: "var(--font-sans)",
|
|
290501
|
+
fontSize: "var(--text-base)",
|
|
290502
|
+
...FIELD_BORDER,
|
|
290503
|
+
borderRadius: "var(--radius-md)",
|
|
290504
|
+
paddingTop: "var(--space-2)",
|
|
290505
|
+
paddingBottom: "var(--space-2)",
|
|
290506
|
+
paddingLeft: "var(--space-3)",
|
|
290507
|
+
paddingRight: "var(--space-3)",
|
|
290508
|
+
...LABEL
|
|
290509
|
+
},
|
|
290510
|
+
stateParameters: { hover: { borderColor: "var(--border-strong)" }, disabled: { opacity: 0.5 } }
|
|
290511
|
+
},
|
|
290512
|
+
"net.noodl.controls.range": {
|
|
290513
|
+
parameters: {
|
|
290514
|
+
thumbColor: "var(--primary)",
|
|
290515
|
+
thumbWidth: "var(--space-4)",
|
|
290516
|
+
thumbHeight: "var(--space-4)",
|
|
290517
|
+
thumbBorderRadius: "var(--radius-full)",
|
|
290518
|
+
trackColor: "var(--muted)",
|
|
290519
|
+
trackActiveColor: "var(--primary)",
|
|
290520
|
+
trackHeight: "var(--space-1)",
|
|
290521
|
+
trackBorderRadius: "var(--radius-full)"
|
|
290522
|
+
}
|
|
290523
|
+
}
|
|
290524
|
+
};
|
|
290525
|
+
var isElementStyle = (v) => !!v && typeof v.typename === "string" && (v.name === void 0 || v.name === null || v.name === "");
|
|
290526
|
+
function readElementStyles(store2) {
|
|
290527
|
+
const out = {};
|
|
290528
|
+
for (const v of store2.readStyles()?.variants ?? []) {
|
|
290529
|
+
if (!isElementStyle(v)) continue;
|
|
290530
|
+
const states = v.stateParameters ?? v.stateParamaters;
|
|
290531
|
+
out[v.typename] = {
|
|
290532
|
+
parameters: v.parameters ?? {},
|
|
290533
|
+
...states && Object.keys(states).length > 0 ? { stateParameters: states } : {}
|
|
290534
|
+
};
|
|
290535
|
+
}
|
|
290536
|
+
return out;
|
|
290537
|
+
}
|
|
290538
|
+
function stylesOrNew(store2) {
|
|
290539
|
+
return store2.readStyles() ?? { $schema: STYLES_SCHEMA };
|
|
290540
|
+
}
|
|
290541
|
+
function upsert2(styles, typename, style) {
|
|
290542
|
+
const variants = (styles.variants ?? []).filter((v) => !(isElementStyle(v) && v.typename === typename));
|
|
290543
|
+
if (style) {
|
|
290544
|
+
variants.push({
|
|
290545
|
+
typename,
|
|
290546
|
+
parameters: style.parameters,
|
|
290547
|
+
...style.stateParameters && Object.keys(style.stateParameters).length > 0 ? { stateParameters: style.stateParameters } : {}
|
|
290548
|
+
});
|
|
290549
|
+
}
|
|
290550
|
+
styles.variants = variants;
|
|
290551
|
+
}
|
|
290552
|
+
function resolveElementType(name) {
|
|
290553
|
+
const direct = getNodeTypeDetail(name);
|
|
290554
|
+
if (!("error" in direct) && !direct.deprecated) return direct.typeName;
|
|
290555
|
+
const wanted = name.trim().toLowerCase();
|
|
290556
|
+
const rows = listNodeTypes({ includeHidden: true }).filter(
|
|
290557
|
+
(r) => r.displayName.toLowerCase() === wanted || r.typeName.toLowerCase() === wanted
|
|
290558
|
+
);
|
|
290559
|
+
const live = rows.find((r) => !r.deprecated);
|
|
290560
|
+
if (live) return live.typeName;
|
|
290561
|
+
if (!("error" in direct)) return direct.replacedBy ?? direct.typeName;
|
|
290562
|
+
throw new ToolError("not-found", direct.error, direct.suggestion ? { suggestion: direct.suggestion } : void 0);
|
|
290563
|
+
}
|
|
290564
|
+
function assertPorts(typename, names2, where) {
|
|
290565
|
+
const detail = getNodeTypeDetail(typename);
|
|
290566
|
+
if ("error" in detail) throw new ToolError("not-found", detail.error);
|
|
290567
|
+
const ports = new Set(detail.inputs.map((p) => p.name));
|
|
290568
|
+
const unknown3 = names2.filter((n) => !ports.has(n));
|
|
290569
|
+
if (unknown3.length === 0) return;
|
|
290570
|
+
const lower = new Map([...ports].map((p) => [p.toLowerCase(), p]));
|
|
290571
|
+
const suggestions = Object.fromEntries(
|
|
290572
|
+
unknown3.map((u) => [u, lower.get(u.toLowerCase()) ?? [...ports].find((p) => p.toLowerCase().includes(u.toLowerCase()))])
|
|
290573
|
+
);
|
|
290574
|
+
throw new ToolError(
|
|
290575
|
+
"invalid-argument",
|
|
290576
|
+
`${detail.displayName} has no ${unknown3.map((u) => `"${u}"`).join(", ")} ${where}. get_node_type lists its ports; an element style sets the same parameters a node does.`,
|
|
290577
|
+
{ unknownParameters: unknown3, suggestions }
|
|
290578
|
+
);
|
|
290579
|
+
}
|
|
290580
|
+
function nodesOverriding(store2, typename, names2) {
|
|
290581
|
+
let nodes = 0;
|
|
290582
|
+
const overriding = [];
|
|
290583
|
+
for (const row of store2.listComponents()) {
|
|
290584
|
+
const key2 = row.path;
|
|
290585
|
+
let stored;
|
|
290586
|
+
try {
|
|
290587
|
+
stored = store2.readComponent(key2);
|
|
290588
|
+
} catch {
|
|
290589
|
+
continue;
|
|
290590
|
+
}
|
|
290591
|
+
for (const n of stored.files.nodes.nodes ?? []) {
|
|
290592
|
+
if (n.type !== typename || n.variant) continue;
|
|
290593
|
+
nodes += 1;
|
|
290594
|
+
const own2 = names2.filter((p) => n.parameters && Object.prototype.hasOwnProperty.call(n.parameters, p));
|
|
290595
|
+
if (own2.length > 0) overriding.push({ component: key2, nodeId: n.id, parameters: own2 });
|
|
290596
|
+
}
|
|
290597
|
+
}
|
|
290598
|
+
return { nodes, overriding };
|
|
290599
|
+
}
|
|
290600
|
+
function registerElementStyleTools(server, binding) {
|
|
290601
|
+
server.registerTool(
|
|
290602
|
+
"set_element_style",
|
|
290603
|
+
{
|
|
290604
|
+
title: "Set an element style",
|
|
290605
|
+
description: `Set the look of every node of one type at once \u2014 every Radio Button, Checkbox, Text Input, Dropdown, Slider, Button or Text \u2014 so whichever control you place matches the page. starter: true writes the on-system look for a control type; parameters are the node's own (get_node_type lists them), as "var(--token)" values. A node that sets a parameter itself keeps its own value. Read them with get_style_vocabulary (elementStyles). Set merges into the current style; remove: true clears it.`,
|
|
290606
|
+
inputSchema: {
|
|
290607
|
+
type: external_exports.string().describe('Node type or its display name, e.g. "Radio Button" or "net.noodl.controls.radiobutton"'),
|
|
290608
|
+
starter: external_exports.boolean().optional().describe("true = start from the on-system look for this control type (parameters still apply on top)"),
|
|
290609
|
+
parameters: external_exports.record(external_exports.unknown()).optional().describe('e.g. { "width": "var(--space-4)", "labelFontSize": "var(--text-sm)" }'),
|
|
290610
|
+
states: external_exports.record(external_exports.record(external_exports.unknown())).optional().describe(`Per visual state (${ELEMENT_STATES.join(", ")}), the parameters that change`),
|
|
290611
|
+
unset: external_exports.array(external_exports.string()).optional().describe("Parameter names to drop from the style"),
|
|
290612
|
+
remove: external_exports.boolean().optional().describe("true = delete this element style")
|
|
290613
|
+
}
|
|
290614
|
+
},
|
|
290615
|
+
guarded(
|
|
290616
|
+
(args) => {
|
|
290617
|
+
const store2 = binding.require();
|
|
290618
|
+
const typename = resolveElementType(args.type);
|
|
290619
|
+
const styles = stylesOrNew(store2);
|
|
290620
|
+
const current2 = readElementStyles(store2)[typename];
|
|
290621
|
+
if (args.remove) {
|
|
290622
|
+
upsert2(styles, typename, null);
|
|
290623
|
+
store2.writeStyles(styles);
|
|
290624
|
+
return jsonResult({ ok: true, type: typename, removed: !!current2 });
|
|
290625
|
+
}
|
|
290626
|
+
if (!args.starter && !args.parameters && !args.states && !args.unset) {
|
|
290627
|
+
throw new ToolError("invalid-argument", "Pass starter: true, parameters, states, unset or remove: true.");
|
|
290628
|
+
}
|
|
290629
|
+
const starter = args.starter ? ELEMENT_STYLE_STARTERS[typename] : void 0;
|
|
290630
|
+
if (args.starter && !starter) {
|
|
290631
|
+
throw new ToolError(
|
|
290632
|
+
"invalid-argument",
|
|
290633
|
+
`There is no starter look for ${typename}. Starters exist for: ${Object.keys(ELEMENT_STYLE_STARTERS).join(", ")}. Pass parameters instead.`
|
|
290634
|
+
);
|
|
290635
|
+
}
|
|
290636
|
+
const parameters = { ...current2?.parameters ?? {}, ...starter?.parameters ?? {}, ...args.parameters ?? {} };
|
|
290637
|
+
for (const u of args.unset ?? []) delete parameters[u];
|
|
290638
|
+
const stateParameters = { ...current2?.stateParameters ?? {} };
|
|
290639
|
+
for (const [state, params] of Object.entries(starter?.stateParameters ?? {})) {
|
|
290640
|
+
stateParameters[state] = { ...stateParameters[state] ?? {}, ...params };
|
|
290641
|
+
}
|
|
290642
|
+
for (const [state, params] of Object.entries(args.states ?? {})) {
|
|
290643
|
+
if (!ELEMENT_STATES.includes(state)) {
|
|
290644
|
+
throw new ToolError("invalid-argument", `Unknown visual state "${state}". Use one of: ${ELEMENT_STATES.join(", ")}.`);
|
|
290645
|
+
}
|
|
290646
|
+
stateParameters[state] = { ...stateParameters[state] ?? {}, ...params };
|
|
290647
|
+
}
|
|
290648
|
+
assertPorts(typename, Object.keys(parameters), "parameter");
|
|
290649
|
+
for (const [state, params] of Object.entries(stateParameters)) assertPorts(typename, Object.keys(params), `parameter (state "${state}")`);
|
|
290650
|
+
upsert2(styles, typename, { parameters, stateParameters });
|
|
290651
|
+
store2.writeStyles(styles);
|
|
290652
|
+
const reach2 = nodesOverriding(store2, typename, Object.keys(parameters));
|
|
290653
|
+
return jsonResult({
|
|
290654
|
+
ok: true,
|
|
290655
|
+
type: typename,
|
|
290656
|
+
style: { parameters, ...Object.keys(stateParameters).length ? { states: stateParameters } : {} },
|
|
290657
|
+
nodesOfType: reach2.nodes,
|
|
290658
|
+
...reach2.overriding.length ? {
|
|
290659
|
+
overriding: reach2.overriding.slice(0, 20),
|
|
290660
|
+
note: `${reach2.overriding.length} of ${reach2.nodes} set some of these parameters themselves and keep their own value. Remove them from those nodes (update_node unset_parameters) to let the element style through.`
|
|
290661
|
+
} : {}
|
|
290662
|
+
});
|
|
290663
|
+
}
|
|
290664
|
+
)
|
|
290665
|
+
);
|
|
290666
|
+
}
|
|
290667
|
+
|
|
289507
290668
|
// src/presetFontFiles.ts
|
|
289508
290669
|
var fs28 = __toESM(require("fs"));
|
|
289509
290670
|
var path28 = __toESM(require("path"));
|
|
@@ -289644,10 +290805,13 @@ function registerStyleReadTools(server, binding) {
|
|
|
289644
290805
|
const vocab = buildStyleVocabulary(store2.designTokenMetaSource(), readProjectLooks(store2));
|
|
289645
290806
|
const icons = readIconSets(store2.projectDir);
|
|
289646
290807
|
const imagery = readImagery(store2.projectDir);
|
|
290808
|
+
const elementStyles = readElementStyles(store2);
|
|
289647
290809
|
if (args.detail === "prompt") {
|
|
289648
290810
|
return jsonResult({
|
|
289649
290811
|
vocabulary: `${renderStyleVocabulary(vocab)}
|
|
289650
290812
|
|
|
290813
|
+
${renderElementStyles(elementStyles)}
|
|
290814
|
+
|
|
289651
290815
|
${renderIconSets(icons)}
|
|
289652
290816
|
|
|
289653
290817
|
${renderImagery(imagery)}
|
|
@@ -289655,7 +290819,7 @@ ${renderImagery(imagery)}
|
|
|
289655
290819
|
LAYOUT: ${LAYOUT_RULES_SHORT}`
|
|
289656
290820
|
});
|
|
289657
290821
|
}
|
|
289658
|
-
return jsonResult({ ...vocab, icons, imagery, layout: LAYOUT_RULES });
|
|
290822
|
+
return jsonResult({ ...vocab, elementStyles, elementStylesNote: ELEMENT_STYLES_NOTE, icons, imagery, layout: LAYOUT_RULES });
|
|
289659
290823
|
})
|
|
289660
290824
|
);
|
|
289661
290825
|
}
|
|
@@ -289675,6 +290839,20 @@ function readProjectLooks(store2) {
|
|
|
289675
290839
|
};
|
|
289676
290840
|
});
|
|
289677
290841
|
}
|
|
290842
|
+
var ELEMENT_STYLES_NOTE = `Element styles dress every node of a type that does not set the parameter itself, so leave a control's look to its element style and set only what is particular to one node. Change a type's look for the whole app with set_element_style (find_tools({ group: "theme" })).`;
|
|
290843
|
+
function renderElementStyles(styles) {
|
|
290844
|
+
const types = Object.keys(styles);
|
|
290845
|
+
if (types.length === 0) {
|
|
290846
|
+
return "ELEMENT STYLES: none \u2014 every control you place draws the runtime's own look until its type has one: call set_element_style for each control type you use.";
|
|
290847
|
+
}
|
|
290848
|
+
const lines = types.map((t) => {
|
|
290849
|
+
const params = Object.entries(styles[t].parameters).map(([k, v]) => `${k} ${String(v)}`).join(", ");
|
|
290850
|
+
return ` ${t.replace("net.noodl.controls.", "")}: ${params}`;
|
|
290851
|
+
});
|
|
290852
|
+
return `ELEMENT STYLES (every node of the type wears these unless it sets the parameter itself):
|
|
290853
|
+
${lines.join("\n")}
|
|
290854
|
+
${ELEMENT_STYLES_NOTE}`;
|
|
290855
|
+
}
|
|
289678
290856
|
function registerStyleWriteTools(server, binding) {
|
|
289679
290857
|
server.registerTool(
|
|
289680
290858
|
"set_project_tokens",
|
|
@@ -293295,87 +294473,10 @@ function registerLessonTools(server) {
|
|
|
293295
294473
|
);
|
|
293296
294474
|
}
|
|
293297
294475
|
|
|
293298
|
-
// src/tools/unknownArguments.ts
|
|
293299
|
-
function declaredKeys(schema) {
|
|
293300
|
-
const def = schema?._def;
|
|
293301
|
-
if (!def || def.typeName !== "ZodObject") return void 0;
|
|
293302
|
-
if (def.unknownKeys !== "strip" && def.unknownKeys !== "strict") return void 0;
|
|
293303
|
-
if (def.catchall && def.catchall._def?.typeName !== "ZodNever") return void 0;
|
|
293304
|
-
return Object.keys(schema.shape);
|
|
293305
|
-
}
|
|
293306
|
-
var fold = (key2) => key2.toLowerCase().replace(/[_\-\s]/g, "");
|
|
293307
|
-
function distance2(a8, b) {
|
|
293308
|
-
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
293309
|
-
for (let i = 1; i <= a8.length; i++) {
|
|
293310
|
-
let prev = row[0];
|
|
293311
|
-
row[0] = i;
|
|
293312
|
-
for (let j = 1; j <= b.length; j++) {
|
|
293313
|
-
const cur = row[j];
|
|
293314
|
-
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a8[i - 1] === b[j - 1] ? 0 : 1));
|
|
293315
|
-
prev = cur;
|
|
293316
|
-
}
|
|
293317
|
-
}
|
|
293318
|
-
return row[b.length];
|
|
293319
|
-
}
|
|
293320
|
-
var ARGUMENT_SYNONYMS = {
|
|
293321
|
-
description: ["summary", "intent", "request"],
|
|
293322
|
-
desc: ["summary", "intent"],
|
|
293323
|
-
title: ["name"],
|
|
293324
|
-
folder: ["directory", "path"],
|
|
293325
|
-
dir: ["directory", "path"]
|
|
293326
|
-
};
|
|
293327
|
-
function closestArgument(key2, declared) {
|
|
293328
|
-
const same = declared.find((d) => fold(d) === fold(key2));
|
|
293329
|
-
if (same) return same;
|
|
293330
|
-
const synonym = (ARGUMENT_SYNONYMS[fold(key2)] ?? []).find((s) => declared.includes(s));
|
|
293331
|
-
if (synonym) return synonym;
|
|
293332
|
-
let best;
|
|
293333
|
-
let bestDistance = 3;
|
|
293334
|
-
for (const d of declared) {
|
|
293335
|
-
const n = distance2(fold(key2), fold(d));
|
|
293336
|
-
if (n < bestDistance) {
|
|
293337
|
-
best = d;
|
|
293338
|
-
bestDistance = n;
|
|
293339
|
-
}
|
|
293340
|
-
}
|
|
293341
|
-
return best;
|
|
293342
|
-
}
|
|
293343
|
-
function unknownArgumentKeys(schema, args) {
|
|
293344
|
-
const declared = declaredKeys(schema);
|
|
293345
|
-
if (!declared || !args || typeof args !== "object" || Array.isArray(args)) return [];
|
|
293346
|
-
return Object.keys(args).filter((key2) => !declared.includes(key2));
|
|
293347
|
-
}
|
|
293348
|
-
function unknownArgumentsRefusal(tool, schema, unknown3) {
|
|
293349
|
-
const declared = declaredKeys(schema) ?? [];
|
|
293350
|
-
const named = unknown3.map((key2) => {
|
|
293351
|
-
const meant = closestArgument(key2, declared);
|
|
293352
|
-
return meant ? `"${key2}" (did you mean "${meant}"?)` : `"${key2}"`;
|
|
293353
|
-
});
|
|
293354
|
-
return {
|
|
293355
|
-
error: {
|
|
293356
|
-
code: "invalid-argument",
|
|
293357
|
-
message: `${tool} has no argument ${named.join(", ")}. Nothing was done: an argument a tool does not have would be dropped without a word. Its arguments are: ${declared.join(", ")}.`,
|
|
293358
|
-
details: {
|
|
293359
|
-
unknownArguments: [...unknown3],
|
|
293360
|
-
didYouMean: Object.fromEntries(unknown3.map((key2) => [key2, closestArgument(key2, declared) ?? null]))
|
|
293361
|
-
}
|
|
293362
|
-
}
|
|
293363
|
-
};
|
|
293364
|
-
}
|
|
293365
|
-
function refuseUnknownArguments(server) {
|
|
293366
|
-
const s = server;
|
|
293367
|
-
const original = s.validateToolInput.bind(server);
|
|
293368
|
-
s.validateToolInput = async (tool, args, toolName) => {
|
|
293369
|
-
const unknown3 = unknownArgumentKeys(tool.inputSchema, args);
|
|
293370
|
-
if (unknown3.length > 0) throw new Error(JSON.stringify(unknownArgumentsRefusal(toolName, tool.inputSchema, unknown3), null, 2));
|
|
293371
|
-
return original(tool, args, toolName);
|
|
293372
|
-
};
|
|
293373
|
-
}
|
|
293374
|
-
|
|
293375
294476
|
// src/server.ts
|
|
293376
294477
|
var PKG_VERSION3 = (
|
|
293377
294478
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
293378
|
-
true ? "0.4.
|
|
294479
|
+
true ? "0.4.3" : null.version
|
|
293379
294480
|
);
|
|
293380
294481
|
function createServer2(options) {
|
|
293381
294482
|
setClientProfile(options.profile);
|
|
@@ -293426,6 +294527,7 @@ function createServer2(options) {
|
|
|
293426
294527
|
registerAuthorTools(rec, binding, planRegistry, exampleBudget, renderLedger, disclosure);
|
|
293427
294528
|
registerPlanTools(rec, binding, planRegistry, exampleBudget, renderLedger, disclosure);
|
|
293428
294529
|
registerStyleWriteTools(rec, binding);
|
|
294530
|
+
registerElementStyleTools(rec, binding);
|
|
293429
294531
|
registerDocsWriteTools(rec, binding);
|
|
293430
294532
|
registerScriptWriteTools(rec, binding);
|
|
293431
294533
|
registerBackendWriteTools(rec, binding);
|