@vizij/node-graph-authoring 0.0.5 → 0.1.1

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.
@@ -6,8 +6,9 @@ var import_node_fs = require("fs");
6
6
  var import_node_path = require("path");
7
7
 
8
8
  // src/graphBuilder.ts
9
- var import_utils2 = require("@vizij/utils");
10
- var import_utils3 = require("@vizij/utils");
9
+ var import_utils4 = require("@vizij/utils");
10
+ var import_utils5 = require("@vizij/utils");
11
+ var import_metadata2 = require("@vizij/node-graph-wasm/metadata");
11
12
 
12
13
  // src/state.ts
13
14
  var import_utils = require("@vizij/utils");
@@ -49,8 +50,6 @@ function bindingTargetFromInput(input) {
49
50
  valueType: "scalar"
50
51
  };
51
52
  }
52
- var DEFAULT_INPUT_RANGE = { min: -1, max: 1 };
53
- var DEFAULT_INPUT_ANCHOR = 0;
54
53
  var LEGACY_SLOT_PATTERN = /^slot_(\d+)$/i;
55
54
  var PRIMARY_SLOT_ID = "s1";
56
55
  var PRIMARY_SLOT_ALIAS = "s1";
@@ -94,128 +93,7 @@ function rewriteLegacyExpression(expression, replacements) {
94
93
  return `s${digits}`;
95
94
  });
96
95
  }
97
- function clamp(value, min, max) {
98
- return Math.min(max, Math.max(min, value));
99
- }
100
- function isFiniteNumber(value) {
101
- return typeof value === "number" && Number.isFinite(value);
102
- }
103
- function deriveOutputDefaults(target) {
104
- const { min, max } = target.range;
105
- const anchor = clamp(target.defaultValue, min, max);
106
- return {
107
- outLow: min,
108
- outAnchor: anchor,
109
- outHigh: max
110
- };
111
- }
112
- function deriveInputDefaults() {
113
- return {
114
- inLow: DEFAULT_INPUT_RANGE.min,
115
- inAnchor: DEFAULT_INPUT_ANCHOR,
116
- inHigh: DEFAULT_INPUT_RANGE.max
117
- };
118
- }
119
- function migrateLegacyRemap(legacy, target) {
120
- const inputDefaults = deriveInputDefaults();
121
- const outputDefaults = deriveOutputDefaults(target);
122
- const defaults = {
123
- inLow: inputDefaults.inLow,
124
- inAnchor: inputDefaults.inAnchor,
125
- inHigh: inputDefaults.inHigh,
126
- outLow: outputDefaults.outLow,
127
- outAnchor: outputDefaults.outAnchor,
128
- outHigh: outputDefaults.outHigh
129
- };
130
- if ("inLow" in legacy && "inHigh" in legacy && "outLow" in legacy && "outHigh" in legacy) {
131
- const inLow2 = isFiniteNumber(legacy.inLow) ? legacy.inLow : defaults.inLow;
132
- const inAnchor2 = isFiniteNumber(legacy.inAnchor) ? legacy.inAnchor : defaults.inAnchor;
133
- const inHigh2 = isFiniteNumber(legacy.inHigh) ? legacy.inHigh : defaults.inHigh;
134
- let outLow = isFiniteNumber(legacy.outLow) ? legacy.outLow : defaults.outLow;
135
- let outHigh = isFiniteNumber(legacy.outHigh) ? legacy.outHigh : defaults.outHigh;
136
- if (outLow > outHigh) {
137
- const low = outHigh;
138
- const high = outLow;
139
- outLow = low;
140
- outHigh = high;
141
- }
142
- const outAnchor2 = clamp(
143
- isFiniteNumber(legacy.outAnchor) ? legacy.outAnchor : defaults.outAnchor,
144
- outLow,
145
- outHigh
146
- );
147
- return {
148
- inLow: inLow2,
149
- inAnchor: inAnchor2,
150
- inHigh: inHigh2,
151
- outLow,
152
- outAnchor: outAnchor2,
153
- outHigh
154
- };
155
- }
156
- const legacyTyped = legacy;
157
- const inLow = isFiniteNumber(legacyTyped.inMin) ? legacyTyped.inMin : defaults.inLow;
158
- const inHigh = isFiniteNumber(legacyTyped.inMax) ? legacyTyped.inMax : defaults.inHigh;
159
- const inAnchor = (inLow + inHigh) / 2;
160
- const legacyOutMid = isFiniteNumber(legacyTyped.outMin) && isFiniteNumber(legacyTyped.outMax) ? (legacyTyped.outMin + legacyTyped.outMax) / 2 : defaults.outAnchor;
161
- const outAnchor = clamp(legacyOutMid, defaults.outLow, defaults.outHigh);
162
- return {
163
- inLow,
164
- inAnchor,
165
- inHigh,
166
- outLow: defaults.outLow,
167
- outAnchor,
168
- outHigh: defaults.outHigh
169
- };
170
- }
171
- function normalizeRemap(remap, target) {
172
- if (!remap) {
173
- return createDefaultRemap(target);
174
- }
175
- return migrateLegacyRemap(remap, target);
176
- }
177
- function cloneRemap(remap) {
178
- return (0, import_utils.cloneRemapSettings)(remap);
179
- }
180
- function sanitizeRemap(remap, target) {
181
- const normalized = normalizeRemap(remap, target);
182
- const outputDefaults = deriveOutputDefaults(target);
183
- if (!Number.isFinite(normalized.outLow)) {
184
- normalized.outLow = outputDefaults.outLow;
185
- }
186
- if (!Number.isFinite(normalized.outHigh)) {
187
- normalized.outHigh = outputDefaults.outHigh;
188
- }
189
- if (!Number.isFinite(normalized.outAnchor)) {
190
- normalized.outAnchor = outputDefaults.outAnchor;
191
- }
192
- if (normalized.outLow > normalized.outHigh) {
193
- const low = normalized.outHigh;
194
- const high = normalized.outLow;
195
- normalized.outLow = low;
196
- normalized.outHigh = high;
197
- }
198
- normalized.outAnchor = clamp(
199
- normalized.outAnchor,
200
- normalized.outLow,
201
- normalized.outHigh
202
- );
203
- return normalized;
204
- }
205
- function createDefaultRemap(target) {
206
- const inputDefaults = deriveInputDefaults();
207
- const outputDefaults = deriveOutputDefaults(target);
208
- return {
209
- inLow: inputDefaults.inLow,
210
- inAnchor: inputDefaults.inAnchor,
211
- inHigh: inputDefaults.inHigh,
212
- outLow: outputDefaults.outLow,
213
- outAnchor: outputDefaults.outAnchor,
214
- outHigh: outputDefaults.outHigh
215
- };
216
- }
217
96
  function ensurePrimarySlot(binding, target) {
218
- const normalizedBindingRemap = sanitizeRemap(binding.remap, target);
219
97
  const targetValueType = getTargetValueType(target);
220
98
  const aliasReplacements = /* @__PURE__ */ new Map();
221
99
  const sourceSlots = Array.isArray(binding.slots) && binding.slots.length > 0 ? binding.slots : [
@@ -223,7 +101,6 @@ function ensurePrimarySlot(binding, target) {
223
101
  id: PRIMARY_SLOT_ID,
224
102
  alias: PRIMARY_SLOT_ALIAS,
225
103
  inputId: binding.inputId ?? null,
226
- remap: cloneRemap(normalizedBindingRemap),
227
104
  valueType: targetValueType
228
105
  }
229
106
  ];
@@ -238,8 +115,6 @@ function ensurePrimarySlot(binding, target) {
238
115
  if (replaced && replaced !== normalizedAlias) {
239
116
  aliasReplacements.set(replaced, normalizedAlias);
240
117
  }
241
- const slotRemapSource = slot.remap ?? (index === 0 ? normalizedBindingRemap : createDefaultRemap(target));
242
- const normalizedSlotRemap = sanitizeRemap(slotRemapSource, target);
243
118
  const inputId = slot.inputId !== void 0 && slot.inputId !== null ? slot.inputId : index === 0 ? binding.inputId ?? null : null;
244
119
  const slotValueType = sanitizeSlotValueType(
245
120
  slot.valueType,
@@ -249,13 +124,11 @@ function ensurePrimarySlot(binding, target) {
249
124
  id: normalizedId,
250
125
  alias: normalizedAlias,
251
126
  inputId,
252
- remap: cloneRemap(normalizedSlotRemap),
253
127
  valueType: slotValueType
254
128
  };
255
129
  }
256
130
  );
257
131
  const primary = normalizedSlots[0];
258
- const primaryRemap = sanitizeRemap(primary.remap, target);
259
132
  const primaryInputId = primary.inputId === import_utils.SELF_BINDING_ID ? import_utils.SELF_BINDING_ID : primary.inputId ?? binding.inputId ?? null;
260
133
  const primaryAlias = primaryInputId === import_utils.SELF_BINDING_ID ? "self" : primary.alias || PRIMARY_SLOT_ALIAS;
261
134
  normalizedSlots[0] = {
@@ -263,16 +136,13 @@ function ensurePrimarySlot(binding, target) {
263
136
  id: primary.id || PRIMARY_SLOT_ID,
264
137
  alias: primaryAlias,
265
138
  inputId: primaryInputId,
266
- remap: cloneRemap(primaryRemap),
267
139
  valueType: sanitizeSlotValueType(primary.valueType, targetValueType)
268
140
  };
269
141
  normalizedSlots.slice(1).forEach((slot, index) => {
270
- const slotRemap = sanitizeRemap(slot.remap, target);
271
142
  normalizedSlots[index + 1] = {
272
143
  ...slot,
273
144
  id: slot.id || defaultSlotId(index + 1),
274
145
  alias: slot.alias || defaultSlotId(index + 1),
275
- remap: cloneRemap(slotRemap),
276
146
  valueType: sanitizeSlotValueType(slot.valueType, targetValueType)
277
147
  };
278
148
  });
@@ -287,7 +157,6 @@ function ensurePrimarySlot(binding, target) {
287
157
  const normalizedBinding = {
288
158
  ...binding,
289
159
  inputId: normalizedSlots[0].inputId ?? null,
290
- remap: cloneRemap(primaryRemap),
291
160
  slots: normalizedSlots,
292
161
  expression
293
162
  };
@@ -296,36 +165,6 @@ function ensurePrimarySlot(binding, target) {
296
165
  function ensureBindingStructure(binding, target) {
297
166
  return ensurePrimarySlot(binding, target);
298
167
  }
299
- function sanitizeLiteral(value) {
300
- if (!Number.isFinite(value)) {
301
- return 0;
302
- }
303
- if (Object.is(value, -0)) {
304
- return 0;
305
- }
306
- return value;
307
- }
308
- function formatVectorLiteral(values) {
309
- return `vec(${values.map((value) => sanitizeLiteral(value)).join(", ")})`;
310
- }
311
- function buildPiecewiseRemapExpression(alias, remap) {
312
- const sanitizedAlias = alias && alias.trim().length > 0 ? alias.trim() : PRIMARY_SLOT_ALIAS;
313
- const inputBreakpoints = [remap.inLow, remap.inAnchor, remap.inHigh];
314
- const outputBreakpoints = [remap.outLow, remap.outAnchor, remap.outHigh];
315
- return `piecewise_remap(${sanitizedAlias}, ${formatVectorLiteral(
316
- inputBreakpoints
317
- )}, ${formatVectorLiteral(outputBreakpoints)})`;
318
- }
319
- function isSelfAlias(alias) {
320
- return alias.trim().toLowerCase() === "self";
321
- }
322
- function buildDefaultSlotExpression(alias, inputId, remap) {
323
- const sanitizedAlias = alias && alias.trim().length > 0 ? alias.trim() : PRIMARY_SLOT_ALIAS;
324
- if (inputId === import_utils.SELF_BINDING_ID || isSelfAlias(sanitizedAlias)) {
325
- return sanitizedAlias;
326
- }
327
- return buildPiecewiseRemapExpression(sanitizedAlias, remap);
328
- }
329
168
  function normalizeSlotAliasForExpression(slot, index) {
330
169
  if (slot.alias && slot.alias.trim().length > 0) {
331
170
  return slot.alias.trim();
@@ -342,16 +181,7 @@ function buildAliasOnlyExpression(slots) {
342
181
  return slots.map((slot, index) => normalizeSlotAliasForExpression(slot, index)).join(" + ");
343
182
  }
344
183
  function buildCanonicalExpressionFromSlots(slots) {
345
- if (!slots.length) {
346
- return PRIMARY_SLOT_ALIAS;
347
- }
348
- return slots.map(
349
- (slot, index) => buildDefaultSlotExpression(
350
- normalizeSlotAliasForExpression(slot, index),
351
- slot.inputId ?? null,
352
- slot.remap
353
- )
354
- ).join(" + ");
184
+ return buildAliasOnlyExpression(slots);
355
185
  }
356
186
  function expressionsEquivalent(left, right) {
357
187
  return left.trim() === right.trim();
@@ -1441,9 +1271,7 @@ var DefaultExpressionVariableTable = class {
1441
1271
  targetId: options.targetId,
1442
1272
  animatableId: options.animatableId,
1443
1273
  component: options.component,
1444
- valueType: options.valueType,
1445
- remap: options.remap,
1446
- autoRemap: options.autoRemap
1274
+ valueType: options.valueType
1447
1275
  }
1448
1276
  });
1449
1277
  }
@@ -1539,9 +1367,6 @@ var RESERVED_EXPRESSION_VARIABLES = [
1539
1367
  }
1540
1368
  ];
1541
1369
 
1542
- // src/graphBuilder.ts
1543
- var import_metadata2 = require("@vizij/node-graph-wasm/metadata");
1544
-
1545
1370
  // src/ir/builder.ts
1546
1371
  var graphSequence = 0;
1547
1372
  function generateGraphId(faceId) {
@@ -1612,12 +1437,12 @@ function createIrGraphBuilder(options) {
1612
1437
  function toIrBindingSummary(summaries) {
1613
1438
  return summaries.map((summary) => ({
1614
1439
  ...summary,
1615
- remap: { ...summary.remap },
1616
1440
  issues: summary.issues ? [...summary.issues] : void 0
1617
1441
  }));
1618
1442
  }
1619
1443
 
1620
1444
  // src/ir/compiler.ts
1445
+ var import_utils2 = require("@vizij/utils");
1621
1446
  function compileIrGraph(graph, options = {}) {
1622
1447
  const preferLegacy = options.preferLegacySpec === true;
1623
1448
  const legacySpec = graph.legacy?.spec;
@@ -1702,7 +1527,7 @@ function cloneJsonLike(value) {
1702
1527
  if (value === void 0 || value === null) {
1703
1528
  return value;
1704
1529
  }
1705
- return JSON.parse(JSON.stringify(value));
1530
+ return (0, import_utils2.cloneDeepSafe)(value);
1706
1531
  }
1707
1532
  function inlineSingleUseConstants(spec) {
1708
1533
  const nodes = spec.nodes ?? [];
@@ -1741,8 +1566,9 @@ function inlineSingleUseConstants(spec) {
1741
1566
  }
1742
1567
 
1743
1568
  // src/bindingMetadata.ts
1569
+ var import_utils3 = require("@vizij/utils");
1744
1570
  function cloneOperand(entry) {
1745
- return JSON.parse(JSON.stringify(entry));
1571
+ return (0, import_utils3.cloneDeepSafe)(entry);
1746
1572
  }
1747
1573
  function describeSlot(entry) {
1748
1574
  const metadata = entry.metadata;
@@ -1784,17 +1610,17 @@ function describeReference(node, variables) {
1784
1610
  function stringifyExpression(node) {
1785
1611
  switch (node.type) {
1786
1612
  case "Literal":
1787
- return Number.isFinite(node.value) ? `${node.value}` : "0";
1613
+ return Number.isFinite(node.value) ? `${node.value} ` : "0";
1788
1614
  case "VectorLiteral":
1789
1615
  return `vec(${node.values.join(", ")})`;
1790
1616
  case "Reference":
1791
1617
  return node.name;
1792
1618
  case "Unary":
1793
- return `${node.operator}${stringifyExpression(node.operand)}`;
1619
+ return `${node.operator}${stringifyExpression(node.operand)} `;
1794
1620
  case "Binary":
1795
- return `${stringifyExpression(node.left)} ${node.operator} ${stringifyExpression(node.right)}`;
1621
+ return `${stringifyExpression(node.left)} ${node.operator} ${stringifyExpression(node.right)} `;
1796
1622
  case "Function":
1797
- return `${node.name}(${node.args.map(stringifyExpression).join(", ")})`;
1623
+ return `${node.name} (${node.args.map(stringifyExpression).join(", ")})`;
1798
1624
  default:
1799
1625
  return "";
1800
1626
  }
@@ -1859,6 +1685,61 @@ function buildBindingMetadataFromExpression(node, variables) {
1859
1685
  }
1860
1686
 
1861
1687
  // src/graphBuilder.ts
1688
+ function resolveBindingSlotInputId(bindingInputId, inputsById) {
1689
+ if (!bindingInputId || bindingInputId === import_utils5.SELF_BINDING_ID) {
1690
+ return bindingInputId;
1691
+ }
1692
+ const resolvedInputId = (0, import_utils4.resolveStandardRigInputId)(bindingInputId, inputsById);
1693
+ if (inputsById.has(resolvedInputId)) {
1694
+ return resolvedInputId;
1695
+ }
1696
+ return bindingInputId;
1697
+ }
1698
+ function isRigElementAliasInput(inputId, inputsById) {
1699
+ const input = inputsById.get(inputId);
1700
+ if (!input?.path) {
1701
+ return false;
1702
+ }
1703
+ return (0, import_utils4.isRigElementStandardInputPath)(input.path);
1704
+ }
1705
+ function isHigherOrderRigBindingInput(inputId, inputsById) {
1706
+ const input = inputsById.get(inputId);
1707
+ if (!input?.path) {
1708
+ return false;
1709
+ }
1710
+ if (isRigElementAliasInput(inputId, inputsById)) {
1711
+ return false;
1712
+ }
1713
+ return true;
1714
+ }
1715
+ function bindingReferencesRigElementInput(binding, inputsById) {
1716
+ const candidateInputIds = /* @__PURE__ */ new Set();
1717
+ if (binding.inputId && binding.inputId !== import_utils5.SELF_BINDING_ID) {
1718
+ candidateInputIds.add(binding.inputId);
1719
+ }
1720
+ binding.slots.forEach((slot) => {
1721
+ if (slot.inputId && slot.inputId !== import_utils5.SELF_BINDING_ID) {
1722
+ candidateInputIds.add(slot.inputId);
1723
+ }
1724
+ });
1725
+ for (const candidateInputId of candidateInputIds) {
1726
+ if ((0, import_utils4.isRigElementStandardInputPath)(candidateInputId)) {
1727
+ return true;
1728
+ }
1729
+ const resolvedCandidateId = resolveBindingSlotInputId(
1730
+ candidateInputId,
1731
+ inputsById
1732
+ );
1733
+ if (!resolvedCandidateId || resolvedCandidateId === import_utils5.SELF_BINDING_ID) {
1734
+ continue;
1735
+ }
1736
+ const resolvedInput = inputsById.get(resolvedCandidateId);
1737
+ if (resolvedInput && (0, import_utils4.isRigElementStandardInputPath)(resolvedInput.path)) {
1738
+ return true;
1739
+ }
1740
+ }
1741
+ return false;
1742
+ }
1862
1743
  function evaluateBinding({
1863
1744
  binding,
1864
1745
  target,
@@ -1867,9 +1748,18 @@ function evaluateBinding({
1867
1748
  component,
1868
1749
  safeId,
1869
1750
  context,
1870
- selfNodeId
1751
+ selfNodeId,
1752
+ enforceRigBoundaryRules = false
1871
1753
  }) {
1872
- const { nodes, edges, ensureInputNode, bindingIssues, summaryBindings } = context;
1754
+ const {
1755
+ nodes,
1756
+ edges,
1757
+ ensureInputNode,
1758
+ bindingIssues,
1759
+ summaryBindings,
1760
+ inputsById,
1761
+ inputBindings
1762
+ } = context;
1873
1763
  const exprContext = {
1874
1764
  componentSafeId: safeId,
1875
1765
  nodes,
@@ -1878,7 +1768,6 @@ function evaluateBinding({
1878
1768
  counter: 0,
1879
1769
  reservedNodes: /* @__PURE__ */ new Map(),
1880
1770
  nodeValueTypes: /* @__PURE__ */ new Map(),
1881
- slotRemapNodes: /* @__PURE__ */ new Map(),
1882
1771
  graphReservedNodes: context.graphReservedNodes,
1883
1772
  generateReservedNodeId: context.generateReservedNodeId
1884
1773
  };
@@ -1894,9 +1783,13 @@ function evaluateBinding({
1894
1783
  const fallbackAlias = `s${index + 1}`;
1895
1784
  const alias = aliasBase.length > 0 ? aliasBase : fallbackAlias;
1896
1785
  const slotId = slot.id && slot.id.length > 0 ? slot.id : alias;
1786
+ const resolvedSlotInputId = resolveBindingSlotInputId(
1787
+ slot.inputId,
1788
+ inputsById
1789
+ );
1897
1790
  const slotValueType = slot.valueType === "vector" ? "vector" : "scalar";
1898
1791
  let slotOutputId;
1899
- if (slot.inputId === import_utils3.SELF_BINDING_ID) {
1792
+ if (slot.inputId === import_utils5.SELF_BINDING_ID) {
1900
1793
  if (selfNodeId) {
1901
1794
  slotOutputId = selfNodeId;
1902
1795
  hasActiveSlot = true;
@@ -1909,19 +1802,30 @@ function evaluateBinding({
1909
1802
  expressionIssues.push("Self reference unavailable for this input.");
1910
1803
  slotOutputId = getConstantNodeId(exprContext, target.defaultValue);
1911
1804
  }
1912
- } else if (slot.inputId) {
1913
- const inputNode = ensureInputNode(slot.inputId);
1914
- if (inputNode) {
1915
- slotOutputId = inputNode.nodeId;
1916
- hasActiveSlot = true;
1917
- setNodeValueType(
1918
- exprContext,
1919
- slotOutputId,
1920
- slotValueType === "vector" ? "vector" : "scalar"
1805
+ } else if (resolvedSlotInputId) {
1806
+ const inputId = resolvedSlotInputId;
1807
+ const sourceBinding = inputBindings[inputId];
1808
+ const allowedHigherOrderViaRigElementSource = sourceBinding !== void 0 && bindingReferencesRigElementInput(sourceBinding, inputsById);
1809
+ if (enforceRigBoundaryRules && isHigherOrderRigBindingInput(inputId, inputsById) && !allowedHigherOrderViaRigElementSource && inputId !== import_utils5.SELF_BINDING_ID) {
1810
+ expressionIssues.push(
1811
+ `Input "${inputId}" is a higher-order rig input and cannot directly drive animatable "${target.id}".`
1921
1812
  );
1922
- } else {
1923
- expressionIssues.push(`Missing standard input "${slot.inputId}".`);
1924
1813
  slotOutputId = getConstantNodeId(exprContext, 0);
1814
+ hasActiveSlot = true;
1815
+ } else {
1816
+ const inputNode = ensureInputNode(inputId);
1817
+ if (inputNode) {
1818
+ slotOutputId = inputNode.nodeId;
1819
+ hasActiveSlot = true;
1820
+ setNodeValueType(
1821
+ exprContext,
1822
+ slotOutputId,
1823
+ slotValueType === "vector" ? "vector" : "scalar"
1824
+ );
1825
+ } else {
1826
+ expressionIssues.push(`Missing standard input "${inputId}".`);
1827
+ slotOutputId = getConstantNodeId(exprContext, 0);
1828
+ }
1925
1829
  }
1926
1830
  } else {
1927
1831
  slotOutputId = getConstantNodeId(exprContext, 0);
@@ -1931,13 +1835,11 @@ function evaluateBinding({
1931
1835
  nodeId: slotOutputId,
1932
1836
  slotId,
1933
1837
  slotAlias: alias,
1934
- inputId: slot.inputId ?? null,
1838
+ inputId: resolvedSlotInputId ?? null,
1935
1839
  targetId,
1936
1840
  animatableId,
1937
1841
  component,
1938
- valueType: slotValueType,
1939
- remap: { ...slot.remap },
1940
- autoRemap: slot.inputId !== import_utils3.SELF_BINDING_ID && slotValueType === "scalar"
1842
+ valueType: slotValueType
1941
1843
  });
1942
1844
  setNodeValueType(
1943
1845
  exprContext,
@@ -1950,8 +1852,7 @@ function evaluateBinding({
1950
1852
  component,
1951
1853
  slotId,
1952
1854
  slotAlias: alias,
1953
- inputId: slot.inputId ?? null,
1954
- remap: { ...slot.remap },
1855
+ inputId: resolvedSlotInputId ?? null,
1955
1856
  expression: trimmedExpression,
1956
1857
  valueType: slotValueType,
1957
1858
  nodeId: slotOutputId,
@@ -1970,9 +1871,7 @@ function evaluateBinding({
1970
1871
  targetId,
1971
1872
  animatableId,
1972
1873
  component,
1973
- valueType: targetValueType,
1974
- remap: createDefaultRemap(target),
1975
- autoRemap: false
1874
+ valueType: targetValueType
1976
1875
  });
1977
1876
  setNodeValueType(
1978
1877
  exprContext,
@@ -1986,7 +1885,6 @@ function evaluateBinding({
1986
1885
  slotId: PRIMARY_SLOT_ID,
1987
1886
  slotAlias: alias,
1988
1887
  inputId: null,
1989
- remap: createDefaultRemap(target),
1990
1888
  expression: trimmedExpression,
1991
1889
  valueType: targetValueType,
1992
1890
  nodeId: constantId,
@@ -2101,6 +1999,20 @@ function buildRigInputPath(faceId, inputPath) {
2101
1999
  const suffix = trimmed ? `/${trimmed}` : "";
2102
2000
  return `rig/${faceId}${suffix}`;
2103
2001
  }
2002
+ function buildPoseControlInputPath(faceId, inputId) {
2003
+ return `rig/${faceId}/pose/control/${inputId}`;
2004
+ }
2005
+ function isPoseWeightInputPath(path) {
2006
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2007
+ return normalized.startsWith("/poses/") && normalized.endsWith(".weight");
2008
+ }
2009
+ function isPoseControlPath(path) {
2010
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2011
+ return normalized.startsWith("/pose/control/");
2012
+ }
2013
+ function resolveInputComposeMode(mode) {
2014
+ return mode === "average" ? "average" : "add";
2015
+ }
2104
2016
  function getComponentOrder(animatable) {
2105
2017
  switch (animatable.type) {
2106
2018
  case "vector2":
@@ -2183,73 +2095,6 @@ function ensureOperandValueType(context, operandId, expected, functionName, inpu
2183
2095
  `Function "${functionName}" expects ${expectation} input for "${inputId}", but the expression produced ${actual}.`
2184
2096
  );
2185
2097
  }
2186
- function resolveSlotReferenceNode(entry, fallbackNodeId, context) {
2187
- if (entry.kind !== "slot") {
2188
- return fallbackNodeId;
2189
- }
2190
- const metadata = entry.metadata;
2191
- if (!metadata || metadata.autoRemap === false || !metadata.remap) {
2192
- return fallbackNodeId;
2193
- }
2194
- if (metadata.valueType !== "scalar") {
2195
- return fallbackNodeId;
2196
- }
2197
- if (isIdentityRemap(metadata.remap)) {
2198
- return fallbackNodeId;
2199
- }
2200
- const cacheKey = metadata.slotId ?? entry.name;
2201
- const existing = context.slotRemapNodes.get(cacheKey);
2202
- if (existing) {
2203
- return existing;
2204
- }
2205
- const remapNodeId = createSlotRemapNode(
2206
- context,
2207
- fallbackNodeId,
2208
- metadata.remap,
2209
- metadata.slotAlias ?? cacheKey
2210
- );
2211
- context.slotRemapNodes.set(cacheKey, remapNodeId);
2212
- return remapNodeId;
2213
- }
2214
- function createSlotRemapNode(context, sourceNodeId, remap, slotKey) {
2215
- const safeSlotKey = sanitizeNodeId(slotKey || "slot");
2216
- const nodeId = `slot_remap_${safeSlotKey}_${context.counter++}`;
2217
- context.nodes.push({
2218
- id: nodeId,
2219
- type: "centered_remap",
2220
- inputDefaults: {
2221
- in_low: remap.inLow,
2222
- in_anchor: remap.inAnchor,
2223
- in_high: remap.inHigh,
2224
- out_low: remap.outLow,
2225
- out_anchor: remap.outAnchor,
2226
- out_high: remap.outHigh
2227
- }
2228
- });
2229
- context.edges.push({
2230
- from: { nodeId: sourceNodeId },
2231
- to: { nodeId, portId: "in" }
2232
- });
2233
- setNodeValueType(context, nodeId, "scalar");
2234
- return nodeId;
2235
- }
2236
- function isIdentityRemap(remap) {
2237
- return nearlyEqual(remap.inLow, remap.outLow) && nearlyEqual(remap.inAnchor, remap.outAnchor) && nearlyEqual(remap.inHigh, remap.outHigh);
2238
- }
2239
- function nearlyEqual(a, b, epsilon = 1e-4) {
2240
- return Math.abs(a - b) <= epsilon;
2241
- }
2242
- var REMAP_FUNCTION_NODE_TYPES = /* @__PURE__ */ new Set([
2243
- "piecewise_remap",
2244
- "centered_remap",
2245
- "remap"
2246
- ]);
2247
- function shouldSkipAutoRemapForArgument(nodeType, index) {
2248
- if (!REMAP_FUNCTION_NODE_TYPES.has(nodeType)) {
2249
- return false;
2250
- }
2251
- return index === 0;
2252
- }
2253
2098
  function getConstantNodeId(context, value) {
2254
2099
  const key = Number.isFinite(value) ? value.toString() : "NaN";
2255
2100
  const existing = context.constants.get(key);
@@ -2708,8 +2553,7 @@ var BINARY_FUNCTION_OPERATOR_MAP = {
2708
2553
  "&&": "and",
2709
2554
  "||": "or"
2710
2555
  };
2711
- function materializeExpression(node, context, variables, issues, options) {
2712
- const autoRemap = options?.autoRemap !== false;
2556
+ function materializeExpression(node, context, variables, issues) {
2713
2557
  switch (node.type) {
2714
2558
  case "Literal": {
2715
2559
  return getConstantNodeId(context, node.value);
@@ -2724,10 +2568,7 @@ function materializeExpression(node, context, variables, issues, options) {
2724
2568
  return getConstantNodeId(context, 0);
2725
2569
  }
2726
2570
  const mappedId = entry.nodeId ?? getConstantNodeId(context, 0);
2727
- if (!autoRemap || entry.kind !== "slot") {
2728
- return mappedId;
2729
- }
2730
- return resolveSlotReferenceNode(entry, mappedId, context);
2571
+ return mappedId;
2731
2572
  }
2732
2573
  case "Unary": {
2733
2574
  const operandId = materializeExpression(
@@ -2865,13 +2706,7 @@ function materializeExpression(node, context, variables, issues, options) {
2865
2706
  );
2866
2707
  const operandNodes = argNodes.slice(0, operandLimit);
2867
2708
  const operands = operandNodes.map(
2868
- (arg, index) => materializeExpression(
2869
- arg,
2870
- context,
2871
- variables,
2872
- issues,
2873
- shouldSkipAutoRemapForArgument(definition.nodeType, index) ? { autoRemap: false } : void 0
2874
- )
2709
+ (arg, _index) => materializeExpression(arg, context, variables, issues)
2875
2710
  );
2876
2711
  const paramArgStart = operandLimit;
2877
2712
  const paramArgNodes = paramArgCount > 0 ? argNodes.slice(paramArgStart, paramArgStart + paramArgCount) : [];
@@ -2900,7 +2735,9 @@ function buildRigGraphSpec({
2900
2735
  bindings,
2901
2736
  inputsById,
2902
2737
  inputBindings,
2903
- inputMetadata
2738
+ inputMetadata,
2739
+ inputComposeModesById,
2740
+ pipelineV1
2904
2741
  }) {
2905
2742
  const metadataByInputId = inputMetadata ?? /* @__PURE__ */ new Map();
2906
2743
  const irBuilder = createIrGraphBuilder({
@@ -2923,8 +2760,634 @@ function buildRigGraphSpec({
2923
2760
  const computedInputs = /* @__PURE__ */ new Set();
2924
2761
  const summaryBindings = [];
2925
2762
  const bindingIssues = /* @__PURE__ */ new Map();
2763
+ const stagedPipelineByInputId = /* @__PURE__ */ new Map();
2926
2764
  const animatableEntries = /* @__PURE__ */ new Map();
2927
2765
  const outputs = /* @__PURE__ */ new Set();
2766
+ const composeModeByInputId = /* @__PURE__ */ new Map();
2767
+ Object.entries(inputComposeModesById ?? {}).forEach(([inputId, mode]) => {
2768
+ if (!inputsById.has(inputId)) {
2769
+ return;
2770
+ }
2771
+ composeModeByInputId.set(inputId, resolveInputComposeMode(mode));
2772
+ });
2773
+ const shouldComposeInputWithPoseControl = (input) => {
2774
+ if (!composeModeByInputId.has(input.id)) {
2775
+ return false;
2776
+ }
2777
+ if (isPoseWeightInputPath(input.path)) {
2778
+ return false;
2779
+ }
2780
+ if (isPoseControlPath(input.path)) {
2781
+ return false;
2782
+ }
2783
+ return true;
2784
+ };
2785
+ const buildNormalizedAdditiveBlendNodeId = ({
2786
+ nodeIdPrefix,
2787
+ sourceNodeIds,
2788
+ baseline
2789
+ }) => {
2790
+ if (sourceNodeIds.length === 0) {
2791
+ const fallbackNodeId = `${nodeIdPrefix}_baseline`;
2792
+ nodes.push({
2793
+ id: fallbackNodeId,
2794
+ type: "constant",
2795
+ params: {
2796
+ value: baseline
2797
+ }
2798
+ });
2799
+ return fallbackNodeId;
2800
+ }
2801
+ if (sourceNodeIds.length === 1) {
2802
+ return sourceNodeIds[0];
2803
+ }
2804
+ const addNodeId = `${nodeIdPrefix}_add`;
2805
+ nodes.push({
2806
+ id: addNodeId,
2807
+ type: "add"
2808
+ });
2809
+ sourceNodeIds.forEach((sourceNodeId, index) => {
2810
+ edges.push({
2811
+ from: { nodeId: sourceNodeId },
2812
+ to: { nodeId: addNodeId, portId: `operand_${index + 1}` }
2813
+ });
2814
+ });
2815
+ const normalizedNodeId = `${nodeIdPrefix}_normalized_add`;
2816
+ nodes.push({
2817
+ id: normalizedNodeId,
2818
+ type: "subtract",
2819
+ inputDefaults: {
2820
+ rhs: (sourceNodeIds.length - 1) * baseline
2821
+ }
2822
+ });
2823
+ edges.push({
2824
+ from: { nodeId: addNodeId },
2825
+ to: { nodeId: normalizedNodeId, portId: "lhs" }
2826
+ });
2827
+ return normalizedNodeId;
2828
+ };
2829
+ const splitTopLevelCommaSeparated = (value) => {
2830
+ const segments = [];
2831
+ let depthParen = 0;
2832
+ let depthBracket = 0;
2833
+ let start = 0;
2834
+ for (let index = 0; index < value.length; index += 1) {
2835
+ const char = value[index];
2836
+ if (char === "(") {
2837
+ depthParen += 1;
2838
+ continue;
2839
+ }
2840
+ if (char === ")") {
2841
+ depthParen = Math.max(0, depthParen - 1);
2842
+ continue;
2843
+ }
2844
+ if (char === "[") {
2845
+ depthBracket += 1;
2846
+ continue;
2847
+ }
2848
+ if (char === "]") {
2849
+ depthBracket = Math.max(0, depthBracket - 1);
2850
+ continue;
2851
+ }
2852
+ if (char === "," && depthParen === 0 && depthBracket === 0) {
2853
+ segments.push(value.slice(start, index));
2854
+ start = index + 1;
2855
+ }
2856
+ }
2857
+ segments.push(value.slice(start));
2858
+ return segments;
2859
+ };
2860
+ const stripTopLevelAssignment = (value) => {
2861
+ let depthParen = 0;
2862
+ let depthBracket = 0;
2863
+ for (let index = 0; index < value.length; index += 1) {
2864
+ const char = value[index];
2865
+ if (char === "(") {
2866
+ depthParen += 1;
2867
+ continue;
2868
+ }
2869
+ if (char === ")") {
2870
+ depthParen = Math.max(0, depthParen - 1);
2871
+ continue;
2872
+ }
2873
+ if (char === "[") {
2874
+ depthBracket += 1;
2875
+ continue;
2876
+ }
2877
+ if (char === "]") {
2878
+ depthBracket = Math.max(0, depthBracket - 1);
2879
+ continue;
2880
+ }
2881
+ if (char !== "=" || depthParen !== 0 || depthBracket !== 0) {
2882
+ continue;
2883
+ }
2884
+ const previous = value[index - 1];
2885
+ const next = value[index + 1];
2886
+ if (previous === "=" || next === "=") {
2887
+ continue;
2888
+ }
2889
+ return value.slice(index + 1).trim();
2890
+ }
2891
+ return value.trim();
2892
+ };
2893
+ const isNormalizedAdditiveFunctionName = (value) => {
2894
+ const normalized = value.trim().toLowerCase();
2895
+ return normalized === "normalizedadditive" || normalized === "normalizedaddative" || normalized === "noramalizedadditive" || normalized === "noramalizedaddative";
2896
+ };
2897
+ const buildNormalizedAdditiveExpression = (value) => {
2898
+ const args = splitTopLevelCommaSeparated(value).map(
2899
+ (entry) => entry.trim()
2900
+ );
2901
+ if (args.length === 0) {
2902
+ return "default";
2903
+ }
2904
+ const parentTerms = [];
2905
+ let baselineExpression = "default";
2906
+ const firstArg = args[0];
2907
+ if (firstArg && firstArg.startsWith("[") && firstArg.endsWith("]")) {
2908
+ const inner = firstArg.slice(1, -1).trim();
2909
+ if (inner.length > 0) {
2910
+ splitTopLevelCommaSeparated(inner).forEach((entry) => {
2911
+ const term = entry.trim();
2912
+ if (term.length > 0) {
2913
+ parentTerms.push(term);
2914
+ }
2915
+ });
2916
+ }
2917
+ args.slice(1).forEach((entry) => {
2918
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
2919
+ if (baselineMatch?.[1]) {
2920
+ baselineExpression = baselineMatch[1].trim();
2921
+ return;
2922
+ }
2923
+ const term = entry.trim();
2924
+ if (term.length > 0) {
2925
+ parentTerms.push(term);
2926
+ }
2927
+ });
2928
+ } else {
2929
+ args.forEach((entry) => {
2930
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
2931
+ if (baselineMatch?.[1]) {
2932
+ baselineExpression = baselineMatch[1].trim();
2933
+ return;
2934
+ }
2935
+ const term = entry.trim();
2936
+ if (term.length > 0) {
2937
+ parentTerms.push(term);
2938
+ }
2939
+ });
2940
+ }
2941
+ if (parentTerms.length === 0) {
2942
+ return `(${baselineExpression})`;
2943
+ }
2944
+ if (parentTerms.length === 1) {
2945
+ return `(${parentTerms[0]})`;
2946
+ }
2947
+ return `((${parentTerms.join(" + ")}) - (${parentTerms.length - 1}) * (${baselineExpression}))`;
2948
+ };
2949
+ const rewriteNormalizedAdditiveCalls = (value) => {
2950
+ let cursor = 0;
2951
+ let rewritten = "";
2952
+ while (cursor < value.length) {
2953
+ const remaining = value.slice(cursor);
2954
+ const match = remaining.match(
2955
+ /(normalizedadditive|normalizedaddative|noramalizedadditive|noramalizedaddative)\s*\(/i
2956
+ );
2957
+ if (!match || match.index === void 0) {
2958
+ rewritten += remaining;
2959
+ break;
2960
+ }
2961
+ const matchStart = cursor + match.index;
2962
+ const functionName = match[1] ?? "";
2963
+ const openParenIndex = value.indexOf(
2964
+ "(",
2965
+ matchStart + functionName.length
2966
+ );
2967
+ if (openParenIndex < 0) {
2968
+ rewritten += value.slice(cursor);
2969
+ break;
2970
+ }
2971
+ rewritten += value.slice(cursor, matchStart);
2972
+ let depth = 1;
2973
+ let closeParenIndex = openParenIndex + 1;
2974
+ while (closeParenIndex < value.length && depth > 0) {
2975
+ const char = value[closeParenIndex];
2976
+ if (char === "(") {
2977
+ depth += 1;
2978
+ } else if (char === ")") {
2979
+ depth -= 1;
2980
+ }
2981
+ closeParenIndex += 1;
2982
+ }
2983
+ if (depth !== 0) {
2984
+ rewritten += value.slice(matchStart);
2985
+ break;
2986
+ }
2987
+ const argsContent = value.slice(openParenIndex + 1, closeParenIndex - 1);
2988
+ if (isNormalizedAdditiveFunctionName(functionName)) {
2989
+ rewritten += buildNormalizedAdditiveExpression(argsContent);
2990
+ } else {
2991
+ rewritten += value.slice(matchStart, closeParenIndex);
2992
+ }
2993
+ cursor = closeParenIndex;
2994
+ }
2995
+ return rewritten;
2996
+ };
2997
+ const normalizeStagedFormulaExpression = (expression) => {
2998
+ const rhs = stripTopLevelAssignment(expression);
2999
+ return rewriteNormalizedAdditiveCalls(rhs);
3000
+ };
3001
+ const normalizeFormulaSignature = (expression) => normalizeStagedFormulaExpression(expression).replace(/\s+/g, "").toLowerCase();
3002
+ const buildDefaultParentTransformNodeId = (params) => {
3003
+ let transformedNodeId = params.sourceNodeId;
3004
+ if (params.scale !== 1) {
3005
+ const scaledNodeId = `input_parent_scale_${params.nodeSuffix}`;
3006
+ nodes.push({
3007
+ id: scaledNodeId,
3008
+ type: "multiply",
3009
+ inputDefaults: {
3010
+ operand_2: params.scale
3011
+ }
3012
+ });
3013
+ edges.push({
3014
+ from: { nodeId: transformedNodeId },
3015
+ to: { nodeId: scaledNodeId, portId: "operand_1" }
3016
+ });
3017
+ transformedNodeId = scaledNodeId;
3018
+ }
3019
+ if (params.offset !== 0) {
3020
+ const offsetNodeId = `input_parent_offset_${params.nodeSuffix}`;
3021
+ nodes.push({
3022
+ id: offsetNodeId,
3023
+ type: "add",
3024
+ inputDefaults: {
3025
+ operand_2: params.offset
3026
+ }
3027
+ });
3028
+ edges.push({
3029
+ from: { nodeId: transformedNodeId },
3030
+ to: { nodeId: offsetNodeId, portId: "operand_1" }
3031
+ });
3032
+ transformedNodeId = offsetNodeId;
3033
+ }
3034
+ return transformedNodeId;
3035
+ };
3036
+ const buildStagedFormulaNodeId = (params) => {
3037
+ const normalizedExpression = normalizeStagedFormulaExpression(
3038
+ params.expression
3039
+ );
3040
+ if (!normalizedExpression) {
3041
+ return params.fallbackNodeId;
3042
+ }
3043
+ const parseResult = parseControlExpression(normalizedExpression);
3044
+ const issues = [];
3045
+ if (!parseResult.node) {
3046
+ parseResult.errors.forEach((error) => {
3047
+ issues.push(
3048
+ `${params.issuePrefix}: ${error.message} (index ${error.index}).`
3049
+ );
3050
+ });
3051
+ }
3052
+ if (!parseResult.node || issues.length > 0) {
3053
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3054
+ issues.forEach((issue) => issueSet.add(issue));
3055
+ if (issues.length > 0) {
3056
+ bindingIssues.set(params.inputId, issueSet);
3057
+ }
3058
+ return params.fallbackNodeId;
3059
+ }
3060
+ const exprContext = {
3061
+ componentSafeId: params.componentSafeId,
3062
+ nodes,
3063
+ edges,
3064
+ constants: /* @__PURE__ */ new Map(),
3065
+ counter: 1,
3066
+ reservedNodes: /* @__PURE__ */ new Map(),
3067
+ nodeValueTypes: /* @__PURE__ */ new Map(),
3068
+ graphReservedNodes,
3069
+ generateReservedNodeId
3070
+ };
3071
+ const variableTable = createExpressionVariableTable();
3072
+ const registerVariableName = (name, nodeId2) => {
3073
+ const trimmed = name.trim();
3074
+ if (!trimmed) {
3075
+ return;
3076
+ }
3077
+ variableTable.registerReservedVariable({
3078
+ name: trimmed,
3079
+ nodeId: nodeId2,
3080
+ description: "Staged pipeline formula variable"
3081
+ });
3082
+ const lower = trimmed.toLowerCase();
3083
+ if (lower !== trimmed) {
3084
+ variableTable.registerReservedVariable({
3085
+ name: lower,
3086
+ nodeId: nodeId2,
3087
+ description: "Staged pipeline formula variable"
3088
+ });
3089
+ }
3090
+ };
3091
+ Object.entries(params.variables).forEach(([name, variable]) => {
3092
+ const nodeId2 = variable.nodeId ?? (typeof variable.value === "number" && Number.isFinite(variable.value) ? getConstantNodeId(exprContext, variable.value) : null);
3093
+ if (!nodeId2) {
3094
+ return;
3095
+ }
3096
+ registerVariableName(name, nodeId2);
3097
+ });
3098
+ const references = collectExpressionReferences(parseResult.node);
3099
+ const missingVariables = variableTable.missing(references);
3100
+ if (missingVariables.length > 0) {
3101
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3102
+ missingVariables.forEach((entry) => {
3103
+ issueSet.add(
3104
+ `${params.issuePrefix}: unknown formula variable "${entry.name}".`
3105
+ );
3106
+ });
3107
+ bindingIssues.set(params.inputId, issueSet);
3108
+ return params.fallbackNodeId;
3109
+ }
3110
+ validateLiteralParamArguments(parseResult.node, issues);
3111
+ const nodeId = materializeExpression(
3112
+ parseResult.node,
3113
+ exprContext,
3114
+ variableTable,
3115
+ issues
3116
+ );
3117
+ if (issues.length > 0) {
3118
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3119
+ issues.forEach(
3120
+ (issue) => issueSet.add(`${params.issuePrefix}: ${issue}`)
3121
+ );
3122
+ bindingIssues.set(params.inputId, issueSet);
3123
+ return params.fallbackNodeId;
3124
+ }
3125
+ return nodeId;
3126
+ };
3127
+ const buildLegacyEffectiveInputNodeId = (input, directNodeId) => {
3128
+ if (!shouldComposeInputWithPoseControl(input)) {
3129
+ return directNodeId;
3130
+ }
3131
+ const safeInputId = sanitizeNodeId(input.id);
3132
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3133
+ const poseControlNodeId = `input_pose_control_${safeInputId}`;
3134
+ nodes.push({
3135
+ id: poseControlNodeId,
3136
+ type: "input",
3137
+ params: {
3138
+ path: buildPoseControlInputPath(faceId, input.id),
3139
+ value: { float: composeBaseline }
3140
+ }
3141
+ });
3142
+ const composeAddNodeId = `input_compose_add_${safeInputId}`;
3143
+ nodes.push({
3144
+ id: composeAddNodeId,
3145
+ type: "add"
3146
+ });
3147
+ edges.push(
3148
+ {
3149
+ from: { nodeId: directNodeId },
3150
+ to: { nodeId: composeAddNodeId, portId: "operand_1" }
3151
+ },
3152
+ {
3153
+ from: { nodeId: poseControlNodeId },
3154
+ to: { nodeId: composeAddNodeId, portId: "operand_2" }
3155
+ }
3156
+ );
3157
+ const composeMode = composeModeByInputId.get(input.id) ?? "add";
3158
+ let composeOutputNodeId = composeAddNodeId;
3159
+ if (composeMode === "average") {
3160
+ composeOutputNodeId = `input_compose_average_${safeInputId}`;
3161
+ nodes.push({
3162
+ id: composeOutputNodeId,
3163
+ type: "divide",
3164
+ inputDefaults: { rhs: 2 }
3165
+ });
3166
+ edges.push({
3167
+ from: { nodeId: composeAddNodeId },
3168
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3169
+ });
3170
+ } else {
3171
+ composeOutputNodeId = `input_compose_normalized_add_${safeInputId}`;
3172
+ nodes.push({
3173
+ id: composeOutputNodeId,
3174
+ type: "subtract",
3175
+ inputDefaults: { rhs: composeBaseline }
3176
+ });
3177
+ edges.push({
3178
+ from: { nodeId: composeAddNodeId },
3179
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3180
+ });
3181
+ }
3182
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3183
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3184
+ const clampNodeId = `input_effective_${safeInputId}`;
3185
+ nodes.push({
3186
+ id: clampNodeId,
3187
+ type: "clamp",
3188
+ inputDefaults: { min: minValue, max: maxValue }
3189
+ });
3190
+ edges.push({
3191
+ from: { nodeId: composeOutputNodeId },
3192
+ to: { nodeId: clampNodeId, portId: "in" }
3193
+ });
3194
+ return clampNodeId;
3195
+ };
3196
+ const buildStagedEffectiveInputNodeId = (input, stagedConfig) => {
3197
+ const safeInputId = sanitizeNodeId(input.id);
3198
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3199
+ const parentContributionNodes = [];
3200
+ const parentNodeIdByAlias = /* @__PURE__ */ new Map();
3201
+ stagedConfig.parents.forEach((parent, index) => {
3202
+ if (!parent.enabled) {
3203
+ return;
3204
+ }
3205
+ const resolvedParentInputId = (0, import_utils4.resolveStandardRigInputId)(
3206
+ parent.inputId,
3207
+ inputsById
3208
+ );
3209
+ const parentInput = ensureInputNode(resolvedParentInputId);
3210
+ if (!parentInput) {
3211
+ const issueSet = bindingIssues.get(input.id) ?? /* @__PURE__ */ new Set();
3212
+ issueSet.add(
3213
+ `Staged parent "${resolvedParentInputId}" missing for "${input.id}".`
3214
+ );
3215
+ bindingIssues.set(input.id, issueSet);
3216
+ return;
3217
+ }
3218
+ const nodeSuffix = `${safeInputId}_${index + 1}`;
3219
+ const fallbackParentNodeId = buildDefaultParentTransformNodeId({
3220
+ sourceNodeId: parentInput.nodeId,
3221
+ nodeSuffix,
3222
+ scale: parent.scale,
3223
+ offset: parent.offset
3224
+ });
3225
+ const defaultParentFormulaExpression = `${parent.alias} = parent * scale + offset`;
3226
+ const parentFormulaNodeId = normalizeFormulaSignature(parent.expression) === normalizeFormulaSignature(defaultParentFormulaExpression) ? fallbackParentNodeId : buildStagedFormulaNodeId({
3227
+ expression: parent.expression,
3228
+ fallbackNodeId: fallbackParentNodeId,
3229
+ componentSafeId: `staged_parent_${nodeSuffix}`,
3230
+ inputId: input.id,
3231
+ issuePrefix: `Parent formula "${parent.alias}"`,
3232
+ variables: {
3233
+ parent: { nodeId: parentInput.nodeId },
3234
+ scale: { value: parent.scale },
3235
+ offset: { value: parent.offset },
3236
+ default: { value: composeBaseline },
3237
+ baseline: { value: composeBaseline }
3238
+ }
3239
+ });
3240
+ parentContributionNodes.push(parentFormulaNodeId);
3241
+ parentNodeIdByAlias.set(parent.alias, parentFormulaNodeId);
3242
+ const normalizedAlias = parent.alias.toLowerCase();
3243
+ if (normalizedAlias !== parent.alias) {
3244
+ parentNodeIdByAlias.set(normalizedAlias, parentFormulaNodeId);
3245
+ }
3246
+ });
3247
+ const parentContributionNodeId = parentContributionNodes.length > 0 ? (() => {
3248
+ const defaultParentContributionNodeId = buildNormalizedAdditiveBlendNodeId({
3249
+ nodeIdPrefix: `input_parent_blend_${safeInputId}`,
3250
+ sourceNodeIds: parentContributionNodes,
3251
+ baseline: composeBaseline
3252
+ });
3253
+ const defaultParentContributionExpression = `parentContribution = normalizedAdditive([${stagedConfig.parents.filter((entry) => entry.enabled).map((entry) => entry.alias).join(", ")}], baseline=default)`;
3254
+ if (normalizeFormulaSignature(stagedConfig.parentBlend.expression) === normalizeFormulaSignature(defaultParentContributionExpression)) {
3255
+ return defaultParentContributionNodeId;
3256
+ }
3257
+ return buildStagedFormulaNodeId({
3258
+ expression: stagedConfig.parentBlend.expression,
3259
+ fallbackNodeId: defaultParentContributionNodeId,
3260
+ componentSafeId: `staged_parent_contribution_${safeInputId}`,
3261
+ inputId: input.id,
3262
+ issuePrefix: "Parent contribution formula",
3263
+ variables: {
3264
+ ...Object.fromEntries(
3265
+ Array.from(parentNodeIdByAlias.entries()).map(
3266
+ ([alias, nodeId]) => [alias, { nodeId }]
3267
+ )
3268
+ ),
3269
+ default: { value: composeBaseline },
3270
+ baseline: { value: composeBaseline }
3271
+ }
3272
+ });
3273
+ })() : null;
3274
+ let poseContributionNodeId = null;
3275
+ const hasPoseContribution = stagedConfig.poseSource.targetIds.length > 0 || shouldComposeInputWithPoseControl(input);
3276
+ if (hasPoseContribution) {
3277
+ poseContributionNodeId = `input_pose_control_${safeInputId}`;
3278
+ nodes.push({
3279
+ id: poseContributionNodeId,
3280
+ type: "input",
3281
+ params: {
3282
+ path: buildPoseControlInputPath(faceId, input.id),
3283
+ value: { float: composeBaseline }
3284
+ }
3285
+ });
3286
+ }
3287
+ const sourceBranchNodeIds = [];
3288
+ if (parentContributionNodeId) {
3289
+ sourceBranchNodeIds.push(parentContributionNodeId);
3290
+ }
3291
+ if (poseContributionNodeId) {
3292
+ sourceBranchNodeIds.push(poseContributionNodeId);
3293
+ }
3294
+ if (stagedConfig.directInput.enabled) {
3295
+ const directNodeId = `input_direct_${safeInputId}`;
3296
+ nodes.push({
3297
+ id: directNodeId,
3298
+ type: "input",
3299
+ params: {
3300
+ path: stagedConfig.directInput.valuePath,
3301
+ value: { float: composeBaseline }
3302
+ }
3303
+ });
3304
+ sourceBranchNodeIds.push(directNodeId);
3305
+ }
3306
+ const sourceBlendNodeId = buildNormalizedAdditiveBlendNodeId({
3307
+ nodeIdPrefix: `input_source_blend_${safeInputId}`,
3308
+ sourceNodeIds: sourceBranchNodeIds,
3309
+ baseline: composeBaseline
3310
+ });
3311
+ const overrideEnabledNodeId = `input_override_enabled_${safeInputId}`;
3312
+ nodes.push({
3313
+ id: overrideEnabledNodeId,
3314
+ type: "input",
3315
+ params: {
3316
+ path: stagedConfig.override.enabledPath,
3317
+ value: { float: stagedConfig.override.enabledDefault ? 1 : 0 }
3318
+ }
3319
+ });
3320
+ const overrideValueNodeId = `input_override_value_${safeInputId}`;
3321
+ nodes.push({
3322
+ id: overrideValueNodeId,
3323
+ type: "input",
3324
+ params: {
3325
+ path: stagedConfig.override.valuePath,
3326
+ value: { float: stagedConfig.override.valueDefault }
3327
+ }
3328
+ });
3329
+ const overrideDeltaNodeId = `input_override_delta_${safeInputId}`;
3330
+ nodes.push({
3331
+ id: overrideDeltaNodeId,
3332
+ type: "subtract"
3333
+ });
3334
+ edges.push(
3335
+ {
3336
+ from: { nodeId: overrideValueNodeId },
3337
+ to: { nodeId: overrideDeltaNodeId, portId: "lhs" }
3338
+ },
3339
+ {
3340
+ from: { nodeId: sourceBlendNodeId },
3341
+ to: { nodeId: overrideDeltaNodeId, portId: "rhs" }
3342
+ }
3343
+ );
3344
+ const overrideScaleNodeId = `input_override_scale_${safeInputId}`;
3345
+ nodes.push({
3346
+ id: overrideScaleNodeId,
3347
+ type: "multiply"
3348
+ });
3349
+ edges.push(
3350
+ {
3351
+ from: { nodeId: overrideEnabledNodeId },
3352
+ to: { nodeId: overrideScaleNodeId, portId: "operand_1" }
3353
+ },
3354
+ {
3355
+ from: { nodeId: overrideDeltaNodeId },
3356
+ to: { nodeId: overrideScaleNodeId, portId: "operand_2" }
3357
+ }
3358
+ );
3359
+ const overrideSelectedNodeId = `input_override_selected_${safeInputId}`;
3360
+ nodes.push({
3361
+ id: overrideSelectedNodeId,
3362
+ type: "add"
3363
+ });
3364
+ edges.push(
3365
+ {
3366
+ from: { nodeId: sourceBlendNodeId },
3367
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_1" }
3368
+ },
3369
+ {
3370
+ from: { nodeId: overrideScaleNodeId },
3371
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_2" }
3372
+ }
3373
+ );
3374
+ if (!stagedConfig.clamp.enabled) {
3375
+ return overrideSelectedNodeId;
3376
+ }
3377
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3378
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3379
+ const clampNodeId = `input_effective_${safeInputId}`;
3380
+ nodes.push({
3381
+ id: clampNodeId,
3382
+ type: "clamp",
3383
+ inputDefaults: { min: minValue, max: maxValue }
3384
+ });
3385
+ edges.push({
3386
+ from: { nodeId: overrideSelectedNodeId },
3387
+ to: { nodeId: clampNodeId, portId: "in" }
3388
+ });
3389
+ return clampNodeId;
3390
+ };
2928
3391
  const ensureInputNode = (inputId) => {
2929
3392
  const existing = inputNodes.get(inputId);
2930
3393
  if (existing) {
@@ -2936,7 +3399,8 @@ function buildRigGraphSpec({
2936
3399
  }
2937
3400
  const defaultValue = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
2938
3401
  const inputBindingRaw = inputBindings[inputId];
2939
- if (inputBindingRaw) {
3402
+ const isStagedInput = (0, import_utils4.hasRigPipelineV1InputConfig)(pipelineV1, inputId);
3403
+ if (isStagedInput || inputBindingRaw) {
2940
3404
  if (buildingDerived.has(inputId)) {
2941
3405
  const issueSet = bindingIssues.get(inputId) ?? /* @__PURE__ */ new Set();
2942
3406
  issueSet.add("Derived input cycle detected.");
@@ -2945,9 +3409,24 @@ function buildRigGraphSpec({
2945
3409
  }
2946
3410
  buildingDerived.add(inputId);
2947
3411
  try {
3412
+ if (isStagedInput) {
3413
+ const stagedConfig = (0, import_utils4.resolveRigPipelineV1InputConfig)({
3414
+ faceId,
3415
+ input,
3416
+ pipelineV1
3417
+ });
3418
+ stagedPipelineByInputId.set(input.id, stagedConfig);
3419
+ computedInputs.add(inputId);
3420
+ const record3 = {
3421
+ nodeId: buildStagedEffectiveInputNodeId(input, stagedConfig),
3422
+ input
3423
+ };
3424
+ inputNodes.set(inputId, record3);
3425
+ return record3;
3426
+ }
2948
3427
  const target = bindingTargetFromInput(input);
2949
3428
  const binding = ensureBindingStructure(inputBindingRaw, target);
2950
- const requiresSelf = binding.inputId === import_utils3.SELF_BINDING_ID || binding.slots.some((slot) => slot.inputId === import_utils3.SELF_BINDING_ID);
3429
+ const requiresSelf = binding.inputId === import_utils5.SELF_BINDING_ID || binding.slots.some((slot) => slot.inputId === import_utils5.SELF_BINDING_ID);
2951
3430
  let selfNodeId;
2952
3431
  if (requiresSelf) {
2953
3432
  const sliderNodeId = `input_raw_${sanitizeNodeId(inputId)}`;
@@ -2968,7 +3447,10 @@ function buildRigGraphSpec({
2968
3447
  animatableId: inputId,
2969
3448
  component: void 0,
2970
3449
  safeId: sanitizeNodeId(inputId),
3450
+ enforceRigBoundaryRules: false,
2971
3451
  context: {
3452
+ inputsById,
3453
+ inputBindings,
2972
3454
  nodes,
2973
3455
  edges,
2974
3456
  ensureInputNode,
@@ -2988,12 +3470,18 @@ function buildRigGraphSpec({
2988
3470
  value: input.defaultValue
2989
3471
  }
2990
3472
  });
2991
- const record3 = { nodeId: constNodeId, input };
3473
+ const record3 = {
3474
+ nodeId: buildLegacyEffectiveInputNodeId(input, constNodeId),
3475
+ input
3476
+ };
2992
3477
  inputNodes.set(inputId, record3);
2993
3478
  return record3;
2994
3479
  }
2995
3480
  computedInputs.add(inputId);
2996
- const record2 = { nodeId: valueNodeId, input };
3481
+ const record2 = {
3482
+ nodeId: buildLegacyEffectiveInputNodeId(input, valueNodeId),
3483
+ input
3484
+ };
2997
3485
  inputNodes.set(inputId, record2);
2998
3486
  return record2;
2999
3487
  } finally {
@@ -3009,7 +3497,10 @@ function buildRigGraphSpec({
3009
3497
  value: { float: defaultValue }
3010
3498
  }
3011
3499
  });
3012
- const record = { nodeId, input };
3500
+ const record = {
3501
+ nodeId: buildLegacyEffectiveInputNodeId(input, nodeId),
3502
+ input
3503
+ };
3013
3504
  inputNodes.set(inputId, record);
3014
3505
  return record;
3015
3506
  };
@@ -3050,7 +3541,10 @@ function buildRigGraphSpec({
3050
3541
  animatableId: component.animatableId,
3051
3542
  component: component.component,
3052
3543
  safeId: component.safeId,
3544
+ enforceRigBoundaryRules: true,
3053
3545
  context: {
3546
+ inputsById,
3547
+ inputBindings,
3054
3548
  nodes,
3055
3549
  edges,
3056
3550
  ensureInputNode,
@@ -3073,7 +3567,6 @@ function buildRigGraphSpec({
3073
3567
  slotId: PRIMARY_SLOT_ID,
3074
3568
  slotAlias: PRIMARY_SLOT_ALIAS,
3075
3569
  inputId: null,
3076
- remap: createDefaultRemap(target),
3077
3570
  expression: PRIMARY_SLOT_ALIAS,
3078
3571
  valueType: target.valueType === "vector" ? "vector" : "scalar",
3079
3572
  issues: ["Binding not found."],
@@ -3128,7 +3621,7 @@ function buildRigGraphSpec({
3128
3621
  let sourceId = entry.values.get(componentKey);
3129
3622
  if (!sourceId) {
3130
3623
  const componentDefault = entry.defaults.get(componentKey) ?? extractComponentDefault(
3131
- (0, import_utils2.buildAnimatableValue)(entry.animatable, void 0),
3624
+ (0, import_utils4.buildAnimatableValue)(entry.animatable, void 0),
3132
3625
  componentKey
3133
3626
  );
3134
3627
  const constNodeId = `const_${safeId}_${componentKey}`;
@@ -3199,6 +3692,61 @@ function buildRigGraphSpec({
3199
3692
  const filteredSummaryBindings = summaryBindings.filter(
3200
3693
  (binding) => outputs.has(binding.animatableId) || computedInputs.has(binding.animatableId)
3201
3694
  );
3695
+ const pipelineV1ByInputId = stagedPipelineByInputId.size > 0 ? Object.fromEntries(
3696
+ Array.from(stagedPipelineByInputId.entries()).map(
3697
+ ([inputId, stagedConfig]) => [
3698
+ inputId,
3699
+ {
3700
+ inputId: stagedConfig.inputId,
3701
+ parents: stagedConfig.parents.map((parent) => ({
3702
+ linkId: parent.linkId,
3703
+ inputId: parent.inputId,
3704
+ alias: parent.alias,
3705
+ scale: parent.scale,
3706
+ offset: parent.offset,
3707
+ enabled: parent.enabled,
3708
+ expression: parent.expression
3709
+ })),
3710
+ children: stagedConfig.children.map((child) => ({
3711
+ linkId: child.linkId,
3712
+ childInputId: child.childInputId
3713
+ })),
3714
+ parentBlend: {
3715
+ mode: stagedConfig.parentBlend.mode,
3716
+ expression: stagedConfig.parentBlend.expression
3717
+ },
3718
+ poseSource: {
3719
+ targetIds: [...stagedConfig.poseSource.targetIds]
3720
+ },
3721
+ directInput: {
3722
+ enabled: stagedConfig.directInput.enabled,
3723
+ valuePath: stagedConfig.directInput.valuePath
3724
+ },
3725
+ sourceBlend: {
3726
+ mode: stagedConfig.sourceBlend.mode
3727
+ },
3728
+ sourceFallback: {
3729
+ whenNoSources: stagedConfig.sourceFallback.whenNoSources
3730
+ },
3731
+ clamp: {
3732
+ enabled: stagedConfig.clamp.enabled
3733
+ },
3734
+ override: {
3735
+ enabledDefault: stagedConfig.override.enabledDefault,
3736
+ valueDefault: stagedConfig.override.valueDefault,
3737
+ enabledPath: stagedConfig.override.enabledPath,
3738
+ valuePath: stagedConfig.override.valuePath
3739
+ }
3740
+ }
3741
+ ]
3742
+ )
3743
+ ) : void 0;
3744
+ const hasPipelineLinks = pipelineV1?.links && typeof pipelineV1.links === "object" && Object.keys(pipelineV1.links).length > 0;
3745
+ const pipelineV1Metadata = pipelineV1ByInputId || hasPipelineLinks ? {
3746
+ version: import_utils4.RIG_PIPELINE_V1_VERSION,
3747
+ ...pipelineV1ByInputId ? { byInputId: pipelineV1ByInputId } : {},
3748
+ ...hasPipelineLinks ? { links: cloneJsonLike2(pipelineV1?.links) } : {}
3749
+ } : void 0;
3202
3750
  const vizijMetadata = {
3203
3751
  vizij: {
3204
3752
  faceId,
@@ -3231,12 +3779,12 @@ function buildRigGraphSpec({
3231
3779
  }),
3232
3780
  bindings: filteredSummaryBindings.map((binding) => ({
3233
3781
  ...binding,
3234
- remap: { ...binding.remap },
3235
3782
  expression: binding.expression,
3236
3783
  valueType: binding.valueType,
3237
3784
  issues: binding.issues ? [...binding.issues] : void 0,
3238
3785
  metadata: binding.metadata ? cloneJsonLike2(binding.metadata) : void 0
3239
- }))
3786
+ })),
3787
+ ...pipelineV1Metadata ? { pipelineV1: pipelineV1Metadata } : {}
3240
3788
  }
3241
3789
  };
3242
3790
  const issuesByTarget = {};
@@ -3266,7 +3814,6 @@ function buildRigGraphSpec({
3266
3814
  bindings: toIrBindingSummary(
3267
3815
  filteredSummaryBindings.map((binding) => ({
3268
3816
  ...binding,
3269
- remap: { ...binding.remap },
3270
3817
  issues: binding.issues ? [...binding.issues] : void 0
3271
3818
  }))
3272
3819
  )
@@ -3385,7 +3932,7 @@ function cloneJsonLike2(value) {
3385
3932
  if (value === void 0 || value === null) {
3386
3933
  return value;
3387
3934
  }
3388
- return JSON.parse(JSON.stringify(value));
3935
+ return (0, import_utils4.cloneDeepSafe)(value);
3389
3936
  }
3390
3937
  function validateRemapDefaults(nodes) {
3391
3938
  const issues = [];
@@ -3413,15 +3960,8 @@ function validateRemapDefaults(nodes) {
3413
3960
 
3414
3961
  // src/ir/inspection.ts
3415
3962
  var import_metadata3 = require("@vizij/node-graph-wasm/metadata");
3963
+ var import_utils6 = require("@vizij/utils");
3416
3964
  var MACHINE_REPORT_VERSION = 1;
3417
- var REMAP_KEYS = [
3418
- "inLow",
3419
- "inAnchor",
3420
- "inHigh",
3421
- "outLow",
3422
- "outAnchor",
3423
- "outHigh"
3424
- ];
3425
3965
  var DEFAULT_DIFF_LIMIT = 50;
3426
3966
  function buildMachineReport(result) {
3427
3967
  return {
@@ -3462,7 +4002,6 @@ function normalizeGraphBindingSummaries(bindings) {
3462
4002
  slotId: binding.slotId,
3463
4003
  slotAlias: binding.slotAlias,
3464
4004
  inputId: binding.inputId ?? null,
3465
- remap: normalizeRemap2(binding.remap),
3466
4005
  expression: binding.expression,
3467
4006
  valueType: binding.valueType,
3468
4007
  nodeId: binding.nodeId,
@@ -3477,7 +4016,7 @@ function cloneBindingMetadata(metadata) {
3477
4016
  if (!metadata) {
3478
4017
  return void 0;
3479
4018
  }
3480
- return JSON.parse(JSON.stringify(metadata));
4019
+ return (0, import_utils6.cloneDeepSafe)(metadata);
3481
4020
  }
3482
4021
  function normalizeIssues(issues) {
3483
4022
  const byTargetEntries = Object.entries(issues.byTarget ?? {}).map(
@@ -3561,7 +4100,6 @@ function normalizeIrGraphSummary(summary) {
3561
4100
  function normalizeIrBindingSummaries(bindings) {
3562
4101
  const normalized = bindings.map((binding) => ({
3563
4102
  ...binding,
3564
- remap: sortPlainObject(binding.remap),
3565
4103
  issues: normalizeStringArray(binding.issues)
3566
4104
  }));
3567
4105
  normalized.sort((a, b) => bindingSortKey(a).localeCompare(bindingSortKey(b)));
@@ -3642,6 +4180,9 @@ function normalizeRegistryVariadicSpec(spec) {
3642
4180
  if (typeof spec.max === "number") {
3643
4181
  normalized.max = spec.max;
3644
4182
  }
4183
+ if (spec.keyed) {
4184
+ normalized.keyed = true;
4185
+ }
3645
4186
  return normalized;
3646
4187
  }
3647
4188
  function normalizeRegistryParamSpec(param) {
@@ -3664,13 +4205,6 @@ function normalizeRegistryParamSpec(param) {
3664
4205
  }
3665
4206
  return normalized;
3666
4207
  }
3667
- function normalizeRemap2(remap) {
3668
- const normalized = {};
3669
- REMAP_KEYS.forEach((key) => {
3670
- normalized[key] = remap[key];
3671
- });
3672
- return normalized;
3673
- }
3674
4208
  function normalizeStringArray(values) {
3675
4209
  if (!values || values.length === 0) {
3676
4210
  return void 0;