@vizij/node-graph-authoring 0.0.2 → 0.0.5

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/index.cjs CHANGED
@@ -20,37 +20,46 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ EXPRESSION_FUNCTION_VOCABULARY: () => EXPRESSION_FUNCTION_VOCABULARY,
24
+ MACHINE_REPORT_VERSION: () => MACHINE_REPORT_VERSION,
23
25
  PRIMARY_SLOT_ALIAS: () => PRIMARY_SLOT_ALIAS,
24
26
  PRIMARY_SLOT_ID: () => PRIMARY_SLOT_ID,
27
+ RESERVED_EXPRESSION_VARIABLES: () => RESERVED_EXPRESSION_VARIABLES,
28
+ SCALAR_FUNCTIONS: () => SCALAR_FUNCTIONS,
29
+ SCALAR_FUNCTION_VOCABULARY: () => SCALAR_FUNCTION_VOCABULARY,
25
30
  addBindingSlot: () => addBindingSlot,
26
31
  bindingFromDefinition: () => bindingFromDefinition,
27
- bindingOperatorDefinitions: () => bindingOperatorDefinitions,
28
- bindingOperatorTypes: () => bindingOperatorTypes,
29
32
  bindingTargetFromComponent: () => bindingTargetFromComponent,
30
33
  bindingTargetFromInput: () => bindingTargetFromInput,
31
34
  bindingToDefinition: () => bindingToDefinition,
35
+ buildCanonicalBindingExpression: () => buildCanonicalBindingExpression,
36
+ buildDefaultSlotExpression: () => buildDefaultSlotExpression,
37
+ buildMachineReport: () => buildMachineReport,
38
+ buildPiecewiseRemapExpression: () => buildPiecewiseRemapExpression,
32
39
  buildRigGraphSpec: () => buildRigGraphSpec,
33
40
  collectExpressionReferences: () => collectExpressionReferences,
41
+ compileIrGraph: () => compileIrGraph,
34
42
  createDefaultBinding: () => createDefaultBinding,
35
43
  createDefaultBindings: () => createDefaultBindings,
36
44
  createDefaultInputValues: () => createDefaultInputValues,
37
- createDefaultOperatorSettings: () => createDefaultOperatorSettings,
38
45
  createDefaultParentBinding: () => createDefaultParentBinding,
39
46
  createDefaultRemap: () => createDefaultRemap,
47
+ createExpressionVariableTable: () => createExpressionVariableTable,
48
+ createIrGraphBuilder: () => createIrGraphBuilder,
49
+ createLegacyIrGraph: () => createLegacyIrGraph,
50
+ diffMachineReports: () => diffMachineReports,
40
51
  ensureBindingStructure: () => ensureBindingStructure,
41
- ensureOperatorParams: () => ensureOperatorParams,
42
- getBindingOperatorDefinition: () => getBindingOperatorDefinition,
43
52
  getPrimaryBindingSlot: () => getPrimaryBindingSlot,
44
53
  mapExpression: () => mapExpression,
45
54
  parseControlExpression: () => parseControlExpression,
46
55
  reconcileBindings: () => reconcileBindings,
47
56
  remapValue: () => remapValue,
48
57
  removeBindingSlot: () => removeBindingSlot,
49
- setBindingOperatorEnabled: () => setBindingOperatorEnabled,
58
+ toIrBindingSummary: () => toIrBindingSummary,
50
59
  updateBindingExpression: () => updateBindingExpression,
51
- updateBindingOperatorParam: () => updateBindingOperatorParam,
52
60
  updateBindingSlotAlias: () => updateBindingSlotAlias,
53
61
  updateBindingSlotRemap: () => updateBindingSlotRemap,
62
+ updateBindingSlotValueType: () => updateBindingSlotValueType,
54
63
  updateBindingWithInput: () => updateBindingWithInput
55
64
  });
56
65
  module.exports = __toCommonJS(index_exports);
@@ -61,84 +70,6 @@ var import_utils3 = require("@vizij/utils");
61
70
 
62
71
  // src/state.ts
63
72
  var import_utils = require("@vizij/utils");
64
-
65
- // src/operators.ts
66
- var import_metadata = require("@vizij/node-graph-wasm/metadata");
67
- var OPERATOR_TYPES = [
68
- "spring",
69
- "damp",
70
- "slew"
71
- ];
72
- function valueJsonToNumber(value) {
73
- if (!value || typeof value !== "object") {
74
- return typeof value === "number" ? value : 0;
75
- }
76
- if ("float" in value && typeof value.float === "number") {
77
- return value.float;
78
- }
79
- if ("int" in value && typeof value.int === "number") {
80
- return value.int;
81
- }
82
- return 0;
83
- }
84
- var operatorDefinitionMap = /* @__PURE__ */ new Map();
85
- OPERATOR_TYPES.forEach((type) => {
86
- const signature = (0, import_metadata.requireNodeSignature)(type);
87
- const params = signature.params.map(
88
- (param) => ({
89
- id: param.id,
90
- label: param.label,
91
- doc: param.doc,
92
- min: param.min ?? void 0,
93
- max: param.max ?? void 0,
94
- defaultValue: valueJsonToNumber(param.default_json)
95
- })
96
- );
97
- operatorDefinitionMap.set(type, {
98
- type,
99
- nodeType: signature.type_id,
100
- label: signature.name,
101
- description: signature.doc,
102
- inputs: signature.inputs.map((input) => input.id),
103
- params
104
- });
105
- });
106
- function getBindingOperatorDefinition(type) {
107
- const definition = operatorDefinitionMap.get(type);
108
- if (!definition) {
109
- throw new Error(`Unknown binding operator type '${type}'.`);
110
- }
111
- return definition;
112
- }
113
- var bindingOperatorDefinitions = OPERATOR_TYPES.map((type) => getBindingOperatorDefinition(type));
114
- function createDefaultOperatorSettings(type) {
115
- const definition = getBindingOperatorDefinition(type);
116
- const params = {};
117
- definition.params.forEach((param) => {
118
- params[param.id] = param.defaultValue;
119
- });
120
- return {
121
- type,
122
- enabled: false,
123
- params
124
- };
125
- }
126
- function ensureOperatorParams(operator) {
127
- const definition = getBindingOperatorDefinition(operator.type);
128
- const params = {};
129
- definition.params.forEach((param) => {
130
- const value = operator.params?.[param.id];
131
- params[param.id] = typeof value === "number" ? value : param.defaultValue;
132
- });
133
- return {
134
- type: operator.type,
135
- enabled: !!operator.enabled,
136
- params
137
- };
138
- }
139
- var bindingOperatorTypes = OPERATOR_TYPES;
140
-
141
- // src/state.ts
142
73
  var VECTOR_ANIMATABLE_TYPES = /* @__PURE__ */ new Set(["vector2", "vector3", "euler", "rgb"]);
143
74
  function deriveComponentValueType(component) {
144
75
  if (component.component) {
@@ -184,83 +115,6 @@ var LEGACY_SLOT_PATTERN = /^slot_(\d+)$/i;
184
115
  var ALIAS_SANITIZE_PATTERN = /[^A-Za-z0-9_]+/g;
185
116
  var PRIMARY_SLOT_ID = "s1";
186
117
  var PRIMARY_SLOT_ALIAS = "s1";
187
- function createDefaultOperators() {
188
- return bindingOperatorTypes.map(
189
- (type) => createDefaultOperatorSettings(type)
190
- );
191
- }
192
- function normalizeOperator(operator) {
193
- const normalized = ensureOperatorParams(operator);
194
- const definition = getBindingOperatorDefinition(normalized.type);
195
- const params = {};
196
- definition.params.forEach((param) => {
197
- const value = normalized.params[param.id];
198
- params[param.id] = typeof value === "number" ? value : param.defaultValue;
199
- });
200
- return {
201
- type: normalized.type,
202
- enabled: !!normalized.enabled,
203
- params
204
- };
205
- }
206
- function ensureOperators(binding) {
207
- const existing = /* @__PURE__ */ new Map();
208
- (binding.operators ?? []).forEach((operator) => {
209
- try {
210
- existing.set(operator.type, normalizeOperator(operator));
211
- } catch {
212
- }
213
- });
214
- return bindingOperatorTypes.map((type) => {
215
- const current = existing.get(type);
216
- if (current) {
217
- return current;
218
- }
219
- return createDefaultOperatorSettings(type);
220
- });
221
- }
222
- function paramsEqual(a, b) {
223
- const aKeys = Object.keys(a);
224
- const bKeys = Object.keys(b);
225
- if (aKeys.length !== bKeys.length) {
226
- return false;
227
- }
228
- for (const key of aKeys) {
229
- if (a[key] !== b[key]) {
230
- return false;
231
- }
232
- }
233
- return true;
234
- }
235
- function operatorsEqual(a, b) {
236
- if (!a || a.length !== b.length) {
237
- return false;
238
- }
239
- for (let index = 0; index < a.length; index += 1) {
240
- const left = a[index];
241
- const right = b[index];
242
- if (left.type !== right.type || left.enabled !== right.enabled) {
243
- return false;
244
- }
245
- if (!paramsEqual(left.params, right.params)) {
246
- return false;
247
- }
248
- }
249
- return true;
250
- }
251
- function normalizeBindingWithOperators(binding) {
252
- const operators = ensureOperators(binding);
253
- if (operatorsEqual(binding.operators, operators)) {
254
- return { binding, operators };
255
- }
256
- return {
257
- binding: {
258
- ...binding,
259
- operators
260
- },
261
- operators
262
- };
263
- }
264
118
  function defaultSlotId(index) {
265
119
  return `s${index + 1}`;
266
120
  }
@@ -415,6 +269,12 @@ function normalizeRemap(remap, target) {
415
269
  function cloneRemap(remap) {
416
270
  return (0, import_utils.cloneRemapSettings)(remap);
417
271
  }
272
+ function cloneBindingMetadata(metadata) {
273
+ if (!metadata) {
274
+ return void 0;
275
+ }
276
+ return JSON.parse(JSON.stringify(metadata));
277
+ }
418
278
  function sanitizeRemap(remap, target) {
419
279
  const normalized = normalizeRemap(remap, target);
420
280
  const outputDefaults = deriveOutputDefaults(target);
@@ -462,21 +322,21 @@ function createDefaultBindings(components) {
462
322
  function createDefaultBinding(component) {
463
323
  const remap = createDefaultRemap(component);
464
324
  const valueType = getTargetValueType(component);
325
+ const slots = [
326
+ {
327
+ id: PRIMARY_SLOT_ID,
328
+ alias: PRIMARY_SLOT_ALIAS,
329
+ inputId: null,
330
+ remap: cloneRemap(remap),
331
+ valueType
332
+ }
333
+ ];
465
334
  return {
466
335
  targetId: component.id,
467
336
  inputId: null,
468
337
  remap,
469
- slots: [
470
- {
471
- id: PRIMARY_SLOT_ID,
472
- alias: PRIMARY_SLOT_ALIAS,
473
- inputId: null,
474
- remap: cloneRemap(remap),
475
- valueType
476
- }
477
- ],
478
- expression: PRIMARY_SLOT_ALIAS,
479
- operators: createDefaultOperators()
338
+ slots,
339
+ expression: buildCanonicalExpressionFromSlots(slots)
480
340
  };
481
341
  }
482
342
  function createDefaultParentBinding(component) {
@@ -496,7 +356,7 @@ function createDefaultParentBinding(component) {
496
356
  ...ensured,
497
357
  inputId: import_utils.SELF_BINDING_ID,
498
358
  slots,
499
- expression: "self"
359
+ expression: buildCanonicalExpressionFromSlots(slots)
500
360
  };
501
361
  }
502
362
  function ensurePrimarySlot(binding, target) {
@@ -562,8 +422,13 @@ function ensurePrimarySlot(binding, target) {
562
422
  };
563
423
  });
564
424
  const rawExpression = typeof binding.expression === "string" ? binding.expression.trim() : "";
565
- let expression = rawExpression.length > 0 ? rawExpression : normalizedSlots[0].alias;
566
- expression = rewriteLegacyExpression(expression, aliasReplacements);
425
+ const canonicalExpression = buildCanonicalExpressionFromSlots(normalizedSlots);
426
+ let expression;
427
+ if (expressionMatchesAliasOnly(rawExpression, normalizedSlots)) {
428
+ expression = canonicalExpression;
429
+ } else {
430
+ expression = rewriteLegacyExpression(rawExpression, aliasReplacements);
431
+ }
567
432
  const normalizedBinding = {
568
433
  ...binding,
569
434
  inputId: normalizedSlots[0].inputId ?? null,
@@ -571,14 +436,7 @@ function ensurePrimarySlot(binding, target) {
571
436
  slots: normalizedSlots,
572
437
  expression
573
438
  };
574
- const operators = ensureOperators(normalizedBinding);
575
- if (operatorsEqual(normalizedBinding.operators, operators)) {
576
- return normalizedBinding;
577
- }
578
- return {
579
- ...normalizedBinding,
580
- operators
581
- };
439
+ return normalizedBinding;
582
440
  }
583
441
  function createDefaultInputValues(inputs = []) {
584
442
  const values = {};
@@ -689,87 +547,44 @@ function updateBindingSlotAlias(binding, target, slotId, nextAlias) {
689
547
  );
690
548
  return updated;
691
549
  }
692
- function setBindingOperatorEnabled(binding, type, enabled) {
693
- const { binding: normalizedBinding, operators } = normalizeBindingWithOperators(binding);
694
- let changed = false;
695
- const nextOperators = operators.map((operator) => {
696
- if (operator.type !== type) {
697
- return operator;
698
- }
699
- if (operator.enabled === enabled) {
700
- return operator;
701
- }
702
- changed = true;
703
- return {
704
- ...operator,
705
- enabled
706
- };
707
- });
708
- if (!changed) {
709
- return normalizedBinding;
710
- }
711
- if (operatorsEqual(normalizedBinding.operators, nextOperators)) {
712
- return normalizedBinding;
550
+ function updateBindingSlotValueType(binding, target, slotId, nextValueType) {
551
+ const base = ensurePrimarySlot(binding, target);
552
+ const slotIndex = base.slots.findIndex((slot) => slot.id === slotId);
553
+ if (slotIndex < 0) {
554
+ return base;
713
555
  }
714
- return {
715
- ...normalizedBinding,
716
- operators: nextOperators
717
- };
718
- }
719
- function updateBindingOperatorParam(binding, type, paramId, value) {
720
- const { binding: normalizedBinding, operators } = normalizeBindingWithOperators(binding);
721
- const definition = getBindingOperatorDefinition(type);
722
- const paramDefinition = definition.params.find(
723
- (param) => param.id === paramId
556
+ const normalizedType = sanitizeSlotValueType(
557
+ nextValueType,
558
+ getTargetValueType(target)
724
559
  );
725
- if (!paramDefinition) {
726
- return normalizedBinding;
727
- }
728
- let nextValue = value;
729
- if (typeof paramDefinition.min === "number") {
730
- nextValue = Math.max(paramDefinition.min, nextValue);
731
- }
732
- if (typeof paramDefinition.max === "number") {
733
- nextValue = Math.min(paramDefinition.max, nextValue);
734
- }
735
- let changed = false;
736
- const nextOperators = operators.map((operator) => {
737
- if (operator.type !== type) {
738
- return operator;
739
- }
740
- if (operator.params[paramId] === nextValue) {
741
- return operator;
560
+ const slots = base.slots.map((slot, index) => {
561
+ if (index !== slotIndex) {
562
+ return slot;
742
563
  }
743
- changed = true;
744
564
  return {
745
- ...operator,
746
- params: {
747
- ...operator.params,
748
- [paramId]: nextValue
749
- }
565
+ ...slot,
566
+ valueType: normalizedType
750
567
  };
751
568
  });
752
- if (!changed) {
753
- return normalizedBinding;
754
- }
755
- if (operatorsEqual(normalizedBinding.operators, nextOperators)) {
756
- return normalizedBinding;
757
- }
758
569
  return {
759
- ...normalizedBinding,
760
- operators: nextOperators
570
+ ...base,
571
+ slots
761
572
  };
762
573
  }
763
574
  function updateBindingExpression(binding, target, expression) {
764
575
  const base = ensurePrimarySlot(binding, target);
765
576
  const trimmed = expression.trim();
577
+ const canonicalExpression = buildCanonicalExpressionFromSlots(base.slots);
766
578
  return {
767
579
  ...base,
768
- expression: trimmed.length > 0 ? trimmed : base.slots[0]?.alias ?? PRIMARY_SLOT_ALIAS
580
+ expression: trimmed.length > 0 ? trimmed : canonicalExpression
769
581
  };
770
582
  }
771
583
  function updateBindingSlotRemap(binding, target, slotId, field, value) {
772
584
  const base = ensurePrimarySlot(binding, target);
585
+ const existingExpression = typeof base.expression === "string" ? base.expression.trim() : "";
586
+ const canonicalBefore = buildCanonicalExpressionFromSlots(base.slots);
587
+ const expressionWasDefault = expressionMatchesAliasOnly(existingExpression, base.slots) || expressionsEquivalent(existingExpression, canonicalBefore);
773
588
  const nextSlots = base.slots.map((slot) => {
774
589
  if (slot.id !== slotId) {
775
590
  return slot;
@@ -797,10 +612,23 @@ function updateBindingSlotRemap(binding, target, slotId, field, value) {
797
612
  [field]: value
798
613
  };
799
614
  }
800
- return updated;
615
+ if (!expressionWasDefault) {
616
+ return updated;
617
+ }
618
+ const canonicalAfter = buildCanonicalExpressionFromSlots(updated.slots);
619
+ if (expressionsEquivalent(updated.expression ?? "", canonicalAfter)) {
620
+ return updated;
621
+ }
622
+ return {
623
+ ...updated,
624
+ expression: canonicalAfter
625
+ };
801
626
  }
802
627
  function updateBindingWithInput(binding, target, input, slotId = PRIMARY_SLOT_ID) {
803
628
  const base = ensurePrimarySlot(binding, target);
629
+ const existingExpression = typeof base.expression === "string" ? base.expression.trim() : "";
630
+ const canonicalBefore = buildCanonicalExpressionFromSlots(base.slots);
631
+ const expressionWasDefault = expressionMatchesAliasOnly(existingExpression, base.slots) || expressionsEquivalent(existingExpression, canonicalBefore);
804
632
  const slotIndex = base.slots.findIndex((slot) => slot.id === slotId);
805
633
  const effectiveIndex = slotIndex >= 0 ? slotIndex : base.slots.length;
806
634
  const slots = base.slots.map((slot) => ({
@@ -817,9 +645,10 @@ function updateBindingWithInput(binding, target, input, slotId = PRIMARY_SLOT_ID
817
645
  });
818
646
  }
819
647
  const currentSlot = slots[effectiveIndex];
648
+ let nextBinding;
820
649
  if (!input) {
821
650
  const normalizedSlotRemap = sanitizeRemap(currentSlot.remap, target);
822
- const updatedRemap2 = {
651
+ const updatedRemap = {
823
652
  ...normalizedSlotRemap,
824
653
  inLow: DEFAULT_INPUT_RANGE.min,
825
654
  inAnchor: DEFAULT_INPUT_ANCHOR,
@@ -828,45 +657,59 @@ function updateBindingWithInput(binding, target, input, slotId = PRIMARY_SLOT_ID
828
657
  slots[effectiveIndex] = {
829
658
  ...currentSlot,
830
659
  inputId: null,
831
- remap: cloneRemap(updatedRemap2)
660
+ remap: cloneRemap(updatedRemap)
832
661
  };
833
662
  if (effectiveIndex === 0) {
834
- return {
663
+ nextBinding = {
835
664
  ...base,
836
665
  inputId: null,
837
- remap: cloneRemap(updatedRemap2),
666
+ remap: cloneRemap(updatedRemap),
667
+ slots
668
+ };
669
+ } else {
670
+ nextBinding = {
671
+ ...base,
838
672
  slots
839
673
  };
840
674
  }
841
- return {
842
- ...base,
843
- slots
675
+ } else {
676
+ const normalizedRemap = sanitizeRemap(currentSlot.remap, target);
677
+ const updatedRemap = {
678
+ ...normalizedRemap,
679
+ inLow: input.range.min,
680
+ inAnchor: clamp(input.defaultValue, input.range.min, input.range.max),
681
+ inHigh: input.range.max,
682
+ ...deriveOutputDefaults(target)
844
683
  };
845
- }
846
- const normalizedRemap = sanitizeRemap(currentSlot.remap, target);
847
- const updatedRemap = {
848
- ...normalizedRemap,
849
- inLow: input.range.min,
850
- inAnchor: clamp(input.defaultValue, input.range.min, input.range.max),
851
- inHigh: input.range.max,
852
- ...deriveOutputDefaults(target)
853
- };
854
- slots[effectiveIndex] = {
855
- ...currentSlot,
856
- inputId: input.id,
857
- remap: cloneRemap(updatedRemap)
858
- };
859
- if (effectiveIndex === 0) {
860
- return {
861
- ...base,
684
+ slots[effectiveIndex] = {
685
+ ...currentSlot,
862
686
  inputId: input.id,
863
- remap: cloneRemap(updatedRemap),
864
- slots
687
+ remap: cloneRemap(updatedRemap)
865
688
  };
689
+ if (effectiveIndex === 0) {
690
+ nextBinding = {
691
+ ...base,
692
+ inputId: input.id,
693
+ remap: cloneRemap(updatedRemap),
694
+ slots
695
+ };
696
+ } else {
697
+ nextBinding = {
698
+ ...base,
699
+ slots
700
+ };
701
+ }
702
+ }
703
+ if (!expressionWasDefault) {
704
+ return nextBinding;
705
+ }
706
+ const canonicalAfter = buildCanonicalExpressionFromSlots(nextBinding.slots);
707
+ if (expressionsEquivalent(nextBinding.expression ?? "", canonicalAfter)) {
708
+ return nextBinding;
866
709
  }
867
710
  return {
868
- ...base,
869
- slots
711
+ ...nextBinding,
712
+ expression: canonicalAfter
870
713
  };
871
714
  }
872
715
  function remapValue(value, remap) {
@@ -932,11 +775,6 @@ function reconcileBindings(previous, components) {
932
775
  return next;
933
776
  }
934
777
  function bindingToDefinition(binding) {
935
- const operators = binding.operators ? binding.operators.map((operator) => ({
936
- type: operator.type,
937
- enabled: !!operator.enabled,
938
- params: { ...operator.params }
939
- })) : void 0;
940
778
  const definition = {
941
779
  inputId: binding.inputId ?? null,
942
780
  remap: cloneRemap(binding.remap),
@@ -945,7 +783,7 @@ function bindingToDefinition(binding) {
945
783
  remap: cloneRemap(slot.remap)
946
784
  })),
947
785
  expression: binding.expression,
948
- operators
786
+ metadata: cloneBindingMetadata(binding.metadata)
949
787
  };
950
788
  return definition;
951
789
  }
@@ -953,7 +791,6 @@ function bindingFromDefinition(target, definition) {
953
791
  if (!definition) {
954
792
  return createDefaultBinding(target);
955
793
  }
956
- const definitionOperators = definition.operators;
957
794
  const binding = {
958
795
  targetId: target.id,
959
796
  inputId: definition.inputId ?? null,
@@ -963,17 +800,91 @@ function bindingFromDefinition(target, definition) {
963
800
  remap: cloneRemap(slot.remap)
964
801
  })),
965
802
  expression: definition.expression,
966
- operators: definitionOperators ? definitionOperators.map((operator) => ({
967
- type: operator.type,
968
- enabled: !!operator.enabled,
969
- params: { ...operator.params }
970
- })) : void 0
803
+ metadata: cloneBindingMetadata(definition.metadata)
971
804
  };
972
805
  return ensureBindingStructure(binding, target);
973
806
  }
807
+ function sanitizeLiteral(value) {
808
+ if (!Number.isFinite(value)) {
809
+ return 0;
810
+ }
811
+ if (Object.is(value, -0)) {
812
+ return 0;
813
+ }
814
+ return value;
815
+ }
816
+ function formatVectorLiteral(values) {
817
+ return `vec(${values.map((value) => sanitizeLiteral(value)).join(", ")})`;
818
+ }
819
+ function buildPiecewiseRemapExpression(alias, remap) {
820
+ const sanitizedAlias = alias && alias.trim().length > 0 ? alias.trim() : PRIMARY_SLOT_ALIAS;
821
+ const inputBreakpoints = [remap.inLow, remap.inAnchor, remap.inHigh];
822
+ const outputBreakpoints = [remap.outLow, remap.outAnchor, remap.outHigh];
823
+ return `piecewise_remap(${sanitizedAlias}, ${formatVectorLiteral(
824
+ inputBreakpoints
825
+ )}, ${formatVectorLiteral(outputBreakpoints)})`;
826
+ }
827
+ function isSelfAlias(alias) {
828
+ return alias.trim().toLowerCase() === "self";
829
+ }
830
+ function buildDefaultSlotExpression(alias, inputId, remap) {
831
+ const sanitizedAlias = alias && alias.trim().length > 0 ? alias.trim() : PRIMARY_SLOT_ALIAS;
832
+ if (inputId === import_utils.SELF_BINDING_ID || isSelfAlias(sanitizedAlias)) {
833
+ return sanitizedAlias;
834
+ }
835
+ return buildPiecewiseRemapExpression(sanitizedAlias, remap);
836
+ }
837
+ function normalizeSlotAliasForExpression(slot, index) {
838
+ if (slot.alias && slot.alias.trim().length > 0) {
839
+ return slot.alias.trim();
840
+ }
841
+ if (slot.id && slot.id.trim().length > 0) {
842
+ return slot.id.trim();
843
+ }
844
+ return defaultSlotId(index);
845
+ }
846
+ function buildAliasOnlyExpression(slots) {
847
+ if (!slots.length) {
848
+ return PRIMARY_SLOT_ALIAS;
849
+ }
850
+ return slots.map((slot, index) => normalizeSlotAliasForExpression(slot, index)).join(" + ");
851
+ }
852
+ function buildCanonicalExpressionFromSlots(slots) {
853
+ if (!slots.length) {
854
+ return PRIMARY_SLOT_ALIAS;
855
+ }
856
+ return slots.map(
857
+ (slot, index) => buildDefaultSlotExpression(
858
+ normalizeSlotAliasForExpression(slot, index),
859
+ slot.inputId ?? null,
860
+ slot.remap
861
+ )
862
+ ).join(" + ");
863
+ }
864
+ function expressionsEquivalent(left, right) {
865
+ return left.trim() === right.trim();
866
+ }
867
+ function expressionMatchesAliasOnly(expression, slots) {
868
+ if (expression.trim().length === 0) {
869
+ return true;
870
+ }
871
+ const aliasOnly = buildAliasOnlyExpression(slots);
872
+ return expressionsEquivalent(expression, aliasOnly);
873
+ }
874
+ function buildCanonicalBindingExpression(binding) {
875
+ const slots = binding.slots ?? [];
876
+ return buildCanonicalExpressionFromSlots(slots);
877
+ }
974
878
 
975
879
  // src/expression.ts
976
- var WHITESPACE = /\s/;
880
+ var STANDARD_WHITESPACE = /\s/;
881
+ var EXTRA_WHITESPACE_CHARS = /* @__PURE__ */ new Set(["\0", "\u200B", "\uFEFF"]);
882
+ function isWhitespaceCharacter(char) {
883
+ if (!char) {
884
+ return false;
885
+ }
886
+ return STANDARD_WHITESPACE.test(char) || EXTRA_WHITESPACE_CHARS.has(char);
887
+ }
977
888
  var IDENT_START = /[A-Za-z_]/;
978
889
  var IDENT_PART = /[A-Za-z0-9_]/;
979
890
  var DIGIT = /[0-9]/;
@@ -1211,6 +1122,10 @@ var ControlExpressionParser = class {
1211
1122
  });
1212
1123
  return null;
1213
1124
  }
1125
+ if (char === "[") {
1126
+ this.index += 1;
1127
+ return this.parseVectorLiteral("]", "vector literal");
1128
+ }
1214
1129
  if (IDENT_START.test(char)) {
1215
1130
  return this.parseIdentifierOrFunction();
1216
1131
  }
@@ -1239,6 +1154,9 @@ var ControlExpressionParser = class {
1239
1154
  this.skipWhitespace();
1240
1155
  if (this.peek() === "(") {
1241
1156
  this.index += 1;
1157
+ if (name.toLowerCase() === "vec") {
1158
+ return this.parseVectorLiteral(")", `"${name}" literal`);
1159
+ }
1242
1160
  const args = [];
1243
1161
  this.skipWhitespace();
1244
1162
  if (this.peek() === ")") {
@@ -1301,28 +1219,134 @@ var ControlExpressionParser = class {
1301
1219
  name
1302
1220
  };
1303
1221
  }
1304
- parseNumber() {
1222
+ parseVectorLiteral(terminator, context) {
1223
+ const values = this.parseVectorLiteralValues(terminator, context);
1224
+ if (!values) {
1225
+ return null;
1226
+ }
1227
+ return {
1228
+ type: "VectorLiteral",
1229
+ values
1230
+ };
1231
+ }
1232
+ parseVectorLiteralValues(terminator, context) {
1233
+ const values = [];
1234
+ this.skipWhitespace();
1235
+ if (this.peek() === terminator) {
1236
+ this.errors.push({
1237
+ index: this.index,
1238
+ message: `Expected at least one value in ${context}.`
1239
+ });
1240
+ return null;
1241
+ }
1242
+ while (true) {
1243
+ this.skipWhitespace();
1244
+ const literalValue = this.parseNumericLiteralValue();
1245
+ if (literalValue === null) {
1246
+ this.errors.push({
1247
+ index: this.index,
1248
+ message: `Expected numeric literal in ${context}.`
1249
+ });
1250
+ return null;
1251
+ }
1252
+ values.push(literalValue);
1253
+ this.skipWhitespace();
1254
+ const next = this.peek();
1255
+ if (next === ",") {
1256
+ this.index += 1;
1257
+ this.skipWhitespace();
1258
+ if (this.peek() === terminator) {
1259
+ this.errors.push({
1260
+ index: this.index,
1261
+ message: `Expected value after "," in ${context}.`
1262
+ });
1263
+ return null;
1264
+ }
1265
+ continue;
1266
+ }
1267
+ if (next === terminator) {
1268
+ this.index += 1;
1269
+ break;
1270
+ }
1271
+ if (next === null) {
1272
+ this.errors.push({
1273
+ index: this.index,
1274
+ message: `Unterminated ${context}.`
1275
+ });
1276
+ } else {
1277
+ this.errors.push({
1278
+ index: this.index,
1279
+ message: `Expected "," or "${terminator}" in ${context}.`
1280
+ });
1281
+ }
1282
+ return null;
1283
+ }
1284
+ if (values.length === 0) {
1285
+ this.errors.push({
1286
+ index: this.index,
1287
+ message: `Expected at least one value in ${context}.`
1288
+ });
1289
+ return null;
1290
+ }
1291
+ return values;
1292
+ }
1293
+ parseNumericLiteralValue(allowLeadingSign = true) {
1305
1294
  const start = this.index;
1295
+ if (allowLeadingSign && (this.peek() === "+" || this.peek() === "-")) {
1296
+ this.index += 1;
1297
+ }
1306
1298
  let hasDigits = false;
1299
+ let seenDecimal = false;
1300
+ let seenExponent = false;
1301
+ let exponentDigits = false;
1302
+ let inExponent = false;
1307
1303
  while (!this.isAtEnd()) {
1308
1304
  const char = this.peek();
1309
1305
  if (DIGIT.test(char)) {
1310
1306
  hasDigits = true;
1307
+ if (inExponent) {
1308
+ exponentDigits = true;
1309
+ }
1310
+ this.index += 1;
1311
+ continue;
1312
+ }
1313
+ if (char === "." && !seenDecimal && !seenExponent) {
1314
+ seenDecimal = true;
1311
1315
  this.index += 1;
1312
1316
  continue;
1313
1317
  }
1314
- if (char === ".") {
1318
+ if ((char === "e" || char === "E") && !seenExponent && hasDigits) {
1319
+ seenExponent = true;
1320
+ inExponent = true;
1321
+ exponentDigits = false;
1315
1322
  this.index += 1;
1323
+ const sign = this.peek();
1324
+ if (sign === "+" || sign === "-") {
1325
+ this.index += 1;
1326
+ }
1316
1327
  continue;
1317
1328
  }
1318
1329
  break;
1319
1330
  }
1331
+ if (!hasDigits || seenExponent && !exponentDigits) {
1332
+ this.index = start;
1333
+ return null;
1334
+ }
1320
1335
  const raw = this.input.slice(start, this.index);
1321
1336
  const value = Number(raw);
1322
- if (!hasDigits || Number.isNaN(value)) {
1337
+ if (!Number.isFinite(value)) {
1338
+ this.index = start;
1339
+ return null;
1340
+ }
1341
+ return value;
1342
+ }
1343
+ parseNumber() {
1344
+ const start = this.index;
1345
+ const value = this.parseNumericLiteralValue(false);
1346
+ if (value === null) {
1323
1347
  this.errors.push({
1324
1348
  index: start,
1325
- message: `Invalid numeric literal "${raw}".`
1349
+ message: "Invalid numeric literal."
1326
1350
  });
1327
1351
  return null;
1328
1352
  }
@@ -1341,7 +1365,11 @@ var ControlExpressionParser = class {
1341
1365
  return null;
1342
1366
  }
1343
1367
  skipWhitespace() {
1344
- while (!this.isAtEnd() && WHITESPACE.test(this.peek())) {
1368
+ while (!this.isAtEnd()) {
1369
+ const char = this.peek();
1370
+ if (!isWhitespaceCharacter(char)) {
1371
+ break;
1372
+ }
1345
1373
  this.index += 1;
1346
1374
  }
1347
1375
  }
@@ -1401,70 +1429,979 @@ function mapExpression(node, visit) {
1401
1429
  }
1402
1430
 
1403
1431
  // src/expressionFunctions.ts
1404
- var import_metadata2 = require("@vizij/node-graph-wasm/metadata");
1405
- var FUNCTION_CONFIGS = [
1406
- { typeId: "sin", names: ["sin"] },
1407
- { typeId: "cos", names: ["cos"] },
1408
- { typeId: "tan", names: ["tan"] },
1409
- { typeId: "power", names: ["power", "pow"] },
1410
- { typeId: "log", names: ["log"] },
1411
- { typeId: "clamp", names: ["clamp"] },
1412
- { typeId: "add", names: ["add"] },
1413
- { typeId: "multiply", names: ["multiply"] },
1414
- { typeId: "subtract", names: ["subtract"] },
1415
- { typeId: "divide", names: ["divide"] },
1416
- {
1417
- typeId: "greaterthan",
1418
- names: ["greaterthan", "gt"],
1432
+ var import_metadata = require("@vizij/node-graph-wasm/metadata");
1433
+ var FUNCTION_OVERRIDES = {
1434
+ case: {
1435
+ replaceInputs: [
1436
+ { id: "selector", optional: false, valueType: "any" },
1437
+ { id: "default", optional: false, valueType: "any" }
1438
+ ],
1439
+ variadic: { id: "operand", min: 1, max: null, valueType: "any" },
1440
+ minArgs: 3,
1441
+ maxArgs: null,
1442
+ params: []
1443
+ },
1444
+ slew: {
1445
+ dropTrailingInputs: 1,
1446
+ params: [
1447
+ {
1448
+ id: "max_rate",
1449
+ label: "max_rate",
1450
+ optional: false,
1451
+ valueType: "scalar"
1452
+ }
1453
+ ],
1419
1454
  minArgs: 2,
1420
1455
  maxArgs: 2
1456
+ }
1457
+ };
1458
+ var FUNCTION_CONFIGS = [
1459
+ {
1460
+ typeId: "sin",
1461
+ names: ["sin"],
1462
+ category: "math",
1463
+ description: "Sine of the input value (radians)."
1421
1464
  },
1422
- { typeId: "lessthan", names: ["lessthan", "lt"], minArgs: 2, maxArgs: 2 },
1423
- { typeId: "equal", names: ["equal", "eq"], minArgs: 2, maxArgs: 2 },
1424
1465
  {
1425
- typeId: "notequal",
1426
- names: ["notequal", "neq", "ne"],
1427
- minArgs: 2,
1428
- maxArgs: 2
1466
+ typeId: "cos",
1467
+ names: ["cos"],
1468
+ category: "math",
1469
+ description: "Cosine of the input value (radians)."
1429
1470
  },
1430
- { typeId: "and", names: ["and"], minArgs: 2, maxArgs: 2 },
1431
- { typeId: "or", names: ["or"], minArgs: 2, maxArgs: 2 },
1432
- { typeId: "xor", names: ["xor"], minArgs: 2, maxArgs: 2 },
1433
- { typeId: "not", names: ["not"], minArgs: 1, maxArgs: 1 },
1434
- { typeId: "if", names: ["if"], minArgs: 2, maxArgs: 3 },
1435
- { typeId: "time", names: ["time"], minArgs: 0, maxArgs: 0 },
1436
- { typeId: "oscillator", names: ["oscillator"], minArgs: 2, maxArgs: 2 }
1437
- ];
1438
- var SCALAR_FUNCTIONS = /* @__PURE__ */ new Map();
1439
- for (const config of FUNCTION_CONFIGS) {
1440
- const signature = (0, import_metadata2.requireNodeSignature)(config.typeId);
1441
- const signatureInputs = signature.inputs;
1442
- const inputs = signatureInputs.map((input) => ({
1443
- id: input.id,
1444
- optional: Boolean(input.optional)
1445
- }));
1446
- const variadic = signature.variadic_inputs ? {
1447
- id: signature.variadic_inputs.id,
1471
+ {
1472
+ typeId: "tan",
1473
+ names: ["tan"],
1474
+ category: "math",
1475
+ description: "Tangent of the input value (radians)."
1476
+ },
1477
+ {
1478
+ typeId: "power",
1479
+ names: ["power", "pow"],
1480
+ category: "math",
1481
+ description: "Raise a base value to an exponent."
1482
+ },
1483
+ {
1484
+ typeId: "log",
1485
+ names: ["log"],
1486
+ category: "math",
1487
+ description: "Logarithm of the input value."
1488
+ },
1489
+ {
1490
+ typeId: "clamp",
1491
+ names: ["clamp"],
1492
+ category: "utility",
1493
+ description: "Clamp an input between min/max."
1494
+ },
1495
+ {
1496
+ typeId: "remap",
1497
+ names: ["remap"],
1498
+ category: "utility",
1499
+ description: "Linearly map a value from the input range to the output range."
1500
+ },
1501
+ {
1502
+ typeId: "centered_remap",
1503
+ names: ["centeredremap", "centered_remap"],
1504
+ category: "utility",
1505
+ description: "Remap a value using in/out ranges that include explicit anchors."
1506
+ },
1507
+ {
1508
+ typeId: "piecewise_remap",
1509
+ names: ["piecewiseremap", "piecewise_remap"],
1510
+ category: "utility",
1511
+ description: "Map a value through piecewise-linear breakpoints."
1512
+ },
1513
+ {
1514
+ typeId: "abs",
1515
+ names: ["abs"],
1516
+ category: "math",
1517
+ description: "Absolute value of the input."
1518
+ },
1519
+ {
1520
+ typeId: "add",
1521
+ names: ["add"],
1522
+ category: "math",
1523
+ description: "Sum all operands together."
1524
+ },
1525
+ {
1526
+ typeId: "multiply",
1527
+ names: ["multiply"],
1528
+ category: "math",
1529
+ description: "Multiply operands together."
1530
+ },
1531
+ {
1532
+ typeId: "subtract",
1533
+ names: ["subtract"],
1534
+ category: "math",
1535
+ description: "Subtract rhs from lhs."
1536
+ },
1537
+ {
1538
+ typeId: "divide",
1539
+ names: ["divide"],
1540
+ category: "math",
1541
+ description: "Divide lhs by rhs."
1542
+ },
1543
+ {
1544
+ typeId: "min",
1545
+ names: ["min"],
1546
+ category: "math",
1547
+ description: "Minimum of all operands."
1548
+ },
1549
+ {
1550
+ typeId: "max",
1551
+ names: ["max"],
1552
+ category: "math",
1553
+ description: "Maximum of all operands."
1554
+ },
1555
+ {
1556
+ typeId: "modulo",
1557
+ names: ["modulo", "mod"],
1558
+ category: "math",
1559
+ description: "Modulo of lhs by rhs."
1560
+ },
1561
+ {
1562
+ typeId: "greaterthan",
1563
+ names: ["greaterthan", "gt"],
1564
+ minArgs: 2,
1565
+ maxArgs: 2,
1566
+ category: "logic",
1567
+ description: "Return true when lhs > rhs."
1568
+ },
1569
+ {
1570
+ typeId: "lessthan",
1571
+ names: ["lessthan", "lt"],
1572
+ minArgs: 2,
1573
+ maxArgs: 2,
1574
+ category: "logic",
1575
+ description: "Return true when lhs < rhs."
1576
+ },
1577
+ {
1578
+ typeId: "equal",
1579
+ names: ["equal", "eq"],
1580
+ minArgs: 2,
1581
+ maxArgs: 2,
1582
+ category: "logic",
1583
+ description: "Return true when operands are equal."
1584
+ },
1585
+ {
1586
+ typeId: "notequal",
1587
+ names: ["notequal", "neq", "ne"],
1588
+ minArgs: 2,
1589
+ maxArgs: 2,
1590
+ category: "logic",
1591
+ description: "Return true when operands differ."
1592
+ },
1593
+ {
1594
+ typeId: "and",
1595
+ names: ["and"],
1596
+ minArgs: 2,
1597
+ maxArgs: 2,
1598
+ category: "logic",
1599
+ description: "Logical AND."
1600
+ },
1601
+ {
1602
+ typeId: "or",
1603
+ names: ["or"],
1604
+ minArgs: 2,
1605
+ maxArgs: 2,
1606
+ category: "logic",
1607
+ description: "Logical OR."
1608
+ },
1609
+ {
1610
+ typeId: "xor",
1611
+ names: ["xor"],
1612
+ minArgs: 2,
1613
+ maxArgs: 2,
1614
+ category: "logic",
1615
+ description: "Logical XOR."
1616
+ },
1617
+ {
1618
+ typeId: "not",
1619
+ names: ["not"],
1620
+ minArgs: 1,
1621
+ maxArgs: 1,
1622
+ category: "logic",
1623
+ description: "Logical NOT of input."
1624
+ },
1625
+ {
1626
+ typeId: "if",
1627
+ names: ["if"],
1628
+ minArgs: 2,
1629
+ maxArgs: 3,
1630
+ category: "logic",
1631
+ description: "Conditional branch with optional fallback."
1632
+ },
1633
+ {
1634
+ typeId: "round",
1635
+ names: ["round"],
1636
+ category: "math",
1637
+ description: "Round input using the configured mode (floor/ceil/trunc)."
1638
+ },
1639
+ {
1640
+ typeId: "case",
1641
+ names: ["case"],
1642
+ minArgs: 3,
1643
+ category: "logic",
1644
+ description: "Route the selector value to matching labeled branches."
1645
+ },
1646
+ {
1647
+ typeId: "time",
1648
+ names: ["time"],
1649
+ minArgs: 0,
1650
+ maxArgs: 0,
1651
+ category: "time",
1652
+ description: "Graph time in seconds."
1653
+ },
1654
+ {
1655
+ typeId: "oscillator",
1656
+ names: ["oscillator"],
1657
+ minArgs: 2,
1658
+ maxArgs: 2,
1659
+ category: "time",
1660
+ description: "Sine/cosine oscillator driven by time."
1661
+ },
1662
+ {
1663
+ typeId: "spring",
1664
+ names: ["spring"],
1665
+ category: "time",
1666
+ description: "Spring toward the target with configurable stiffness and damping."
1667
+ },
1668
+ {
1669
+ typeId: "damp",
1670
+ names: ["damp"],
1671
+ category: "time",
1672
+ description: "Damp input values toward zero at the specified rate."
1673
+ },
1674
+ {
1675
+ typeId: "slew",
1676
+ names: ["slew"],
1677
+ category: "time",
1678
+ description: "Limit the rate of change between inputs over time."
1679
+ },
1680
+ {
1681
+ typeId: "default-blend",
1682
+ names: ["defaultblend", "blend"],
1683
+ category: "utility",
1684
+ description: "Blend operand values using optional baseline/offset and weight inputs."
1685
+ },
1686
+ {
1687
+ typeId: "blendweightedaverage",
1688
+ names: ["blendweightedaverage"],
1689
+ category: "utility",
1690
+ description: "Compute weighted average blends using aggregate sums."
1691
+ },
1692
+ {
1693
+ typeId: "blendadditive",
1694
+ names: ["blendadditive"],
1695
+ category: "utility",
1696
+ description: "Additive blending helper."
1697
+ },
1698
+ {
1699
+ typeId: "blendmultiply",
1700
+ names: ["blendmultiply"],
1701
+ category: "utility",
1702
+ description: "Multiplicative blending helper."
1703
+ },
1704
+ {
1705
+ typeId: "blendweightedoverlay",
1706
+ names: ["blendweightedoverlay"],
1707
+ category: "utility",
1708
+ description: "Overlay blending helper driven by weighted sums."
1709
+ },
1710
+ {
1711
+ typeId: "blendweightedaverageoverlay",
1712
+ names: ["blendweightedaverageoverlay"],
1713
+ category: "utility",
1714
+ description: "Overlay helper that adds averaged deltas to the base value."
1715
+ },
1716
+ {
1717
+ typeId: "blendmax",
1718
+ names: ["blendmax"],
1719
+ category: "utility",
1720
+ description: "Select the operand whose effective weight is largest."
1721
+ },
1722
+ {
1723
+ typeId: "vec3cross",
1724
+ names: ["vec3cross", "cross"],
1725
+ category: "vector",
1726
+ description: "Cross product of vectors A \xD7 B."
1727
+ },
1728
+ {
1729
+ typeId: "vectoradd",
1730
+ names: ["vectoradd", "vadd"],
1731
+ category: "vector",
1732
+ description: "Element-wise sum of vectors."
1733
+ },
1734
+ {
1735
+ typeId: "vectorsubtract",
1736
+ names: ["vectorsubtract", "vsub"],
1737
+ category: "vector",
1738
+ description: "Element-wise subtraction of vectors."
1739
+ },
1740
+ {
1741
+ typeId: "vectormultiply",
1742
+ names: ["vectormultiply", "vmul"],
1743
+ category: "vector",
1744
+ description: "Element-wise multiplication of vectors."
1745
+ },
1746
+ {
1747
+ typeId: "vectorscale",
1748
+ names: ["vectorscale", "vscale"],
1749
+ category: "vector",
1750
+ description: "Scale a vector by a scalar value."
1751
+ },
1752
+ {
1753
+ typeId: "vectornormalize",
1754
+ names: ["vectornormalize", "vnormalize"],
1755
+ category: "vector",
1756
+ description: "Normalize a vector to unit length."
1757
+ },
1758
+ {
1759
+ typeId: "vectordot",
1760
+ names: ["vectordot", "vdot"],
1761
+ category: "vector",
1762
+ description: "Dot product of vectors A \xB7 B."
1763
+ },
1764
+ {
1765
+ typeId: "vectorlength",
1766
+ names: ["vectorlength", "vlength"],
1767
+ category: "vector",
1768
+ description: "Euclidean length of a vector."
1769
+ },
1770
+ {
1771
+ typeId: "vectorindex",
1772
+ names: ["vectorindex", "vindex"],
1773
+ category: "vector",
1774
+ description: "Extract a component from a vector."
1775
+ },
1776
+ {
1777
+ typeId: "vectorconstant",
1778
+ names: ["vectorconstant", "vconst"],
1779
+ category: "vector",
1780
+ description: "Constant vector value."
1781
+ },
1782
+ {
1783
+ typeId: "join",
1784
+ names: ["join"],
1785
+ category: "vector",
1786
+ description: "Join scalar inputs into a vector."
1787
+ },
1788
+ {
1789
+ typeId: "split",
1790
+ names: ["split"],
1791
+ category: "vector",
1792
+ description: "Split a vector into scalar outputs."
1793
+ },
1794
+ {
1795
+ typeId: "vectormin",
1796
+ names: ["vectormin"],
1797
+ category: "vector",
1798
+ description: "Minimum component in a vector."
1799
+ },
1800
+ {
1801
+ typeId: "vectormax",
1802
+ names: ["vectormax"],
1803
+ category: "vector",
1804
+ description: "Maximum component in a vector."
1805
+ },
1806
+ {
1807
+ typeId: "vectormean",
1808
+ names: ["vectormean"],
1809
+ category: "vector",
1810
+ description: "Mean of the provided vector values."
1811
+ },
1812
+ {
1813
+ typeId: "vectormedian",
1814
+ names: ["vectormedian"],
1815
+ category: "vector",
1816
+ description: "Median of the provided vector values."
1817
+ },
1818
+ {
1819
+ typeId: "vectormode",
1820
+ names: ["vectormode"],
1821
+ category: "vector",
1822
+ description: "Mode of the provided vector values."
1823
+ },
1824
+ {
1825
+ typeId: "weightedsumvector",
1826
+ names: ["weightedsumvector", "vectorsum"],
1827
+ category: "vector",
1828
+ description: "Weighted sum across vectors with optional masks."
1829
+ }
1830
+ ];
1831
+ var SCALAR_FUNCTIONS = /* @__PURE__ */ new Map();
1832
+ var SCALAR_FUNCTION_VOCABULARY = [];
1833
+ for (const config of FUNCTION_CONFIGS) {
1834
+ const signature = (0, import_metadata.requireNodeSignature)(config.typeId);
1835
+ const signatureInputs = signature.inputs;
1836
+ let inputs = signatureInputs.map((input) => ({
1837
+ id: input.id,
1838
+ optional: Boolean(input.optional),
1839
+ valueType: inferExpressionValueType(input.ty)
1840
+ }));
1841
+ let variadic = signature.variadic_inputs ? {
1842
+ id: signature.variadic_inputs.id,
1448
1843
  min: signature.variadic_inputs.min,
1449
- max: signature.variadic_inputs.max ?? null
1844
+ max: typeof signature.variadic_inputs.max === "number" ? signature.variadic_inputs.max : null,
1845
+ valueType: inferExpressionValueType(signature.variadic_inputs.ty)
1450
1846
  } : null;
1451
- const derivedMin = variadic ? variadic.min : inputs.filter((input) => !input.optional).length;
1452
- const derivedMax = variadic ? variadic.max : inputs.length;
1453
- const minArgs = config.minArgs ?? derivedMin;
1454
- const maxArgs = config.maxArgs !== void 0 ? config.maxArgs : derivedMax ?? null;
1847
+ const baseRequiredInputs = inputs.filter((input) => !input.optional).length;
1848
+ const variadicMin = variadic?.min ?? 0;
1849
+ const derivedMin = baseRequiredInputs + variadicMin;
1850
+ let derivedMax;
1851
+ if (variadic) {
1852
+ derivedMax = variadic.max === null ? null : inputs.length + (variadic.max ?? 0);
1853
+ } else {
1854
+ derivedMax = inputs.length;
1855
+ }
1856
+ let params = buildParamArgumentSpecs(signature);
1857
+ const override = FUNCTION_OVERRIDES[config.typeId];
1858
+ if (override) {
1859
+ if (override.replaceInputs) {
1860
+ inputs = override.replaceInputs.map((input) => ({ ...input }));
1861
+ } else if (override.dropTrailingInputs) {
1862
+ const dropCount = Math.min(override.dropTrailingInputs, inputs.length);
1863
+ inputs = inputs.slice(0, inputs.length - dropCount);
1864
+ }
1865
+ if ("variadic" in override) {
1866
+ variadic = override.variadic ?? null;
1867
+ }
1868
+ if (override.params) {
1869
+ params = override.params.map((param) => ({ ...param }));
1870
+ }
1871
+ }
1872
+ const requiredParamCount = params.filter((param) => !param.optional).length;
1873
+ const minArgs = override?.minArgs !== void 0 ? override.minArgs : config.minArgs !== void 0 ? config.minArgs : derivedMin + requiredParamCount;
1874
+ const maxArgs = override?.maxArgs !== void 0 ? override.maxArgs : config.maxArgs !== void 0 ? config.maxArgs : derivedMax === null ? null : derivedMax + params.length;
1455
1875
  const definition = {
1456
1876
  nodeType: config.typeId,
1457
1877
  inputs,
1458
1878
  variadic,
1879
+ params,
1459
1880
  minArgs,
1460
- maxArgs
1881
+ maxArgs,
1882
+ resultValueType: inferExpressionValueType(
1883
+ signature.outputs?.[0]?.ty ?? null
1884
+ )
1461
1885
  };
1886
+ if (config.typeId === "if" || config.typeId === "case") {
1887
+ const adjustedInputs = definition.inputs.map(
1888
+ (input, index) => {
1889
+ if (config.typeId === "if" && index === 0) {
1890
+ return input;
1891
+ }
1892
+ return {
1893
+ ...input,
1894
+ valueType: "any"
1895
+ };
1896
+ }
1897
+ );
1898
+ definition.inputs = adjustedInputs;
1899
+ if (definition.variadic) {
1900
+ definition.variadic = {
1901
+ ...definition.variadic,
1902
+ valueType: "any"
1903
+ };
1904
+ }
1905
+ definition.resultValueType = "any";
1906
+ }
1462
1907
  const names = new Set(
1463
1908
  [config.typeId, ...config.names].map((name) => name.toLowerCase())
1464
1909
  );
1465
1910
  names.forEach((name) => {
1466
1911
  SCALAR_FUNCTIONS.set(name, definition);
1467
1912
  });
1913
+ const orderedNames = Array.from(names);
1914
+ const displayName = orderedNames[0] ?? config.typeId;
1915
+ SCALAR_FUNCTION_VOCABULARY.push({
1916
+ name: displayName,
1917
+ aliases: orderedNames.slice(1),
1918
+ nodeType: config.typeId,
1919
+ category: config.category ?? "math",
1920
+ description: config.description
1921
+ });
1922
+ }
1923
+ function buildParamArgumentSpecs(signature) {
1924
+ if (!signature || !Array.isArray(signature.params)) {
1925
+ return [];
1926
+ }
1927
+ const params = signature.params;
1928
+ return params.map((param) => ({
1929
+ id: param.id,
1930
+ label: param.label ?? param.id,
1931
+ doc: param.doc ?? void 0,
1932
+ optional: param.default_json !== void 0,
1933
+ valueType: inferExpressionValueType(param.ty),
1934
+ min: typeof param.min === "number" ? param.min : void 0,
1935
+ max: typeof param.max === "number" ? param.max : void 0
1936
+ }));
1937
+ }
1938
+ function inferExpressionValueType(ty) {
1939
+ if (!ty) {
1940
+ return "scalar";
1941
+ }
1942
+ const normalized = ty.toLowerCase();
1943
+ if (normalized === "any") {
1944
+ return "any";
1945
+ }
1946
+ if (normalized === "vector" || normalized === "vec2" || normalized === "vec3" || normalized === "vec4" || normalized === "quat" || normalized === "colorrgba" || normalized === "transform" || normalized === "array" || normalized === "list" || normalized === "record") {
1947
+ return "vector";
1948
+ }
1949
+ if (normalized === "bool" || normalized === "boolean") {
1950
+ return "boolean";
1951
+ }
1952
+ return "scalar";
1953
+ }
1954
+
1955
+ // src/expressionVariables.ts
1956
+ var DefaultExpressionVariableTable = class {
1957
+ constructor() {
1958
+ this.variables = /* @__PURE__ */ new Map();
1959
+ this.order = [];
1960
+ }
1961
+ registerSlotVariable(options) {
1962
+ this.upsert({
1963
+ name: options.name,
1964
+ kind: "slot",
1965
+ nodeId: options.nodeId,
1966
+ metadata: {
1967
+ slotId: options.slotId,
1968
+ slotAlias: options.slotAlias,
1969
+ inputId: options.inputId,
1970
+ targetId: options.targetId,
1971
+ animatableId: options.animatableId,
1972
+ component: options.component,
1973
+ valueType: options.valueType,
1974
+ remap: options.remap,
1975
+ autoRemap: options.autoRemap
1976
+ }
1977
+ });
1978
+ }
1979
+ registerReservedVariable(options) {
1980
+ this.upsert({
1981
+ name: options.name,
1982
+ kind: "reserved",
1983
+ nodeId: options.nodeId,
1984
+ description: options.description,
1985
+ metadata: {
1986
+ targetId: options.targetId,
1987
+ animatableId: options.animatableId,
1988
+ component: options.component
1989
+ }
1990
+ });
1991
+ }
1992
+ resolve(name) {
1993
+ return this.variables.get(name) ?? null;
1994
+ }
1995
+ resolveNodeId(name) {
1996
+ return this.variables.get(name)?.nodeId ?? null;
1997
+ }
1998
+ entries() {
1999
+ return this.order.map((name) => this.variables.get(name)).filter((entry) => Boolean(entry));
2000
+ }
2001
+ firstNodeId() {
2002
+ for (const name of this.order) {
2003
+ const nodeId = this.variables.get(name)?.nodeId;
2004
+ if (nodeId) {
2005
+ return nodeId;
2006
+ }
2007
+ }
2008
+ return null;
2009
+ }
2010
+ missing(names) {
2011
+ const missing = [];
2012
+ for (const name of names) {
2013
+ const entry = this.variables.get(name);
2014
+ if (!entry) {
2015
+ missing.push({ name, reason: "unknown" });
2016
+ continue;
2017
+ }
2018
+ if (!entry.nodeId) {
2019
+ missing.push({
2020
+ name,
2021
+ reason: "unresolved",
2022
+ entry
2023
+ });
2024
+ }
2025
+ }
2026
+ return missing;
2027
+ }
2028
+ upsert(entry) {
2029
+ if (!this.variables.has(entry.name)) {
2030
+ this.order.push(entry.name);
2031
+ }
2032
+ this.variables.set(entry.name, entry);
2033
+ }
2034
+ };
2035
+ function createExpressionVariableTable() {
2036
+ return new DefaultExpressionVariableTable();
2037
+ }
2038
+
2039
+ // src/expressionVocabulary.ts
2040
+ var RESERVED_EXPRESSION_VARIABLES = [
2041
+ {
2042
+ name: "self",
2043
+ description: "Output of the current binding.",
2044
+ scope: "binding",
2045
+ defaultValueType: "scalar",
2046
+ available: true
2047
+ },
2048
+ {
2049
+ name: "time",
2050
+ description: "Elapsed time in seconds since the rig started.",
2051
+ scope: "graph",
2052
+ defaultValueType: "scalar",
2053
+ available: true
2054
+ },
2055
+ {
2056
+ name: "deltaTime",
2057
+ description: "Seconds elapsed since the previous frame update.",
2058
+ scope: "graph",
2059
+ defaultValueType: "scalar",
2060
+ available: true
2061
+ },
2062
+ {
2063
+ name: "frame",
2064
+ description: "Current frame counter.",
2065
+ scope: "graph",
2066
+ defaultValueType: "scalar",
2067
+ available: true
2068
+ }
2069
+ ];
2070
+ var EXPRESSION_FUNCTION_VOCABULARY = SCALAR_FUNCTION_VOCABULARY;
2071
+
2072
+ // src/graphBuilder.ts
2073
+ var import_metadata2 = require("@vizij/node-graph-wasm/metadata");
2074
+
2075
+ // src/ir/builder.ts
2076
+ var graphSequence = 0;
2077
+ function generateGraphId(faceId) {
2078
+ graphSequence += 1;
2079
+ return `ir_${faceId}_${graphSequence}`;
2080
+ }
2081
+ var DefaultIrGraphBuilder = class {
2082
+ constructor(options) {
2083
+ this.nodes = [];
2084
+ this.edges = [];
2085
+ this.constants = [];
2086
+ this.issues = [];
2087
+ this.faceId = options.faceId;
2088
+ this.summary = {
2089
+ faceId: options.faceId,
2090
+ inputs: [],
2091
+ outputs: [],
2092
+ bindings: []
2093
+ };
2094
+ this.metadata = {
2095
+ source: options.source ?? "ir-builder",
2096
+ registryVersion: options.registryVersion,
2097
+ generatedAt: options.generatedAt,
2098
+ annotations: options.annotations
2099
+ };
2100
+ }
2101
+ addNode(node) {
2102
+ this.nodes.push(node);
2103
+ return node;
2104
+ }
2105
+ addEdge(edge) {
2106
+ this.edges.push(edge);
2107
+ return edge;
2108
+ }
2109
+ addConstant(constant) {
2110
+ this.constants.push(constant);
2111
+ return constant;
2112
+ }
2113
+ addIssue(issue) {
2114
+ this.issues.push(issue);
2115
+ return issue;
2116
+ }
2117
+ setSummary(summary) {
2118
+ this.summary = summary;
2119
+ }
2120
+ updateMetadata(metadata) {
2121
+ this.metadata = {
2122
+ ...this.metadata,
2123
+ ...metadata
2124
+ };
2125
+ }
2126
+ build() {
2127
+ return {
2128
+ id: generateGraphId(this.faceId),
2129
+ faceId: this.faceId,
2130
+ nodes: [...this.nodes],
2131
+ edges: [...this.edges],
2132
+ constants: [...this.constants],
2133
+ issues: [...this.issues],
2134
+ summary: { ...this.summary },
2135
+ metadata: { ...this.metadata }
2136
+ };
2137
+ }
2138
+ };
2139
+ function createIrGraphBuilder(options) {
2140
+ return new DefaultIrGraphBuilder(options);
2141
+ }
2142
+ function createLegacyIrGraph(payload) {
2143
+ const builder = createIrGraphBuilder({
2144
+ faceId: payload.faceId,
2145
+ registryVersion: payload.registryVersion,
2146
+ source: payload.source ?? "legacy-graph-builder",
2147
+ annotations: payload.annotations,
2148
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
2149
+ });
2150
+ builder.setSummary(payload.summary);
2151
+ (payload.issues ?? []).forEach((issue) => builder.addIssue(issue));
2152
+ const graph = builder.build();
2153
+ return {
2154
+ ...graph,
2155
+ legacy: { spec: payload.spec }
2156
+ };
2157
+ }
2158
+ function toIrBindingSummary(summaries) {
2159
+ return summaries.map((summary) => ({
2160
+ ...summary,
2161
+ remap: { ...summary.remap },
2162
+ issues: summary.issues ? [...summary.issues] : void 0
2163
+ }));
2164
+ }
2165
+
2166
+ // src/ir/compiler.ts
2167
+ function compileIrGraph(graph, options = {}) {
2168
+ const preferLegacy = options.preferLegacySpec === true;
2169
+ const legacySpec = graph.legacy?.spec;
2170
+ if (preferLegacy && legacySpec) {
2171
+ return {
2172
+ spec: legacySpec,
2173
+ issues: [...graph.issues]
2174
+ };
2175
+ }
2176
+ const convertedNodes = graph.nodes.map(convertIrNodeToNodeSpec);
2177
+ const existingNodeIds = new Set(convertedNodes.map((node) => node.id));
2178
+ graph.constants.forEach((constant) => {
2179
+ if (existingNodeIds.has(constant.id)) {
2180
+ return;
2181
+ }
2182
+ convertedNodes.push(convertIrConstantToNodeSpec(constant));
2183
+ });
2184
+ const convertedEdges = graph.edges.map(convertIrEdgeToGraphEdge);
2185
+ const spec = {
2186
+ nodes: convertedNodes,
2187
+ edges: convertedEdges.length > 0 ? convertedEdges : void 0
2188
+ };
2189
+ inlineSingleUseConstants(spec);
2190
+ const metadata = extractGraphSpecMetadata(graph);
2191
+ if (metadata !== void 0) {
2192
+ spec.metadata = metadata;
2193
+ }
2194
+ return {
2195
+ spec,
2196
+ issues: [...graph.issues]
2197
+ };
2198
+ }
2199
+ function convertIrNodeToNodeSpec(node) {
2200
+ const spec = {
2201
+ id: node.id,
2202
+ type: node.type
2203
+ };
2204
+ if (node.params) {
2205
+ spec.params = cloneJsonLike(node.params);
2206
+ }
2207
+ if (node.inputDefaults) {
2208
+ spec.input_defaults = cloneJsonLike(node.inputDefaults);
2209
+ }
2210
+ if (node.metadata) {
2211
+ spec.metadata = cloneJsonLike(node.metadata);
2212
+ }
2213
+ return spec;
2214
+ }
2215
+ function convertIrConstantToNodeSpec(constant) {
2216
+ const spec = {
2217
+ id: constant.id,
2218
+ type: "constant",
2219
+ params: {
2220
+ value: constant.value
2221
+ }
2222
+ };
2223
+ if (constant.metadata) {
2224
+ spec.metadata = cloneJsonLike(constant.metadata);
2225
+ }
2226
+ return spec;
2227
+ }
2228
+ function convertIrEdgeToGraphEdge(edge) {
2229
+ return {
2230
+ from: {
2231
+ node_id: edge.from.nodeId,
2232
+ output: edge.from.portId
2233
+ },
2234
+ to: {
2235
+ node_id: edge.to.nodeId,
2236
+ input: edge.to.portId
2237
+ }
2238
+ };
2239
+ }
2240
+ function extractGraphSpecMetadata(graph) {
2241
+ const annotations = graph.metadata.annotations;
2242
+ if (!annotations?.graphSpecMetadata) {
2243
+ return void 0;
2244
+ }
2245
+ return cloneJsonLike(annotations.graphSpecMetadata);
2246
+ }
2247
+ function cloneJsonLike(value) {
2248
+ if (value === void 0 || value === null) {
2249
+ return value;
2250
+ }
2251
+ return JSON.parse(JSON.stringify(value));
2252
+ }
2253
+ function inlineSingleUseConstants(spec) {
2254
+ const nodes = spec.nodes ?? [];
2255
+ const edges = spec.edges ? [...spec.edges] : [];
2256
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
2257
+ const constantUsage = /* @__PURE__ */ new Map();
2258
+ edges.forEach((edge) => {
2259
+ const source = nodeById.get(edge.from.node_id);
2260
+ if (source?.type === "constant") {
2261
+ constantUsage.set(source.id, (constantUsage.get(source.id) ?? 0) + 1);
2262
+ }
2263
+ });
2264
+ const updatedEdges = [];
2265
+ const constantsToRemove = /* @__PURE__ */ new Set();
2266
+ edges.forEach((edge) => {
2267
+ const source = nodeById.get(edge.from.node_id);
2268
+ if (source?.type === "constant" && constantUsage.get(source.id) === 1 && source.params && Object.prototype.hasOwnProperty.call(source.params, "value")) {
2269
+ const target = nodeById.get(edge.to.node_id);
2270
+ if (target) {
2271
+ const value = source.params.value;
2272
+ if (value !== void 0) {
2273
+ target.input_defaults = {
2274
+ ...target.input_defaults ?? {},
2275
+ [edge.to.input ?? "in"]: value
2276
+ };
2277
+ nodeById.set(target.id, target);
2278
+ constantsToRemove.add(source.id);
2279
+ return;
2280
+ }
2281
+ }
2282
+ }
2283
+ updatedEdges.push(edge);
2284
+ });
2285
+ spec.nodes = nodes.filter((node) => !constantsToRemove.has(node.id));
2286
+ spec.edges = updatedEdges.length > 0 ? updatedEdges : void 0;
2287
+ }
2288
+
2289
+ // src/bindingMetadata.ts
2290
+ function cloneOperand(entry) {
2291
+ return JSON.parse(JSON.stringify(entry));
2292
+ }
2293
+ function describeSlot(entry) {
2294
+ const metadata = entry.metadata;
2295
+ return {
2296
+ kind: "slot",
2297
+ ref: entry.name,
2298
+ alias: metadata?.slotAlias ?? entry.name,
2299
+ slotId: metadata?.slotId,
2300
+ inputId: metadata?.inputId ?? null,
2301
+ valueType: metadata?.valueType
2302
+ };
2303
+ }
2304
+ function describeReserved(entry) {
2305
+ return {
2306
+ kind: "reserved",
2307
+ ref: entry.name,
2308
+ description: entry.description
2309
+ };
2310
+ }
2311
+ function describeReference(node, variables) {
2312
+ const entry = variables.resolve(node.name);
2313
+ if (!entry) {
2314
+ return {
2315
+ kind: "unknown",
2316
+ ref: node.name
2317
+ };
2318
+ }
2319
+ if (entry.kind === "slot") {
2320
+ return describeSlot(entry);
2321
+ }
2322
+ if (entry.kind === "reserved") {
2323
+ return describeReserved(entry);
2324
+ }
2325
+ return {
2326
+ kind: "unknown",
2327
+ ref: entry.name
2328
+ };
2329
+ }
2330
+ function stringifyExpression(node) {
2331
+ switch (node.type) {
2332
+ case "Literal":
2333
+ return Number.isFinite(node.value) ? `${node.value}` : "0";
2334
+ case "VectorLiteral":
2335
+ return `vec(${node.values.join(", ")})`;
2336
+ case "Reference":
2337
+ return node.name;
2338
+ case "Unary":
2339
+ return `${node.operator}${stringifyExpression(node.operand)}`;
2340
+ case "Binary":
2341
+ return `${stringifyExpression(node.left)} ${node.operator} ${stringifyExpression(node.right)}`;
2342
+ case "Function":
2343
+ return `${node.name}(${node.args.map(stringifyExpression).join(", ")})`;
2344
+ default:
2345
+ return "";
2346
+ }
2347
+ }
2348
+ function describeOperand(node, variables) {
2349
+ switch (node.type) {
2350
+ case "Literal":
2351
+ return {
2352
+ kind: "literal",
2353
+ literalValue: node.value
2354
+ };
2355
+ case "Reference":
2356
+ return describeReference(node, variables);
2357
+ case "Unary":
2358
+ case "Binary":
2359
+ case "Function":
2360
+ case "VectorLiteral":
2361
+ return {
2362
+ kind: "expression",
2363
+ expression: stringifyExpression(node)
2364
+ };
2365
+ default:
2366
+ return { kind: "unknown" };
2367
+ }
2368
+ }
2369
+ function describeCaseExpression(node, variables) {
2370
+ if (node.type !== "Function") {
2371
+ return null;
2372
+ }
2373
+ if (node.name.toLowerCase() !== "case") {
2374
+ return null;
2375
+ }
2376
+ if ((node.args?.length ?? 0) < 3) {
2377
+ return null;
2378
+ }
2379
+ const [selector, defaultBranch, ...branches] = node.args;
2380
+ return {
2381
+ kind: "case",
2382
+ selector: describeOperand(selector, variables),
2383
+ defaultBranch: describeOperand(defaultBranch, variables),
2384
+ branches: branches.map((branch) => describeOperand(branch, variables))
2385
+ };
2386
+ }
2387
+ function buildBindingMetadataFromExpression(node, variables) {
2388
+ if (!node) {
2389
+ return void 0;
2390
+ }
2391
+ const caseMetadata = describeCaseExpression(node, variables);
2392
+ if (!caseMetadata) {
2393
+ return void 0;
2394
+ }
2395
+ return {
2396
+ expression: {
2397
+ case: {
2398
+ kind: "case",
2399
+ selector: caseMetadata.selector ? cloneOperand(caseMetadata.selector) : void 0,
2400
+ defaultBranch: caseMetadata.defaultBranch ? cloneOperand(caseMetadata.defaultBranch) : void 0,
2401
+ branches: caseMetadata.branches.map(cloneOperand)
2402
+ }
2403
+ }
2404
+ };
1468
2405
  }
1469
2406
 
1470
2407
  // src/graphBuilder.ts
@@ -1484,10 +2421,15 @@ function evaluateBinding({
1484
2421
  nodes,
1485
2422
  edges,
1486
2423
  constants: /* @__PURE__ */ new Map(),
1487
- counter: 0
2424
+ counter: 0,
2425
+ reservedNodes: /* @__PURE__ */ new Map(),
2426
+ nodeValueTypes: /* @__PURE__ */ new Map(),
2427
+ slotRemapNodes: /* @__PURE__ */ new Map(),
2428
+ graphReservedNodes: context.graphReservedNodes,
2429
+ generateReservedNodeId: context.generateReservedNodeId
1488
2430
  };
1489
2431
  const targetValueType = target.valueType === "vector" ? "vector" : "scalar";
1490
- const aliasNodes = /* @__PURE__ */ new Map();
2432
+ const variableTable = createExpressionVariableTable();
1491
2433
  const slotSummaries = [];
1492
2434
  const expressionIssues = [];
1493
2435
  const rawExpression = typeof binding.expression === "string" ? binding.expression : "";
@@ -1504,6 +2446,11 @@ function evaluateBinding({
1504
2446
  if (selfNodeId) {
1505
2447
  slotOutputId = selfNodeId;
1506
2448
  hasActiveSlot = true;
2449
+ setNodeValueType(
2450
+ exprContext,
2451
+ slotOutputId,
2452
+ slotValueType === "vector" ? "vector" : "scalar"
2453
+ );
1507
2454
  } else {
1508
2455
  expressionIssues.push("Self reference unavailable for this input.");
1509
2456
  slotOutputId = getConstantNodeId(exprContext, target.defaultValue);
@@ -1511,25 +2458,13 @@ function evaluateBinding({
1511
2458
  } else if (slot.inputId) {
1512
2459
  const inputNode = ensureInputNode(slot.inputId);
1513
2460
  if (inputNode) {
1514
- const remapNodeId = `remap_${safeId}_${sanitizeNodeId(slotId)}`;
1515
- nodes.push({
1516
- id: remapNodeId,
1517
- type: "centered_remap",
1518
- input_defaults: {
1519
- in_low: slot.remap.inLow,
1520
- in_anchor: slot.remap.inAnchor,
1521
- in_high: slot.remap.inHigh,
1522
- out_low: slot.remap.outLow,
1523
- out_anchor: slot.remap.outAnchor,
1524
- out_high: slot.remap.outHigh
1525
- }
1526
- });
1527
- edges.push({
1528
- from: { node_id: inputNode.nodeId },
1529
- to: { node_id: remapNodeId, input: "in" }
1530
- });
1531
- slotOutputId = remapNodeId;
2461
+ slotOutputId = inputNode.nodeId;
1532
2462
  hasActiveSlot = true;
2463
+ setNodeValueType(
2464
+ exprContext,
2465
+ slotOutputId,
2466
+ slotValueType === "vector" ? "vector" : "scalar"
2467
+ );
1533
2468
  } else {
1534
2469
  expressionIssues.push(`Missing standard input "${slot.inputId}".`);
1535
2470
  slotOutputId = getConstantNodeId(exprContext, 0);
@@ -1537,7 +2472,24 @@ function evaluateBinding({
1537
2472
  } else {
1538
2473
  slotOutputId = getConstantNodeId(exprContext, 0);
1539
2474
  }
1540
- aliasNodes.set(alias, slotOutputId);
2475
+ variableTable.registerSlotVariable({
2476
+ name: alias,
2477
+ nodeId: slotOutputId,
2478
+ slotId,
2479
+ slotAlias: alias,
2480
+ inputId: slot.inputId ?? null,
2481
+ targetId,
2482
+ animatableId,
2483
+ component,
2484
+ valueType: slotValueType,
2485
+ remap: { ...slot.remap },
2486
+ autoRemap: slot.inputId !== import_utils3.SELF_BINDING_ID && slotValueType === "scalar"
2487
+ });
2488
+ setNodeValueType(
2489
+ exprContext,
2490
+ slotOutputId,
2491
+ slotValueType === "vector" ? "vector" : "scalar"
2492
+ );
1541
2493
  slotSummaries.push({
1542
2494
  targetId,
1543
2495
  animatableId,
@@ -1547,12 +2499,32 @@ function evaluateBinding({
1547
2499
  inputId: slot.inputId ?? null,
1548
2500
  remap: { ...slot.remap },
1549
2501
  expression: trimmedExpression,
1550
- valueType: slotValueType
2502
+ valueType: slotValueType,
2503
+ nodeId: slotOutputId,
2504
+ expressionNodeId: slotOutputId
1551
2505
  });
1552
2506
  });
1553
2507
  if (slotSummaries.length === 0) {
1554
2508
  const alias = PRIMARY_SLOT_ALIAS;
1555
- aliasNodes.set(alias, getConstantNodeId(exprContext, 0));
2509
+ const constantId = getConstantNodeId(exprContext, 0);
2510
+ variableTable.registerSlotVariable({
2511
+ name: alias,
2512
+ nodeId: constantId,
2513
+ slotId: PRIMARY_SLOT_ID,
2514
+ slotAlias: alias,
2515
+ inputId: null,
2516
+ targetId,
2517
+ animatableId,
2518
+ component,
2519
+ valueType: targetValueType,
2520
+ remap: createDefaultRemap(target),
2521
+ autoRemap: false
2522
+ });
2523
+ setNodeValueType(
2524
+ exprContext,
2525
+ constantId,
2526
+ targetValueType === "vector" ? "vector" : "scalar"
2527
+ );
1556
2528
  slotSummaries.push({
1557
2529
  targetId,
1558
2530
  animatableId,
@@ -1562,26 +2534,51 @@ function evaluateBinding({
1562
2534
  inputId: null,
1563
2535
  remap: createDefaultRemap(target),
1564
2536
  expression: trimmedExpression,
1565
- valueType: targetValueType
2537
+ valueType: targetValueType,
2538
+ nodeId: constantId,
2539
+ expressionNodeId: constantId
1566
2540
  });
1567
2541
  }
2542
+ RESERVED_EXPRESSION_VARIABLES.forEach((reserved) => {
2543
+ if (reserved.available === false) {
2544
+ return;
2545
+ }
2546
+ let nodeId = null;
2547
+ if (reserved.name === "self") {
2548
+ nodeId = selfNodeId ?? null;
2549
+ } else {
2550
+ nodeId = ensureReservedVariableNode(reserved.name, exprContext);
2551
+ }
2552
+ variableTable.registerReservedVariable({
2553
+ name: reserved.name,
2554
+ nodeId,
2555
+ description: reserved.description,
2556
+ targetId: reserved.scope === "binding" ? targetId : void 0,
2557
+ animatableId: reserved.scope === "binding" ? animatableId : void 0,
2558
+ component: reserved.scope === "binding" ? component : void 0
2559
+ });
2560
+ });
1568
2561
  const defaultAlias = slotSummaries[0]?.slotAlias ?? PRIMARY_SLOT_ALIAS;
1569
2562
  const expressionText = trimmedExpression.length > 0 ? trimmedExpression : defaultAlias;
1570
2563
  const parseResult = parseControlExpression(expressionText);
1571
2564
  let expressionAst = null;
1572
2565
  if (parseResult.node && parseResult.errors.length === 0) {
1573
- const references = collectExpressionReferences(parseResult.node);
1574
- const missing = [];
1575
- references.forEach((ref) => {
1576
- if (!aliasNodes.has(ref)) {
1577
- missing.push(ref);
1578
- }
1579
- });
2566
+ const references = Array.from(
2567
+ collectExpressionReferences(parseResult.node)
2568
+ );
2569
+ const missing = variableTable.missing(references);
1580
2570
  if (missing.length === 0) {
1581
2571
  expressionAst = parseResult.node;
1582
2572
  } else {
1583
- missing.forEach((ref) => {
1584
- expressionIssues.push(`Unknown control "${ref}".`);
2573
+ validateLiteralParamArguments(parseResult.node, expressionIssues);
2574
+ missing.forEach((missingVar) => {
2575
+ if (missingVar.reason === "unresolved" && missingVar.entry?.kind === "reserved") {
2576
+ expressionIssues.push(
2577
+ `Reserved variable "${missingVar.name}" is unavailable for this binding.`
2578
+ );
2579
+ } else {
2580
+ expressionIssues.push(`Unknown control "${missingVar.name}".`);
2581
+ }
1585
2582
  });
1586
2583
  }
1587
2584
  } else {
@@ -1589,22 +2586,30 @@ function evaluateBinding({
1589
2586
  expressionIssues.push(error.message);
1590
2587
  });
1591
2588
  }
2589
+ const expressionMetadata = buildBindingMetadataFromExpression(
2590
+ expressionAst,
2591
+ variableTable
2592
+ );
1592
2593
  let valueNodeId = null;
1593
2594
  if (expressionAst) {
1594
2595
  valueNodeId = materializeExpression(
1595
2596
  expressionAst,
1596
2597
  exprContext,
1597
- aliasNodes,
2598
+ variableTable,
1598
2599
  expressionIssues
1599
2600
  );
1600
2601
  }
1601
2602
  if (!valueNodeId) {
1602
- const fallbackAlias = aliasNodes.has(defaultAlias) ? defaultAlias : aliasNodes.keys().next().value;
1603
- valueNodeId = (fallbackAlias ? aliasNodes.get(fallbackAlias) : void 0) ?? getConstantNodeId(exprContext, 0);
2603
+ const fallbackNodeId = variableTable.resolveNodeId(defaultAlias) ?? variableTable.firstNodeId();
2604
+ valueNodeId = fallbackNodeId ?? getConstantNodeId(exprContext, 0);
1604
2605
  }
1605
2606
  const issuesCopy = expressionIssues.length ? [...new Set(expressionIssues)] : void 0;
1606
2607
  slotSummaries.forEach((summary) => {
1607
2608
  summary.expression = expressionText;
2609
+ summary.expressionNodeId = valueNodeId;
2610
+ if (expressionMetadata) {
2611
+ summary.metadata = expressionMetadata;
2612
+ }
1608
2613
  if (issuesCopy && issuesCopy.length > 0) {
1609
2614
  summary.issues = issuesCopy;
1610
2615
  const issueSet = bindingIssues.get(summary.targetId) ?? /* @__PURE__ */ new Set();
@@ -1622,8 +2627,25 @@ function sanitizeNodeId(value) {
1622
2627
  return value.replace(/[^a-zA-Z0-9_]/g, "_");
1623
2628
  }
1624
2629
  function buildRigInputPath(faceId, inputPath) {
1625
- const trimmed = inputPath.startsWith("/") ? inputPath.slice(1) : inputPath;
1626
- return `rig/${faceId}/${trimmed}`;
2630
+ let trimmed = inputPath.startsWith("/") ? inputPath.slice(1) : inputPath;
2631
+ if (!trimmed) {
2632
+ return `rig/${faceId}`;
2633
+ }
2634
+ while (trimmed.startsWith("rig/")) {
2635
+ const segments = trimmed.split("/");
2636
+ if (segments.length >= 3) {
2637
+ const existingFaceId = segments[1];
2638
+ const remainder = segments.slice(2).join("/");
2639
+ if (existingFaceId === faceId) {
2640
+ return trimmed;
2641
+ }
2642
+ trimmed = remainder || "";
2643
+ } else {
2644
+ trimmed = segments.slice(1).join("/");
2645
+ }
2646
+ }
2647
+ const suffix = trimmed ? `/${trimmed}` : "";
2648
+ return `rig/${faceId}${suffix}`;
1627
2649
  }
1628
2650
  function getComponentOrder(animatable) {
1629
2651
  switch (animatable.type) {
@@ -1679,6 +2701,101 @@ function extractComponentDefault(value, component) {
1679
2701
  }
1680
2702
  return 0;
1681
2703
  }
2704
+ function setNodeValueType(context, nodeId, valueType) {
2705
+ context.nodeValueTypes.set(nodeId, valueType);
2706
+ }
2707
+ function getNodeValueType(context, nodeId) {
2708
+ return context.nodeValueTypes.get(nodeId) ?? "any";
2709
+ }
2710
+ function matchesValueType(actual, expected) {
2711
+ if (expected === "any" || actual === "any") {
2712
+ return true;
2713
+ }
2714
+ if (expected === "scalar") {
2715
+ return actual === "scalar" || actual === "boolean";
2716
+ }
2717
+ return actual === expected;
2718
+ }
2719
+ function ensureOperandValueType(context, operandId, expected, functionName, inputId, issues) {
2720
+ if (expected === "any") {
2721
+ return;
2722
+ }
2723
+ const actual = getNodeValueType(context, operandId);
2724
+ if (matchesValueType(actual, expected)) {
2725
+ return;
2726
+ }
2727
+ const expectation = expected === "vector" ? "a vector" : expected === "boolean" ? "a boolean" : "a scalar";
2728
+ issues.push(
2729
+ `Function "${functionName}" expects ${expectation} input for "${inputId}", but the expression produced ${actual}.`
2730
+ );
2731
+ }
2732
+ function resolveSlotReferenceNode(entry, fallbackNodeId, context) {
2733
+ if (entry.kind !== "slot") {
2734
+ return fallbackNodeId;
2735
+ }
2736
+ const metadata = entry.metadata;
2737
+ if (!metadata || metadata.autoRemap === false || !metadata.remap) {
2738
+ return fallbackNodeId;
2739
+ }
2740
+ if (metadata.valueType !== "scalar") {
2741
+ return fallbackNodeId;
2742
+ }
2743
+ if (isIdentityRemap(metadata.remap)) {
2744
+ return fallbackNodeId;
2745
+ }
2746
+ const cacheKey = metadata.slotId ?? entry.name;
2747
+ const existing = context.slotRemapNodes.get(cacheKey);
2748
+ if (existing) {
2749
+ return existing;
2750
+ }
2751
+ const remapNodeId = createSlotRemapNode(
2752
+ context,
2753
+ fallbackNodeId,
2754
+ metadata.remap,
2755
+ metadata.slotAlias ?? cacheKey
2756
+ );
2757
+ context.slotRemapNodes.set(cacheKey, remapNodeId);
2758
+ return remapNodeId;
2759
+ }
2760
+ function createSlotRemapNode(context, sourceNodeId, remap, slotKey) {
2761
+ const safeSlotKey = sanitizeNodeId(slotKey || "slot");
2762
+ const nodeId = `slot_remap_${safeSlotKey}_${context.counter++}`;
2763
+ context.nodes.push({
2764
+ id: nodeId,
2765
+ type: "centered_remap",
2766
+ inputDefaults: {
2767
+ in_low: remap.inLow,
2768
+ in_anchor: remap.inAnchor,
2769
+ in_high: remap.inHigh,
2770
+ out_low: remap.outLow,
2771
+ out_anchor: remap.outAnchor,
2772
+ out_high: remap.outHigh
2773
+ }
2774
+ });
2775
+ context.edges.push({
2776
+ from: { nodeId: sourceNodeId },
2777
+ to: { nodeId, portId: "in" }
2778
+ });
2779
+ setNodeValueType(context, nodeId, "scalar");
2780
+ return nodeId;
2781
+ }
2782
+ function isIdentityRemap(remap) {
2783
+ return nearlyEqual(remap.inLow, remap.outLow) && nearlyEqual(remap.inAnchor, remap.outAnchor) && nearlyEqual(remap.inHigh, remap.outHigh);
2784
+ }
2785
+ function nearlyEqual(a, b, epsilon = 1e-4) {
2786
+ return Math.abs(a - b) <= epsilon;
2787
+ }
2788
+ var REMAP_FUNCTION_NODE_TYPES = /* @__PURE__ */ new Set([
2789
+ "piecewise_remap",
2790
+ "centered_remap",
2791
+ "remap"
2792
+ ]);
2793
+ function shouldSkipAutoRemapForArgument(nodeType, index) {
2794
+ if (!REMAP_FUNCTION_NODE_TYPES.has(nodeType)) {
2795
+ return false;
2796
+ }
2797
+ return index === 0;
2798
+ }
1682
2799
  function getConstantNodeId(context, value) {
1683
2800
  const key = Number.isFinite(value) ? value.toString() : "NaN";
1684
2801
  const existing = context.constants.get(key);
@@ -1694,6 +2811,30 @@ function getConstantNodeId(context, value) {
1694
2811
  }
1695
2812
  });
1696
2813
  context.constants.set(key, nodeId);
2814
+ setNodeValueType(context, nodeId, "scalar");
2815
+ return nodeId;
2816
+ }
2817
+ function getVectorConstantNodeId(context, values) {
2818
+ const normalized = values.map(
2819
+ (value) => Number.isFinite(value) ? value : 0
2820
+ );
2821
+ const key = `vector:${normalized.join(",")}`;
2822
+ const existing = context.constants.get(key);
2823
+ if (existing) {
2824
+ return existing;
2825
+ }
2826
+ const nodeId = `const_${context.componentSafeId}_${context.constants.size + 1}`;
2827
+ context.nodes.push({
2828
+ id: nodeId,
2829
+ type: "constant",
2830
+ params: {
2831
+ value: {
2832
+ vector: normalized
2833
+ }
2834
+ }
2835
+ });
2836
+ context.constants.set(key, nodeId);
2837
+ setNodeValueType(context, nodeId, "vector");
1697
2838
  return nodeId;
1698
2839
  }
1699
2840
  function createBinaryOperationNode(context, operator, leftId, rightId) {
@@ -1702,15 +2843,16 @@ function createBinaryOperationNode(context, operator, leftId, rightId) {
1702
2843
  id: nodeId,
1703
2844
  type: operator
1704
2845
  });
2846
+ setNodeValueType(context, nodeId, "scalar");
1705
2847
  const leftInput = operator === "subtract" || operator === "divide" ? "lhs" : "operand_1";
1706
2848
  const rightInput = operator === "subtract" || operator === "divide" ? "rhs" : "operand_2";
1707
2849
  context.edges.push({
1708
- from: { node_id: leftId },
1709
- to: { node_id: nodeId, input: leftInput }
2850
+ from: { nodeId: leftId },
2851
+ to: { nodeId, portId: leftInput }
1710
2852
  });
1711
2853
  context.edges.push({
1712
- from: { node_id: rightId },
1713
- to: { node_id: nodeId, input: rightInput }
2854
+ from: { nodeId: rightId },
2855
+ to: { nodeId, portId: rightInput }
1714
2856
  });
1715
2857
  return nodeId;
1716
2858
  }
@@ -1726,95 +2868,339 @@ function createVariadicOperationNode(context, operator, operandIds) {
1726
2868
  id: nodeId,
1727
2869
  type: operator
1728
2870
  });
2871
+ setNodeValueType(context, nodeId, "scalar");
1729
2872
  operandIds.forEach((operandId, index) => {
1730
2873
  context.edges.push({
1731
- from: { node_id: operandId },
1732
- to: { node_id: nodeId, input: `operand_${index + 1}` }
2874
+ from: { nodeId: operandId },
2875
+ to: { nodeId, portId: `operand_${index + 1}` }
1733
2876
  });
1734
2877
  });
1735
2878
  return nodeId;
1736
2879
  }
1737
- function createNamedOperationNode(context, operator, inputNames, operandIds) {
2880
+ function createNamedOperationNode(context, operator, inputNames, operandIds, resultType = "scalar", params) {
1738
2881
  const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1739
- context.nodes.push({
2882
+ const node = {
1740
2883
  id: nodeId,
1741
2884
  type: operator
1742
- });
2885
+ };
2886
+ if (params && Object.keys(params).length > 0) {
2887
+ node.params = params;
2888
+ }
2889
+ context.nodes.push(node);
2890
+ setNodeValueType(context, nodeId, resultType);
1743
2891
  inputNames.forEach((inputName, index) => {
1744
2892
  const operandId = operandIds[index];
1745
2893
  context.edges.push({
1746
- from: { node_id: operandId },
1747
- to: { node_id: nodeId, input: inputName }
2894
+ from: { nodeId: operandId },
2895
+ to: { nodeId, portId: inputName }
1748
2896
  });
1749
2897
  });
1750
2898
  return nodeId;
1751
2899
  }
1752
- function emitScalarFunctionNode(definition, operands, context) {
2900
+ function ensureReservedVariableNode(name, context) {
2901
+ const existing = context.reservedNodes.get(name);
2902
+ if (existing) {
2903
+ return existing;
2904
+ }
2905
+ if (name === "time" || name === "deltaTime" || name === "frame") {
2906
+ const nodeId = ensureGraphTimeNode(context);
2907
+ context.reservedNodes.set(name, nodeId);
2908
+ return nodeId;
2909
+ }
2910
+ return null;
2911
+ }
2912
+ function ensureGraphTimeNode(context) {
2913
+ const existing = context.graphReservedNodes.get("time");
2914
+ if (existing) {
2915
+ return existing;
2916
+ }
2917
+ const nodeId = context.generateReservedNodeId("time");
2918
+ context.nodes.push({
2919
+ id: nodeId,
2920
+ type: "time",
2921
+ metadata: {
2922
+ reservedVariable: "time"
2923
+ }
2924
+ });
2925
+ setNodeValueType(context, nodeId, "scalar");
2926
+ context.graphReservedNodes.set("time", nodeId);
2927
+ return nodeId;
2928
+ }
2929
+ function emitScalarFunctionNode(definition, operands, argNodes, totalArgCount, context, variables, issues, paramArgOverride) {
2930
+ if (definition.nodeType === "case") {
2931
+ return emitCaseFunctionNode(
2932
+ definition,
2933
+ operands,
2934
+ argNodes,
2935
+ context,
2936
+ variables,
2937
+ issues
2938
+ );
2939
+ }
2940
+ const orderedOperands = operands.slice(0, definition.inputs.length);
2941
+ definition.inputs.forEach((input, index) => {
2942
+ const operandId = orderedOperands[index];
2943
+ if (!operandId) {
2944
+ return;
2945
+ }
2946
+ ensureOperandValueType(
2947
+ context,
2948
+ operandId,
2949
+ input.valueType,
2950
+ definition.nodeType,
2951
+ input.id,
2952
+ issues
2953
+ );
2954
+ });
2955
+ const availableAfterInputs = Math.max(
2956
+ 0,
2957
+ totalArgCount - definition.inputs.length
2958
+ );
2959
+ const paramArgCount = definition.params.length > 0 ? Math.min(definition.params.length, availableAfterInputs) : 0;
2960
+ const variadicArgCount = definition.variadic ? Math.max(0, availableAfterInputs - paramArgCount) : 0;
2961
+ const variadicOperands = definition.variadic ? operands.slice(
2962
+ definition.inputs.length,
2963
+ definition.inputs.length + variadicArgCount
2964
+ ) : [];
1753
2965
  if (definition.variadic) {
1754
- const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
1755
- context.nodes.push({
1756
- id: nodeId,
1757
- type: definition.nodeType
2966
+ variadicOperands.forEach((operandId, _variadicIndex) => {
2967
+ ensureOperandValueType(
2968
+ context,
2969
+ operandId,
2970
+ definition.variadic.valueType,
2971
+ definition.nodeType,
2972
+ definition.variadic.id,
2973
+ issues
2974
+ );
2975
+ });
2976
+ }
2977
+ const paramArgStart = definition.inputs.length + variadicArgCount;
2978
+ const paramArgNodes = paramArgCount > 0 ? paramArgOverride ?? argNodes.slice(paramArgStart, paramArgStart + paramArgCount) : [];
2979
+ const isSlewDebugEnabled = typeof process !== "undefined" && process?.env && process.env.DEBUG_SLEW === "1";
2980
+ if (isSlewDebugEnabled && definition.nodeType === "slew") {
2981
+ console.log("slew-debug", {
2982
+ totalArgs: totalArgCount,
2983
+ inputs: definition.inputs.length,
2984
+ paramCount: definition.params.length,
2985
+ paramArgCount,
2986
+ paramArgNodesTypes: paramArgNodes.map((node) => node?.type ?? null)
1758
2987
  });
1759
- operands.forEach((operandId, index) => {
2988
+ }
2989
+ const nodeParams = buildParamAssignments(definition, paramArgNodes, issues);
2990
+ if (definition.variadic && definition.inputs.length === 0) {
2991
+ const nodeId2 = `expr_${context.componentSafeId}_${context.counter++}`;
2992
+ const node = {
2993
+ id: nodeId2,
2994
+ type: definition.nodeType
2995
+ };
2996
+ if (nodeParams) {
2997
+ node.params = nodeParams;
2998
+ }
2999
+ context.nodes.push(node);
3000
+ variadicOperands.forEach((operandId, index) => {
1760
3001
  context.edges.push({
1761
- from: { node_id: operandId },
3002
+ from: { nodeId: operandId },
1762
3003
  to: {
1763
- node_id: nodeId,
1764
- input: `${definition.variadic.id}_${index + 1}`
3004
+ nodeId: nodeId2,
3005
+ portId: `${definition.variadic.id}_${index + 1}`
1765
3006
  }
1766
3007
  });
1767
3008
  });
1768
- return nodeId;
3009
+ setNodeValueType(context, nodeId2, definition.resultValueType);
3010
+ return nodeId2;
1769
3011
  }
1770
3012
  const providedNames = [];
1771
3013
  const providedOperands = [];
1772
3014
  definition.inputs.forEach((input, index) => {
1773
- const operandId = operands[index];
3015
+ const operandId = orderedOperands[index];
1774
3016
  if (!operandId) {
1775
3017
  return;
1776
3018
  }
1777
3019
  providedNames.push(input.id);
1778
3020
  providedOperands.push(operandId);
1779
3021
  });
1780
- return createNamedOperationNode(
3022
+ const nodeId = createNamedOperationNode(
3023
+ context,
3024
+ definition.nodeType,
3025
+ providedNames,
3026
+ providedOperands,
3027
+ definition.resultValueType,
3028
+ nodeParams ?? void 0
3029
+ );
3030
+ if (definition.variadic) {
3031
+ variadicOperands.forEach((operandId, index) => {
3032
+ context.edges.push({
3033
+ from: { nodeId: operandId },
3034
+ to: {
3035
+ nodeId,
3036
+ portId: `${definition.variadic.id}_${index + 1}`
3037
+ }
3038
+ });
3039
+ });
3040
+ }
3041
+ return nodeId;
3042
+ }
3043
+ function emitCaseFunctionNode(definition, operands, argNodes, context, variables, issues) {
3044
+ const selectorId = operands[0];
3045
+ const defaultId = operands[1];
3046
+ const branchOperands = operands.slice(2);
3047
+ const branchArgs = argNodes.slice(2);
3048
+ if (!selectorId || branchOperands.length === 0) {
3049
+ issues.push(
3050
+ 'Function "case" requires a selector, default, and at least one branch.'
3051
+ );
3052
+ return getConstantNodeId(context, 0);
3053
+ }
3054
+ const nodeId = `expr_${context.componentSafeId}_${context.counter++}`;
3055
+ const caseLabels = branchOperands.map((_, index) => {
3056
+ const extracted = extractCaseLabel(branchArgs[index], variables);
3057
+ if (extracted) {
3058
+ return extracted;
3059
+ }
3060
+ const fallback = `case_${index + 1}`;
3061
+ issues.push(
3062
+ `Case branch ${index + 1} is missing an alias; generated fallback label ${fallback}.`
3063
+ );
3064
+ return fallback;
3065
+ });
3066
+ context.nodes.push({
3067
+ id: nodeId,
3068
+ type: definition.nodeType,
3069
+ params: caseLabels.length > 0 ? { case_labels: caseLabels } : void 0
3070
+ });
3071
+ ensureOperandValueType(
1781
3072
  context,
3073
+ selectorId,
3074
+ definition.inputs[0]?.valueType ?? "any",
1782
3075
  definition.nodeType,
1783
- providedNames,
1784
- providedOperands
3076
+ definition.inputs[0]?.id ?? "selector",
3077
+ issues
1785
3078
  );
3079
+ context.edges.push({
3080
+ from: { nodeId: selectorId },
3081
+ to: { nodeId, portId: definition.inputs[0]?.id ?? "selector" }
3082
+ });
3083
+ if (defaultId) {
3084
+ ensureOperandValueType(
3085
+ context,
3086
+ defaultId,
3087
+ definition.inputs[1]?.valueType ?? "any",
3088
+ definition.nodeType,
3089
+ "default",
3090
+ issues
3091
+ );
3092
+ context.edges.push({
3093
+ from: { nodeId: defaultId },
3094
+ to: { nodeId, portId: "default" }
3095
+ });
3096
+ }
3097
+ branchOperands.forEach((operandId, index) => {
3098
+ const portId = `${definition.variadic?.id ?? "operand"}_${index + 1}`;
3099
+ ensureOperandValueType(
3100
+ context,
3101
+ operandId,
3102
+ definition.variadic?.valueType ?? "any",
3103
+ definition.nodeType,
3104
+ portId,
3105
+ issues
3106
+ );
3107
+ context.edges.push({
3108
+ from: { nodeId: operandId },
3109
+ to: { nodeId, portId }
3110
+ });
3111
+ });
3112
+ setNodeValueType(context, nodeId, "any");
3113
+ return nodeId;
1786
3114
  }
1787
- function applyBindingOperators(operators, baseNodeId, safeId, nodes, edges) {
1788
- let currentNodeId = baseNodeId;
1789
- operators.forEach((operator, index) => {
1790
- if (!operator.enabled) {
3115
+ function buildParamAssignments(definition, paramArgNodes, issues) {
3116
+ if (!definition.params.length || paramArgNodes.length === 0) {
3117
+ return null;
3118
+ }
3119
+ const assignments = {};
3120
+ paramArgNodes.forEach((node, index) => {
3121
+ const spec = definition.params[index];
3122
+ if (!spec) {
1791
3123
  return;
1792
3124
  }
1793
- let definition;
1794
- try {
1795
- definition = getBindingOperatorDefinition(operator.type);
1796
- } catch {
1797
- return;
3125
+ const literal = extractParamLiteral(
3126
+ node,
3127
+ spec,
3128
+ definition.nodeType,
3129
+ issues
3130
+ );
3131
+ if (literal !== null) {
3132
+ assignments[spec.id] = literal;
1798
3133
  }
1799
- const nodeId = `${operator.type}_${safeId}_${index + 1}`;
1800
- const params = {};
1801
- definition.params.forEach((param) => {
1802
- const configured = operator.params?.[param.id];
1803
- params[param.id] = typeof configured === "number" ? configured : param.defaultValue;
1804
- });
1805
- nodes.push({
1806
- id: nodeId,
1807
- type: definition.nodeType,
1808
- params
1809
- });
1810
- const inputId = definition.inputs[0] ?? "in";
1811
- edges.push({
1812
- from: { node_id: currentNodeId },
1813
- to: { node_id: nodeId, input: inputId }
1814
- });
1815
- currentNodeId = nodeId;
1816
3134
  });
1817
- return currentNodeId;
3135
+ return Object.keys(assignments).length > 0 ? assignments : null;
3136
+ }
3137
+ function extractParamLiteral(node, spec, functionName, issues) {
3138
+ if (spec.valueType === "vector") {
3139
+ if (node.type !== "VectorLiteral") {
3140
+ issues.push(
3141
+ `Function "${functionName}" requires a literal vector for "${spec.id}".`
3142
+ );
3143
+ return null;
3144
+ }
3145
+ if (!Array.isArray(node.values) || node.values.length === 0) {
3146
+ issues.push(
3147
+ `Function "${functionName}" requires at least one value for "${spec.id}".`
3148
+ );
3149
+ return null;
3150
+ }
3151
+ return node.values.map((value) => clampScalarParamValue(value, spec));
3152
+ }
3153
+ if (node.type !== "Literal") {
3154
+ issues.push(
3155
+ `Function "${functionName}" requires a literal ${describeParamExpectation(spec.valueType)} for "${spec.id}".`
3156
+ );
3157
+ return null;
3158
+ }
3159
+ const numeric = Number(node.value);
3160
+ if (!Number.isFinite(numeric)) {
3161
+ issues.push(
3162
+ `Function "${functionName}" requires a finite ${describeParamExpectation(spec.valueType)} for "${spec.id}".`
3163
+ );
3164
+ return null;
3165
+ }
3166
+ const clamped = clampScalarParamValue(numeric, spec);
3167
+ if (spec.valueType === "boolean") {
3168
+ return clamped !== 0;
3169
+ }
3170
+ return clamped;
3171
+ }
3172
+ function clampScalarParamValue(value, spec) {
3173
+ let next = Number.isFinite(value) ? value : 0;
3174
+ if (typeof spec.min === "number" && next < spec.min) {
3175
+ next = spec.min;
3176
+ }
3177
+ if (typeof spec.max === "number" && next > spec.max) {
3178
+ next = spec.max;
3179
+ }
3180
+ return next;
3181
+ }
3182
+ function describeParamExpectation(valueType) {
3183
+ switch (valueType) {
3184
+ case "vector":
3185
+ return "vector";
3186
+ case "boolean":
3187
+ return "boolean";
3188
+ default:
3189
+ return "scalar";
3190
+ }
3191
+ }
3192
+ function extractCaseLabel(node, variables) {
3193
+ if (!node || node.type !== "Reference") {
3194
+ return null;
3195
+ }
3196
+ const entry = variables.resolve(node.name);
3197
+ if (entry && entry.metadata && "slotAlias" in entry.metadata) {
3198
+ const alias = entry.metadata.slotAlias?.trim();
3199
+ if (alias && alias.length > 0) {
3200
+ return alias;
3201
+ }
3202
+ }
3203
+ return node.name;
1818
3204
  }
1819
3205
  function collectOperands(node, operator, target) {
1820
3206
  if (node.type === "Binary" && node.operator === operator) {
@@ -1824,6 +3210,42 @@ function collectOperands(node, operator, target) {
1824
3210
  }
1825
3211
  target.push(node);
1826
3212
  }
3213
+ function validateLiteralParamArguments(node, issues) {
3214
+ if (node.type === "Function") {
3215
+ const definition = SCALAR_FUNCTIONS.get(node.name.toLowerCase());
3216
+ if (definition && definition.params.length > 0) {
3217
+ const totalArgCount = node.args.length;
3218
+ const availableAfterInputs = Math.max(
3219
+ 0,
3220
+ totalArgCount - definition.inputs.length
3221
+ );
3222
+ const paramArgCount = Math.min(
3223
+ definition.params.length,
3224
+ availableAfterInputs
3225
+ );
3226
+ const variadicArgCount = definition.variadic ? Math.max(0, availableAfterInputs - paramArgCount) : 0;
3227
+ const paramArgStart = definition.inputs.length + variadicArgCount;
3228
+ for (let index = 0; index < paramArgCount; index++) {
3229
+ const spec = definition.params[index];
3230
+ const paramNode = node.args[paramArgStart + index];
3231
+ if (!spec || !paramNode) {
3232
+ continue;
3233
+ }
3234
+ extractParamLiteral(paramNode, spec, node.name, issues);
3235
+ }
3236
+ }
3237
+ node.args.forEach((child) => validateLiteralParamArguments(child, issues));
3238
+ return;
3239
+ }
3240
+ if (node.type === "Unary") {
3241
+ validateLiteralParamArguments(node.operand, issues);
3242
+ return;
3243
+ }
3244
+ if (node.type === "Binary") {
3245
+ validateLiteralParamArguments(node.left, issues);
3246
+ validateLiteralParamArguments(node.right, issues);
3247
+ }
3248
+ }
1827
3249
  var BINARY_FUNCTION_OPERATOR_MAP = {
1828
3250
  ">": "greaterthan",
1829
3251
  "<": "lessthan",
@@ -1832,24 +3254,32 @@ var BINARY_FUNCTION_OPERATOR_MAP = {
1832
3254
  "&&": "and",
1833
3255
  "||": "or"
1834
3256
  };
1835
- function materializeExpression(node, context, aliasNodes, issues) {
3257
+ function materializeExpression(node, context, variables, issues, options) {
3258
+ const autoRemap = options?.autoRemap !== false;
1836
3259
  switch (node.type) {
1837
3260
  case "Literal": {
1838
3261
  return getConstantNodeId(context, node.value);
1839
3262
  }
3263
+ case "VectorLiteral": {
3264
+ return getVectorConstantNodeId(context, node.values);
3265
+ }
1840
3266
  case "Reference": {
1841
- const mapped = aliasNodes.get(node.name);
1842
- if (!mapped) {
3267
+ const entry = variables.resolve(node.name);
3268
+ if (!entry) {
1843
3269
  issues.push(`Unknown control "${node.name}".`);
1844
3270
  return getConstantNodeId(context, 0);
1845
3271
  }
1846
- return mapped;
3272
+ const mappedId = entry.nodeId ?? getConstantNodeId(context, 0);
3273
+ if (!autoRemap || entry.kind !== "slot") {
3274
+ return mappedId;
3275
+ }
3276
+ return resolveSlotReferenceNode(entry, mappedId, context);
1847
3277
  }
1848
3278
  case "Unary": {
1849
3279
  const operandId = materializeExpression(
1850
3280
  node.operand,
1851
3281
  context,
1852
- aliasNodes,
3282
+ variables,
1853
3283
  issues
1854
3284
  );
1855
3285
  switch (node.operator) {
@@ -1868,7 +3298,15 @@ function materializeExpression(node, context, aliasNodes, issues) {
1868
3298
  issues.push('Function "not" is not available in metadata.');
1869
3299
  return getConstantNodeId(context, 0);
1870
3300
  }
1871
- return emitScalarFunctionNode(definition, [operandId], context);
3301
+ return emitScalarFunctionNode(
3302
+ definition,
3303
+ [operandId],
3304
+ [node.operand],
3305
+ 1,
3306
+ context,
3307
+ variables,
3308
+ issues
3309
+ );
1872
3310
  }
1873
3311
  default:
1874
3312
  issues.push("Unsupported unary operator.");
@@ -1881,7 +3319,7 @@ function materializeExpression(node, context, aliasNodes, issues) {
1881
3319
  const children = [];
1882
3320
  collectOperands(node, "+", children);
1883
3321
  const operandIds = children.map(
1884
- (child) => materializeExpression(child, context, aliasNodes, issues)
3322
+ (child) => materializeExpression(child, context, variables, issues)
1885
3323
  );
1886
3324
  return createVariadicOperationNode(context, "add", operandIds);
1887
3325
  }
@@ -1889,20 +3327,20 @@ function materializeExpression(node, context, aliasNodes, issues) {
1889
3327
  const children = [];
1890
3328
  collectOperands(node, "*", children);
1891
3329
  const operandIds = children.map(
1892
- (child) => materializeExpression(child, context, aliasNodes, issues)
3330
+ (child) => materializeExpression(child, context, variables, issues)
1893
3331
  );
1894
3332
  return createVariadicOperationNode(context, "multiply", operandIds);
1895
3333
  }
1896
3334
  const leftId = materializeExpression(
1897
3335
  node.left,
1898
3336
  context,
1899
- aliasNodes,
3337
+ variables,
1900
3338
  issues
1901
3339
  );
1902
3340
  const rightId = materializeExpression(
1903
3341
  node.right,
1904
3342
  context,
1905
- aliasNodes,
3343
+ variables,
1906
3344
  issues
1907
3345
  );
1908
3346
  if (operator === "-") {
@@ -1918,7 +3356,15 @@ function materializeExpression(node, context, aliasNodes, issues) {
1918
3356
  issues.push(`Function "${mappedFunction}" is not available.`);
1919
3357
  return getConstantNodeId(context, 0);
1920
3358
  }
1921
- return emitScalarFunctionNode(definition, [leftId, rightId], context);
3359
+ return emitScalarFunctionNode(
3360
+ definition,
3361
+ [leftId, rightId],
3362
+ [node.left, node.right],
3363
+ 2,
3364
+ context,
3365
+ variables,
3366
+ issues
3367
+ );
1922
3368
  }
1923
3369
  issues.push(`Unsupported operator "${operator}".`);
1924
3370
  return getConstantNodeId(context, 0);
@@ -1931,22 +3377,61 @@ function materializeExpression(node, context, aliasNodes, issues) {
1931
3377
  issues.push(`Unknown function "${name}".`);
1932
3378
  return getConstantNodeId(context, 0);
1933
3379
  }
1934
- const operands = node.args.map(
1935
- (arg) => materializeExpression(arg, context, aliasNodes, issues)
1936
- );
1937
- if (operands.length < definition.minArgs) {
3380
+ const argNodes = node.args;
3381
+ const argCount = argNodes.length;
3382
+ if (argCount < definition.minArgs) {
1938
3383
  issues.push(
1939
- `Function "${name}" expects at least ${definition.minArgs} arguments, received ${operands.length}.`
3384
+ `Function "${name}" expects at least ${definition.minArgs} arguments, received ${argCount}.`
1940
3385
  );
1941
3386
  return getConstantNodeId(context, 0);
1942
3387
  }
1943
- if (definition.maxArgs !== null && operands.length > definition.maxArgs) {
3388
+ if (definition.maxArgs !== null && argCount > definition.maxArgs) {
1944
3389
  issues.push(
1945
- `Function "${name}" expects at most ${definition.maxArgs} arguments, received ${operands.length}.`
3390
+ `Function "${name}" expects at most ${definition.maxArgs} arguments, received ${argCount}.`
1946
3391
  );
1947
3392
  return getConstantNodeId(context, 0);
1948
3393
  }
1949
- return emitScalarFunctionNode(definition, operands, context);
3394
+ if (name === "slew") {
3395
+ const maxRateArg = argNodes[1];
3396
+ if (!maxRateArg || maxRateArg.type !== "Literal") {
3397
+ issues.push(
3398
+ 'Function "slew" requires a literal scalar for "max_rate".'
3399
+ );
3400
+ }
3401
+ }
3402
+ const availableAfterInputs = Math.max(
3403
+ 0,
3404
+ argCount - definition.inputs.length
3405
+ );
3406
+ const paramArgCount = definition.params.length > 0 ? Math.min(definition.params.length, availableAfterInputs) : 0;
3407
+ const variadicArgCount = definition.variadic ? Math.max(0, availableAfterInputs - paramArgCount) : 0;
3408
+ const operandLimit = Math.min(
3409
+ argCount,
3410
+ definition.inputs.length + variadicArgCount
3411
+ );
3412
+ const operandNodes = argNodes.slice(0, operandLimit);
3413
+ const operands = operandNodes.map(
3414
+ (arg, index) => materializeExpression(
3415
+ arg,
3416
+ context,
3417
+ variables,
3418
+ issues,
3419
+ shouldSkipAutoRemapForArgument(definition.nodeType, index) ? { autoRemap: false } : void 0
3420
+ )
3421
+ );
3422
+ const paramArgStart = operandLimit;
3423
+ const paramArgNodes = paramArgCount > 0 ? argNodes.slice(paramArgStart, paramArgStart + paramArgCount) : [];
3424
+ const paramOverride = definition.nodeType === "case" ? void 0 : paramArgNodes;
3425
+ return emitScalarFunctionNode(
3426
+ definition,
3427
+ operands,
3428
+ argNodes,
3429
+ argCount,
3430
+ context,
3431
+ variables,
3432
+ issues,
3433
+ paramOverride
3434
+ );
1950
3435
  }
1951
3436
  default: {
1952
3437
  issues.push("Unsupported expression node.");
@@ -1960,10 +3445,25 @@ function buildRigGraphSpec({
1960
3445
  components,
1961
3446
  bindings,
1962
3447
  inputsById,
1963
- inputBindings
3448
+ inputBindings,
3449
+ inputMetadata
1964
3450
  }) {
3451
+ const metadataByInputId = inputMetadata ?? /* @__PURE__ */ new Map();
3452
+ const irBuilder = createIrGraphBuilder({
3453
+ faceId,
3454
+ registryVersion: import_metadata2.nodeRegistryVersion,
3455
+ source: "graphBuilder",
3456
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
3457
+ });
1965
3458
  const nodes = [];
1966
3459
  const edges = [];
3460
+ const graphReservedNodes = /* @__PURE__ */ new Map();
3461
+ const reservedNodeCounters = /* @__PURE__ */ new Map();
3462
+ const generateReservedNodeId = (kind) => {
3463
+ const nextValue = (reservedNodeCounters.get(kind) ?? 0) + 1;
3464
+ reservedNodeCounters.set(kind, nextValue);
3465
+ return `reserved_${kind}_${nextValue}`;
3466
+ };
1967
3467
  const inputNodes = /* @__PURE__ */ new Map();
1968
3468
  const buildingDerived = /* @__PURE__ */ new Set();
1969
3469
  const computedInputs = /* @__PURE__ */ new Set();
@@ -2019,7 +3519,9 @@ function buildRigGraphSpec({
2019
3519
  edges,
2020
3520
  ensureInputNode,
2021
3521
  bindingIssues,
2022
- summaryBindings
3522
+ summaryBindings,
3523
+ graphReservedNodes,
3524
+ generateReservedNodeId
2023
3525
  },
2024
3526
  selfNodeId
2025
3527
  });
@@ -2099,7 +3601,9 @@ function buildRigGraphSpec({
2099
3601
  edges,
2100
3602
  ensureInputNode,
2101
3603
  bindingIssues,
2102
- summaryBindings
3604
+ summaryBindings,
3605
+ graphReservedNodes,
3606
+ generateReservedNodeId
2103
3607
  }
2104
3608
  });
2105
3609
  valueNodeId = producedNodeId;
@@ -2118,7 +3622,9 @@ function buildRigGraphSpec({
2118
3622
  remap: createDefaultRemap(target),
2119
3623
  expression: PRIMARY_SLOT_ALIAS,
2120
3624
  valueType: target.valueType === "vector" ? "vector" : "scalar",
2121
- issues: ["Binding not found."]
3625
+ issues: ["Binding not found."],
3626
+ nodeId: PRIMARY_SLOT_ID,
3627
+ expressionNodeId: PRIMARY_SLOT_ID
2122
3628
  });
2123
3629
  const fallbackIssues = bindingIssues.get(component.id) ?? /* @__PURE__ */ new Set();
2124
3630
  fallbackIssues.add("Binding not found.");
@@ -2128,15 +3634,7 @@ function buildRigGraphSpec({
2128
3634
  entry.defaults.set(key, component.defaultValue);
2129
3635
  return;
2130
3636
  }
2131
- const operatorList = binding ? binding.operators ?? [] : [];
2132
- const finalNodeId = applyBindingOperators(
2133
- operatorList,
2134
- valueNodeId,
2135
- component.safeId,
2136
- nodes,
2137
- edges
2138
- );
2139
- entry.values.set(key, finalNodeId);
3637
+ entry.values.set(key, valueNodeId);
2140
3638
  });
2141
3639
  inputsById.forEach((_input, inputId) => {
2142
3640
  ensureInputNode(inputId);
@@ -2162,8 +3660,8 @@ function buildRigGraphSpec({
2162
3660
  }
2163
3661
  });
2164
3662
  edges.push({
2165
- from: { node_id: valueNodeId },
2166
- to: { node_id: outputNodeId2, input: "in" }
3663
+ from: { nodeId: valueNodeId },
3664
+ to: { nodeId: outputNodeId2, portId: "in" }
2167
3665
  });
2168
3666
  return;
2169
3667
  }
@@ -2190,8 +3688,8 @@ function buildRigGraphSpec({
2190
3688
  sourceId = constNodeId;
2191
3689
  }
2192
3690
  edges.push({
2193
- from: { node_id: sourceId },
2194
- to: { node_id: joinNodeId, input: `operand_${index + 1}` }
3691
+ from: { nodeId: sourceId },
3692
+ to: { nodeId: joinNodeId, portId: `operand_${index + 1}` }
2195
3693
  });
2196
3694
  });
2197
3695
  const outputNodeId = `out_${safeId}`;
@@ -2203,8 +3701,8 @@ function buildRigGraphSpec({
2203
3701
  }
2204
3702
  });
2205
3703
  edges.push({
2206
- from: { node_id: joinNodeId },
2207
- to: { node_id: outputNodeId, input: "in" }
3704
+ from: { nodeId: joinNodeId },
3705
+ to: { nodeId: outputNodeId, portId: "in" }
2208
3706
  });
2209
3707
  });
2210
3708
  const nodeById = /* @__PURE__ */ new Map();
@@ -2213,7 +3711,7 @@ function buildRigGraphSpec({
2213
3711
  });
2214
3712
  const constantUsage = /* @__PURE__ */ new Map();
2215
3713
  edges.forEach((edge) => {
2216
- const source = nodeById.get(edge.from.node_id);
3714
+ const source = nodeById.get(edge.from.nodeId);
2217
3715
  if (source?.type === "constant") {
2218
3716
  constantUsage.set(source.id, (constantUsage.get(source.id) ?? 0) + 1);
2219
3717
  }
@@ -2221,15 +3719,16 @@ function buildRigGraphSpec({
2221
3719
  const updatedEdges = [];
2222
3720
  const constantsToRemove = /* @__PURE__ */ new Set();
2223
3721
  edges.forEach((edge) => {
2224
- const source = nodeById.get(edge.from.node_id);
3722
+ const source = nodeById.get(edge.from.nodeId);
2225
3723
  if (source?.type === "constant" && constantUsage.get(source.id) === 1 && source.params && Object.prototype.hasOwnProperty.call(source.params, "value")) {
2226
- const target = nodeById.get(edge.to.node_id);
3724
+ const target = nodeById.get(edge.to.nodeId);
2227
3725
  if (target) {
2228
3726
  const value = source.params.value;
2229
3727
  if (value !== void 0) {
2230
- target.input_defaults = {
2231
- ...target.input_defaults ?? {},
2232
- [edge.to.input]: value
3728
+ const targetPort = edge.to.portId ?? "in";
3729
+ target.inputDefaults = {
3730
+ ...target.inputDefaults ?? {},
3731
+ [targetPort]: value
2233
3732
  };
2234
3733
  nodeById.set(target.id, target);
2235
3734
  constantsToRemove.add(source.id);
@@ -2246,18 +3745,17 @@ function buildRigGraphSpec({
2246
3745
  const filteredSummaryBindings = summaryBindings.filter(
2247
3746
  (binding) => outputs.has(binding.animatableId) || computedInputs.has(binding.animatableId)
2248
3747
  );
2249
- const spec = {
2250
- nodes: filteredNodes,
2251
- edges: updatedEdges.length ? updatedEdges : void 0
2252
- };
2253
- const baseSpec = spec;
2254
- const specWithMetadata = {
2255
- ...baseSpec,
2256
- metadata: {
2257
- ...baseSpec.metadata,
2258
- vizij: {
2259
- faceId,
2260
- inputs: Array.from(inputsById.values()).map((input) => ({
3748
+ const vizijMetadata = {
3749
+ vizij: {
3750
+ faceId,
3751
+ inputs: Array.from(inputsById.values()).map((input) => {
3752
+ const meta = metadataByInputId.get(input.id);
3753
+ const derivedRoot = meta?.root ?? input.group;
3754
+ let derivedSource = meta?.source;
3755
+ if (!derivedSource && input.path.startsWith("/standard/")) {
3756
+ derivedSource = "preset";
3757
+ }
3758
+ const entry = {
2261
3759
  id: input.id,
2262
3760
  path: input.path,
2263
3761
  sourceId: input.sourceId,
@@ -2268,15 +3766,23 @@ function buildRigGraphSpec({
2268
3766
  min: input.range.min,
2269
3767
  max: input.range.max
2270
3768
  }
2271
- })),
2272
- bindings: filteredSummaryBindings.map((binding) => ({
2273
- ...binding,
2274
- remap: { ...binding.remap },
2275
- expression: binding.expression,
2276
- valueType: binding.valueType,
2277
- issues: binding.issues ? [...binding.issues] : void 0
2278
- }))
2279
- }
3769
+ };
3770
+ if (derivedSource) {
3771
+ entry.source = derivedSource;
3772
+ }
3773
+ if (derivedRoot) {
3774
+ entry.root = derivedRoot;
3775
+ }
3776
+ return entry;
3777
+ }),
3778
+ bindings: filteredSummaryBindings.map((binding) => ({
3779
+ ...binding,
3780
+ remap: { ...binding.remap },
3781
+ expression: binding.expression,
3782
+ valueType: binding.valueType,
3783
+ issues: binding.issues ? [...binding.issues] : void 0,
3784
+ metadata: binding.metadata ? cloneJsonLike2(binding.metadata) : void 0
3785
+ }))
2280
3786
  }
2281
3787
  };
2282
3788
  const issuesByTarget = {};
@@ -2291,29 +3797,149 @@ function buildRigGraphSpec({
2291
3797
  issues.forEach((issue) => fatalIssues.add(issue));
2292
3798
  });
2293
3799
  remapDefaultIssues.forEach((issue) => fatalIssues.add(issue));
3800
+ const summaryPayload = {
3801
+ faceId,
3802
+ inputs: Array.from(inputNodes.values()).map(
3803
+ ({ input }) => buildRigInputPath(faceId, input.path)
3804
+ ),
3805
+ outputs: [...dynamicOutputs, ...computedInputList],
3806
+ bindings: filteredSummaryBindings
3807
+ };
3808
+ const irSummary = {
3809
+ faceId: summaryPayload.faceId,
3810
+ inputs: [...summaryPayload.inputs],
3811
+ outputs: [...summaryPayload.outputs],
3812
+ bindings: toIrBindingSummary(
3813
+ filteredSummaryBindings.map((binding) => ({
3814
+ ...binding,
3815
+ remap: { ...binding.remap },
3816
+ issues: binding.issues ? [...binding.issues] : void 0
3817
+ }))
3818
+ )
3819
+ };
3820
+ const fatalIssueList = Array.from(fatalIssues);
3821
+ const irIssues = createIrIssuesFromLegacy(issuesByTarget, fatalIssueList);
3822
+ filteredNodes.forEach((node) => {
3823
+ irBuilder.addNode(cloneIrNode(node));
3824
+ const constant = extractIrConstantFromNode(node);
3825
+ if (constant) {
3826
+ irBuilder.addConstant(constant);
3827
+ }
3828
+ });
3829
+ updatedEdges.forEach((edge) => {
3830
+ irBuilder.addEdge(cloneIrEdge(edge));
3831
+ });
3832
+ irBuilder.setSummary(irSummary);
3833
+ irIssues.forEach((issue) => irBuilder.addIssue(issue));
3834
+ irBuilder.updateMetadata({
3835
+ annotations: {
3836
+ graphSpecMetadata: cloneJsonLike2(vizijMetadata)
3837
+ }
3838
+ });
3839
+ const irGraph = irBuilder.build();
3840
+ const compiled = compileIrGraph(irGraph, { preferLegacySpec: false });
2294
3841
  return {
2295
- spec: specWithMetadata,
2296
- summary: {
2297
- faceId,
2298
- inputs: Array.from(inputNodes.values()).map(
2299
- ({ input }) => buildRigInputPath(faceId, input.path)
2300
- ),
2301
- outputs: [...dynamicOutputs, ...computedInputList],
2302
- bindings: filteredSummaryBindings
2303
- },
3842
+ spec: compiled.spec,
3843
+ summary: summaryPayload,
2304
3844
  issues: {
2305
3845
  byTarget: issuesByTarget,
2306
- fatal: Array.from(fatalIssues)
3846
+ fatal: fatalIssueList
3847
+ },
3848
+ ir: {
3849
+ graph: irGraph,
3850
+ compile: (options) => compileIrGraph(irGraph, options)
3851
+ }
3852
+ };
3853
+ }
3854
+ function createIrIssuesFromLegacy(issuesByTarget, fatalIssues) {
3855
+ const fatalSet = new Set(fatalIssues);
3856
+ const collected = [];
3857
+ let counter = 0;
3858
+ Object.entries(issuesByTarget).forEach(([targetId, messages]) => {
3859
+ messages.forEach((message) => {
3860
+ counter += 1;
3861
+ collected.push({
3862
+ id: `issue_${counter}`,
3863
+ severity: fatalSet.has(message) ? "error" : "warning",
3864
+ message,
3865
+ targetId,
3866
+ tags: fatalSet.has(message) ? ["fatal"] : void 0
3867
+ });
3868
+ });
3869
+ });
3870
+ fatalSet.forEach((message) => {
3871
+ const alreadyCaptured = collected.some(
3872
+ (issue) => issue.message === message
3873
+ );
3874
+ if (alreadyCaptured) {
3875
+ return;
2307
3876
  }
3877
+ counter += 1;
3878
+ collected.push({
3879
+ id: `issue_${counter}`,
3880
+ severity: "error",
3881
+ message,
3882
+ tags: ["fatal"]
3883
+ });
3884
+ });
3885
+ return collected;
3886
+ }
3887
+ function cloneIrNode(node) {
3888
+ return {
3889
+ id: node.id,
3890
+ type: node.type,
3891
+ category: node.category,
3892
+ label: node.label,
3893
+ description: node.description,
3894
+ params: cloneJsonLike2(node.params),
3895
+ inputDefaults: cloneJsonLike2(node.inputDefaults),
3896
+ metadata: cloneJsonLike2(node.metadata)
3897
+ };
3898
+ }
3899
+ function cloneIrEdge(edge) {
3900
+ return {
3901
+ id: edge.id,
3902
+ from: {
3903
+ nodeId: edge.from.nodeId,
3904
+ portId: edge.from.portId,
3905
+ component: edge.from.component
3906
+ },
3907
+ to: {
3908
+ nodeId: edge.to.nodeId,
3909
+ portId: edge.to.portId,
3910
+ component: edge.to.component
3911
+ },
3912
+ metadata: cloneJsonLike2(edge.metadata)
3913
+ };
3914
+ }
3915
+ function extractIrConstantFromNode(node) {
3916
+ if (node.type !== "constant") {
3917
+ return null;
3918
+ }
3919
+ const value = node.params?.value;
3920
+ if (typeof value !== "number" || !Number.isFinite(value)) {
3921
+ return null;
3922
+ }
3923
+ return {
3924
+ id: node.id,
3925
+ value,
3926
+ valueType: "scalar",
3927
+ metadata: cloneJsonLike2(node.metadata)
2308
3928
  };
2309
3929
  }
3930
+ function cloneJsonLike2(value) {
3931
+ if (value === void 0 || value === null) {
3932
+ return value;
3933
+ }
3934
+ return JSON.parse(JSON.stringify(value));
3935
+ }
2310
3936
  function validateRemapDefaults(nodes) {
2311
3937
  const issues = [];
2312
3938
  nodes.forEach((node) => {
2313
3939
  if (node.type !== "centered_remap") {
2314
3940
  return;
2315
3941
  }
2316
- const defaults = node.input_defaults ?? {};
3942
+ const defaults = node.inputDefaults ?? {};
2317
3943
  [
2318
3944
  "in_low",
2319
3945
  "in_anchor",
@@ -2330,38 +3956,444 @@ function validateRemapDefaults(nodes) {
2330
3956
  });
2331
3957
  return issues;
2332
3958
  }
3959
+
3960
+ // src/ir/inspection.ts
3961
+ var import_metadata3 = require("@vizij/node-graph-wasm/metadata");
3962
+ var MACHINE_REPORT_VERSION = 1;
3963
+ var REMAP_KEYS = [
3964
+ "inLow",
3965
+ "inAnchor",
3966
+ "inHigh",
3967
+ "outLow",
3968
+ "outAnchor",
3969
+ "outHigh"
3970
+ ];
3971
+ var DEFAULT_DIFF_LIMIT = 50;
3972
+ function buildMachineReport(result) {
3973
+ return {
3974
+ reportVersion: MACHINE_REPORT_VERSION,
3975
+ faceId: result.summary.faceId,
3976
+ summary: normalizeSummary(result.summary),
3977
+ issues: normalizeIssues(result.issues),
3978
+ irGraph: result.ir?.graph ? normalizeIrGraph(result.ir.graph) : void 0
3979
+ };
3980
+ }
3981
+ function diffMachineReports(actual, expected, options) {
3982
+ const limit = normalizeDiffLimit(options?.limit);
3983
+ const ctx = {
3984
+ differences: [],
3985
+ limit,
3986
+ limitReached: false
3987
+ };
3988
+ diffValues(actual, expected, "$", ctx);
3989
+ return {
3990
+ equal: ctx.differences.length === 0,
3991
+ differences: ctx.differences,
3992
+ limitReached: ctx.limitReached
3993
+ };
3994
+ }
3995
+ function normalizeSummary(summary) {
3996
+ return {
3997
+ faceId: summary.faceId,
3998
+ inputs: [...summary.inputs].sort(),
3999
+ outputs: [...summary.outputs].sort(),
4000
+ bindings: normalizeGraphBindingSummaries(summary.bindings)
4001
+ };
4002
+ }
4003
+ function normalizeGraphBindingSummaries(bindings) {
4004
+ const normalized = bindings.map((binding) => ({
4005
+ targetId: binding.targetId,
4006
+ animatableId: binding.animatableId,
4007
+ component: binding.component ?? void 0,
4008
+ slotId: binding.slotId,
4009
+ slotAlias: binding.slotAlias,
4010
+ inputId: binding.inputId ?? null,
4011
+ remap: normalizeRemap2(binding.remap),
4012
+ expression: binding.expression,
4013
+ valueType: binding.valueType,
4014
+ nodeId: binding.nodeId,
4015
+ expressionNodeId: binding.expressionNodeId,
4016
+ issues: normalizeStringArray(binding.issues),
4017
+ metadata: cloneBindingMetadata2(binding.metadata)
4018
+ }));
4019
+ normalized.sort((a, b) => bindingSortKey(a).localeCompare(bindingSortKey(b)));
4020
+ return normalized;
4021
+ }
4022
+ function cloneBindingMetadata2(metadata) {
4023
+ if (!metadata) {
4024
+ return void 0;
4025
+ }
4026
+ return JSON.parse(JSON.stringify(metadata));
4027
+ }
4028
+ function normalizeIssues(issues) {
4029
+ const byTargetEntries = Object.entries(issues.byTarget ?? {}).map(
4030
+ ([targetId, messages]) => [targetId, [...messages].sort()]
4031
+ );
4032
+ byTargetEntries.sort(([a], [b]) => a.localeCompare(b));
4033
+ const byTarget = {};
4034
+ byTargetEntries.forEach(([targetId, messages]) => {
4035
+ byTarget[targetId] = messages;
4036
+ });
4037
+ return {
4038
+ fatal: [...issues.fatal].sort(),
4039
+ byTarget
4040
+ };
4041
+ }
4042
+ function normalizeIrGraph(graph) {
4043
+ return {
4044
+ id: graph.id,
4045
+ faceId: graph.faceId,
4046
+ nodes: normalizeIrNodes(graph.nodes),
4047
+ edges: normalizeIrEdges(graph.edges),
4048
+ constants: normalizeIrConstants(graph.constants),
4049
+ issues: normalizeIrIssues(graph.issues),
4050
+ summary: normalizeIrGraphSummary(graph.summary),
4051
+ metadata: normalizeIrMetadata(graph.metadata)
4052
+ };
4053
+ }
4054
+ function normalizeIrNodes(nodes) {
4055
+ return [...nodes].map((node) => ({
4056
+ ...node,
4057
+ inputDefaults: node.inputDefaults ? sortPlainObject(node.inputDefaults) : void 0,
4058
+ params: node.params ? sortPlainObject(node.params) : void 0,
4059
+ metadata: node.metadata ? sortPlainObject(node.metadata) : void 0,
4060
+ annotations: buildNodeAnnotations(node)
4061
+ })).sort((a, b) => a.id.localeCompare(b.id));
4062
+ }
4063
+ function normalizeIrEdges(edges) {
4064
+ return [...edges].map((edge) => ({
4065
+ ...edge,
4066
+ from: normalizePortRef(edge.from),
4067
+ to: normalizePortRef(edge.to),
4068
+ metadata: edge.metadata ? sortPlainObject(edge.metadata) : void 0
4069
+ })).sort((a, b) => edgeSortKey(a).localeCompare(edgeSortKey(b)));
4070
+ }
4071
+ function normalizePortRef(edgeRef) {
4072
+ return {
4073
+ nodeId: edgeRef.nodeId,
4074
+ portId: edgeRef.portId ?? void 0,
4075
+ component: edgeRef.component ?? void 0
4076
+ };
4077
+ }
4078
+ function edgeSortKey(edge) {
4079
+ if (edge.id) {
4080
+ return edge.id;
4081
+ }
4082
+ const fromPort = edge.from.portId ?? "";
4083
+ const toPort = edge.to.portId ?? "";
4084
+ return `${edge.from.nodeId}:${fromPort}->${edge.to.nodeId}:${toPort}`;
4085
+ }
4086
+ function normalizeIrConstants(constants) {
4087
+ return [...constants].map((constant) => ({
4088
+ ...constant,
4089
+ metadata: constant.metadata ? sortPlainObject(constant.metadata) : void 0
4090
+ })).sort((a, b) => a.id.localeCompare(b.id));
4091
+ }
4092
+ function normalizeIrIssues(issues) {
4093
+ return [...issues].map((issue) => ({
4094
+ ...issue,
4095
+ tags: normalizeStringArray(issue.tags),
4096
+ details: issue.details ? sortPlainObject(issue.details) : void 0
4097
+ })).sort((a, b) => a.id.localeCompare(b.id));
4098
+ }
4099
+ function normalizeIrGraphSummary(summary) {
4100
+ return {
4101
+ faceId: summary.faceId,
4102
+ inputs: [...summary.inputs].sort(),
4103
+ outputs: [...summary.outputs].sort(),
4104
+ bindings: normalizeIrBindingSummaries(summary.bindings)
4105
+ };
4106
+ }
4107
+ function normalizeIrBindingSummaries(bindings) {
4108
+ const normalized = bindings.map((binding) => ({
4109
+ ...binding,
4110
+ remap: sortPlainObject(binding.remap),
4111
+ issues: normalizeStringArray(binding.issues)
4112
+ }));
4113
+ normalized.sort((a, b) => bindingSortKey(a).localeCompare(bindingSortKey(b)));
4114
+ return normalized;
4115
+ }
4116
+ function normalizeIrMetadata(metadata) {
4117
+ const normalized = {
4118
+ source: metadata.source ?? "unknown"
4119
+ };
4120
+ if (metadata.registryVersion) {
4121
+ normalized.registryVersion = metadata.registryVersion;
4122
+ }
4123
+ if (metadata.annotations) {
4124
+ const sorted = sortPlainObject(metadata.annotations);
4125
+ if (Object.keys(sorted).length > 0) {
4126
+ normalized.annotations = sorted;
4127
+ }
4128
+ }
4129
+ return normalized;
4130
+ }
4131
+ function buildNodeAnnotations(node) {
4132
+ const signature = findRegistrySignature(node.type);
4133
+ if (!signature) {
4134
+ return void 0;
4135
+ }
4136
+ return {
4137
+ registry: normalizeRegistrySignature(signature)
4138
+ };
4139
+ }
4140
+ function findRegistrySignature(typeId) {
4141
+ try {
4142
+ const signature = (0, import_metadata3.findNodeSignature)(typeId);
4143
+ if (!signature) {
4144
+ return void 0;
4145
+ }
4146
+ return signature;
4147
+ } catch {
4148
+ return void 0;
4149
+ }
4150
+ }
4151
+ function normalizeRegistrySignature(signature) {
4152
+ return {
4153
+ typeId: signature.type_id,
4154
+ name: signature.name,
4155
+ category: signature.category,
4156
+ doc: signature.doc,
4157
+ inputs: signature.inputs.map(normalizeRegistryPortSpec),
4158
+ variadicInputs: signature.variadic_inputs ? normalizeRegistryVariadicSpec(signature.variadic_inputs) : void 0,
4159
+ outputs: signature.outputs.map(normalizeRegistryPortSpec),
4160
+ variadicOutputs: signature.variadic_outputs ? normalizeRegistryVariadicSpec(signature.variadic_outputs) : void 0,
4161
+ params: signature.params.map(normalizeRegistryParamSpec)
4162
+ };
4163
+ }
4164
+ function normalizeRegistryPortSpec(port) {
4165
+ const normalized = {
4166
+ id: port.id,
4167
+ label: port.label,
4168
+ type: port.ty ?? "unknown"
4169
+ };
4170
+ if (port.doc) {
4171
+ normalized.doc = port.doc;
4172
+ }
4173
+ if (port.optional) {
4174
+ normalized.optional = true;
4175
+ }
4176
+ return normalized;
4177
+ }
4178
+ function normalizeRegistryVariadicSpec(spec) {
4179
+ const normalized = {
4180
+ id: spec.id,
4181
+ label: spec.label,
4182
+ type: spec.ty ?? "unknown",
4183
+ min: spec.min
4184
+ };
4185
+ if (spec.doc) {
4186
+ normalized.doc = spec.doc;
4187
+ }
4188
+ if (typeof spec.max === "number") {
4189
+ normalized.max = spec.max;
4190
+ }
4191
+ return normalized;
4192
+ }
4193
+ function normalizeRegistryParamSpec(param) {
4194
+ const normalized = {
4195
+ id: param.id,
4196
+ label: param.label,
4197
+ type: param.ty ?? "unknown"
4198
+ };
4199
+ if (param.doc) {
4200
+ normalized.doc = param.doc;
4201
+ }
4202
+ if (param.default_json !== void 0) {
4203
+ normalized.defaultValue = normalizePlainValue(param.default_json);
4204
+ }
4205
+ if (typeof param.min === "number") {
4206
+ normalized.min = param.min;
4207
+ }
4208
+ if (typeof param.max === "number") {
4209
+ normalized.max = param.max;
4210
+ }
4211
+ return normalized;
4212
+ }
4213
+ function normalizeRemap2(remap) {
4214
+ const normalized = {};
4215
+ REMAP_KEYS.forEach((key) => {
4216
+ normalized[key] = remap[key];
4217
+ });
4218
+ return normalized;
4219
+ }
4220
+ function normalizeStringArray(values) {
4221
+ if (!values || values.length === 0) {
4222
+ return void 0;
4223
+ }
4224
+ const unique = Array.from(new Set(values));
4225
+ unique.sort();
4226
+ return unique;
4227
+ }
4228
+ function bindingSortKey(binding) {
4229
+ const component = binding.component ?? "";
4230
+ return `${binding.targetId}::${component}::${binding.slotId}::${binding.slotAlias}::${binding.animatableId}`;
4231
+ }
4232
+ function sortPlainObject(record) {
4233
+ const sorted = {};
4234
+ Object.keys(record).sort().forEach((key) => {
4235
+ sorted[key] = normalizePlainValue(record[key]);
4236
+ });
4237
+ return sorted;
4238
+ }
4239
+ function normalizePlainValue(value) {
4240
+ if (Array.isArray(value)) {
4241
+ return value.map((entry) => normalizePlainValue(entry));
4242
+ }
4243
+ if (isPlainObject(value)) {
4244
+ return sortPlainObject(value);
4245
+ }
4246
+ return value;
4247
+ }
4248
+ function isPlainObject(value) {
4249
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4250
+ }
4251
+ function diffValues(actual, expected, path, ctx) {
4252
+ if (ctx.differences.length >= ctx.limit) {
4253
+ ctx.limitReached = true;
4254
+ return;
4255
+ }
4256
+ if (Object.is(actual, expected)) {
4257
+ return;
4258
+ }
4259
+ if (Array.isArray(actual) && Array.isArray(expected)) {
4260
+ const compareLength = Math.min(actual.length, expected.length);
4261
+ for (let index = 0; index < compareLength; index += 1) {
4262
+ diffValues(actual[index], expected[index], pathIndex(path, index), ctx);
4263
+ if (ctx.differences.length >= ctx.limit) {
4264
+ ctx.limitReached = true;
4265
+ return;
4266
+ }
4267
+ }
4268
+ if (actual.length > expected.length) {
4269
+ for (let index = compareLength; index < actual.length; index += 1) {
4270
+ pushDifference(ctx, {
4271
+ kind: "unexpected",
4272
+ path: pathIndex(path, index),
4273
+ actual: actual[index]
4274
+ });
4275
+ if (ctx.differences.length >= ctx.limit) {
4276
+ ctx.limitReached = true;
4277
+ return;
4278
+ }
4279
+ }
4280
+ } else if (expected.length > actual.length) {
4281
+ for (let index = compareLength; index < expected.length; index += 1) {
4282
+ pushDifference(ctx, {
4283
+ kind: "missing",
4284
+ path: pathIndex(path, index),
4285
+ expected: expected[index]
4286
+ });
4287
+ if (ctx.differences.length >= ctx.limit) {
4288
+ ctx.limitReached = true;
4289
+ return;
4290
+ }
4291
+ }
4292
+ }
4293
+ return;
4294
+ }
4295
+ if (isPlainObject(actual) && isPlainObject(expected)) {
4296
+ const keys = /* @__PURE__ */ new Set([...Object.keys(actual), ...Object.keys(expected)]);
4297
+ const sortedKeys = Array.from(keys).sort();
4298
+ for (const key of sortedKeys) {
4299
+ if (!(key in actual)) {
4300
+ pushDifference(ctx, {
4301
+ kind: "missing",
4302
+ path: pathKey(path, key),
4303
+ expected: expected[key]
4304
+ });
4305
+ } else if (!(key in expected)) {
4306
+ pushDifference(ctx, {
4307
+ kind: "unexpected",
4308
+ path: pathKey(path, key),
4309
+ actual: actual[key]
4310
+ });
4311
+ } else {
4312
+ diffValues(actual[key], expected[key], pathKey(path, key), ctx);
4313
+ }
4314
+ if (ctx.differences.length >= ctx.limit) {
4315
+ ctx.limitReached = true;
4316
+ return;
4317
+ }
4318
+ }
4319
+ return;
4320
+ }
4321
+ pushDifference(ctx, {
4322
+ kind: "mismatch",
4323
+ path,
4324
+ actual,
4325
+ expected
4326
+ });
4327
+ }
4328
+ function pathKey(base, key) {
4329
+ if (base === "") {
4330
+ return key;
4331
+ }
4332
+ if (base.endsWith("]")) {
4333
+ return `${base}.${key}`;
4334
+ }
4335
+ return `${base}.${key}`;
4336
+ }
4337
+ function pathIndex(base, index) {
4338
+ return `${base}[${index}]`;
4339
+ }
4340
+ function pushDifference(ctx, entry) {
4341
+ if (ctx.differences.length < ctx.limit) {
4342
+ ctx.differences.push(entry);
4343
+ if (ctx.differences.length === ctx.limit) {
4344
+ ctx.limitReached = true;
4345
+ }
4346
+ } else {
4347
+ ctx.limitReached = true;
4348
+ }
4349
+ }
4350
+ function normalizeDiffLimit(limit) {
4351
+ if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) {
4352
+ return DEFAULT_DIFF_LIMIT;
4353
+ }
4354
+ return Math.floor(limit);
4355
+ }
2333
4356
  // Annotate the CommonJS export names for ESM import in node:
2334
4357
  0 && (module.exports = {
4358
+ EXPRESSION_FUNCTION_VOCABULARY,
4359
+ MACHINE_REPORT_VERSION,
2335
4360
  PRIMARY_SLOT_ALIAS,
2336
4361
  PRIMARY_SLOT_ID,
4362
+ RESERVED_EXPRESSION_VARIABLES,
4363
+ SCALAR_FUNCTIONS,
4364
+ SCALAR_FUNCTION_VOCABULARY,
2337
4365
  addBindingSlot,
2338
4366
  bindingFromDefinition,
2339
- bindingOperatorDefinitions,
2340
- bindingOperatorTypes,
2341
4367
  bindingTargetFromComponent,
2342
4368
  bindingTargetFromInput,
2343
4369
  bindingToDefinition,
4370
+ buildCanonicalBindingExpression,
4371
+ buildDefaultSlotExpression,
4372
+ buildMachineReport,
4373
+ buildPiecewiseRemapExpression,
2344
4374
  buildRigGraphSpec,
2345
4375
  collectExpressionReferences,
4376
+ compileIrGraph,
2346
4377
  createDefaultBinding,
2347
4378
  createDefaultBindings,
2348
4379
  createDefaultInputValues,
2349
- createDefaultOperatorSettings,
2350
4380
  createDefaultParentBinding,
2351
4381
  createDefaultRemap,
4382
+ createExpressionVariableTable,
4383
+ createIrGraphBuilder,
4384
+ createLegacyIrGraph,
4385
+ diffMachineReports,
2352
4386
  ensureBindingStructure,
2353
- ensureOperatorParams,
2354
- getBindingOperatorDefinition,
2355
4387
  getPrimaryBindingSlot,
2356
4388
  mapExpression,
2357
4389
  parseControlExpression,
2358
4390
  reconcileBindings,
2359
4391
  remapValue,
2360
4392
  removeBindingSlot,
2361
- setBindingOperatorEnabled,
4393
+ toIrBindingSummary,
2362
4394
  updateBindingExpression,
2363
- updateBindingOperatorParam,
2364
4395
  updateBindingSlotAlias,
2365
4396
  updateBindingSlotRemap,
4397
+ updateBindingSlotValueType,
2366
4398
  updateBindingWithInput
2367
4399
  });