@vizij/node-graph-authoring 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1685,6 +1685,61 @@ function buildBindingMetadataFromExpression(node, variables) {
1685
1685
  }
1686
1686
 
1687
1687
  // src/graphBuilder.ts
1688
+ function resolveBindingSlotInputId(bindingInputId, inputsById) {
1689
+ if (!bindingInputId || bindingInputId === import_utils5.SELF_BINDING_ID) {
1690
+ return bindingInputId;
1691
+ }
1692
+ const resolvedInputId = (0, import_utils4.resolveStandardRigInputId)(bindingInputId, inputsById);
1693
+ if (inputsById.has(resolvedInputId)) {
1694
+ return resolvedInputId;
1695
+ }
1696
+ return bindingInputId;
1697
+ }
1698
+ function isRigElementAliasInput(inputId, inputsById) {
1699
+ const input = inputsById.get(inputId);
1700
+ if (!input?.path) {
1701
+ return false;
1702
+ }
1703
+ return (0, import_utils4.isRigElementStandardInputPath)(input.path);
1704
+ }
1705
+ function isHigherOrderRigBindingInput(inputId, inputsById) {
1706
+ const input = inputsById.get(inputId);
1707
+ if (!input?.path) {
1708
+ return false;
1709
+ }
1710
+ if (isRigElementAliasInput(inputId, inputsById)) {
1711
+ return false;
1712
+ }
1713
+ return true;
1714
+ }
1715
+ function bindingReferencesRigElementInput(binding, inputsById) {
1716
+ const candidateInputIds = /* @__PURE__ */ new Set();
1717
+ if (binding.inputId && binding.inputId !== import_utils5.SELF_BINDING_ID) {
1718
+ candidateInputIds.add(binding.inputId);
1719
+ }
1720
+ binding.slots.forEach((slot) => {
1721
+ if (slot.inputId && slot.inputId !== import_utils5.SELF_BINDING_ID) {
1722
+ candidateInputIds.add(slot.inputId);
1723
+ }
1724
+ });
1725
+ for (const candidateInputId of candidateInputIds) {
1726
+ if ((0, import_utils4.isRigElementStandardInputPath)(candidateInputId)) {
1727
+ return true;
1728
+ }
1729
+ const resolvedCandidateId = resolveBindingSlotInputId(
1730
+ candidateInputId,
1731
+ inputsById
1732
+ );
1733
+ if (!resolvedCandidateId || resolvedCandidateId === import_utils5.SELF_BINDING_ID) {
1734
+ continue;
1735
+ }
1736
+ const resolvedInput = inputsById.get(resolvedCandidateId);
1737
+ if (resolvedInput && (0, import_utils4.isRigElementStandardInputPath)(resolvedInput.path)) {
1738
+ return true;
1739
+ }
1740
+ }
1741
+ return false;
1742
+ }
1688
1743
  function evaluateBinding({
1689
1744
  binding,
1690
1745
  target,
@@ -1693,9 +1748,18 @@ function evaluateBinding({
1693
1748
  component,
1694
1749
  safeId,
1695
1750
  context,
1696
- selfNodeId
1751
+ selfNodeId,
1752
+ enforceRigBoundaryRules = false
1697
1753
  }) {
1698
- const { nodes, edges, ensureInputNode, bindingIssues, summaryBindings } = context;
1754
+ const {
1755
+ nodes,
1756
+ edges,
1757
+ ensureInputNode,
1758
+ bindingIssues,
1759
+ summaryBindings,
1760
+ inputsById,
1761
+ inputBindings
1762
+ } = context;
1699
1763
  const exprContext = {
1700
1764
  componentSafeId: safeId,
1701
1765
  nodes,
@@ -1719,6 +1783,10 @@ function evaluateBinding({
1719
1783
  const fallbackAlias = `s${index + 1}`;
1720
1784
  const alias = aliasBase.length > 0 ? aliasBase : fallbackAlias;
1721
1785
  const slotId = slot.id && slot.id.length > 0 ? slot.id : alias;
1786
+ const resolvedSlotInputId = resolveBindingSlotInputId(
1787
+ slot.inputId,
1788
+ inputsById
1789
+ );
1722
1790
  const slotValueType = slot.valueType === "vector" ? "vector" : "scalar";
1723
1791
  let slotOutputId;
1724
1792
  if (slot.inputId === import_utils5.SELF_BINDING_ID) {
@@ -1734,19 +1802,30 @@ function evaluateBinding({
1734
1802
  expressionIssues.push("Self reference unavailable for this input.");
1735
1803
  slotOutputId = getConstantNodeId(exprContext, target.defaultValue);
1736
1804
  }
1737
- } else if (slot.inputId) {
1738
- const inputNode = ensureInputNode(slot.inputId);
1739
- if (inputNode) {
1740
- slotOutputId = inputNode.nodeId;
1741
- hasActiveSlot = true;
1742
- setNodeValueType(
1743
- exprContext,
1744
- slotOutputId,
1745
- slotValueType === "vector" ? "vector" : "scalar"
1805
+ } else if (resolvedSlotInputId) {
1806
+ const inputId = resolvedSlotInputId;
1807
+ const sourceBinding = inputBindings[inputId];
1808
+ const allowedHigherOrderViaRigElementSource = sourceBinding !== void 0 && bindingReferencesRigElementInput(sourceBinding, inputsById);
1809
+ if (enforceRigBoundaryRules && isHigherOrderRigBindingInput(inputId, inputsById) && !allowedHigherOrderViaRigElementSource && inputId !== import_utils5.SELF_BINDING_ID) {
1810
+ expressionIssues.push(
1811
+ `Input "${inputId}" is a higher-order rig input and cannot directly drive animatable "${target.id}".`
1746
1812
  );
1747
- } else {
1748
- expressionIssues.push(`Missing standard input "${slot.inputId}".`);
1749
1813
  slotOutputId = getConstantNodeId(exprContext, 0);
1814
+ hasActiveSlot = true;
1815
+ } else {
1816
+ const inputNode = ensureInputNode(inputId);
1817
+ if (inputNode) {
1818
+ slotOutputId = inputNode.nodeId;
1819
+ hasActiveSlot = true;
1820
+ setNodeValueType(
1821
+ exprContext,
1822
+ slotOutputId,
1823
+ slotValueType === "vector" ? "vector" : "scalar"
1824
+ );
1825
+ } else {
1826
+ expressionIssues.push(`Missing standard input "${inputId}".`);
1827
+ slotOutputId = getConstantNodeId(exprContext, 0);
1828
+ }
1750
1829
  }
1751
1830
  } else {
1752
1831
  slotOutputId = getConstantNodeId(exprContext, 0);
@@ -1756,7 +1835,7 @@ function evaluateBinding({
1756
1835
  nodeId: slotOutputId,
1757
1836
  slotId,
1758
1837
  slotAlias: alias,
1759
- inputId: slot.inputId ?? null,
1838
+ inputId: resolvedSlotInputId ?? null,
1760
1839
  targetId,
1761
1840
  animatableId,
1762
1841
  component,
@@ -1773,7 +1852,7 @@ function evaluateBinding({
1773
1852
  component,
1774
1853
  slotId,
1775
1854
  slotAlias: alias,
1776
- inputId: slot.inputId ?? null,
1855
+ inputId: resolvedSlotInputId ?? null,
1777
1856
  expression: trimmedExpression,
1778
1857
  valueType: slotValueType,
1779
1858
  nodeId: slotOutputId,
@@ -1920,6 +1999,20 @@ function buildRigInputPath(faceId, inputPath) {
1920
1999
  const suffix = trimmed ? `/${trimmed}` : "";
1921
2000
  return `rig/${faceId}${suffix}`;
1922
2001
  }
2002
+ function buildPoseControlInputPath(faceId, inputId) {
2003
+ return `rig/${faceId}/pose/control/${inputId}`;
2004
+ }
2005
+ function isPoseWeightInputPath(path) {
2006
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2007
+ return normalized.startsWith("/poses/") && normalized.endsWith(".weight");
2008
+ }
2009
+ function isPoseControlPath(path) {
2010
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2011
+ return normalized.startsWith("/pose/control/");
2012
+ }
2013
+ function resolveInputComposeMode(mode) {
2014
+ return mode === "average" ? "average" : "add";
2015
+ }
1923
2016
  function getComponentOrder(animatable) {
1924
2017
  switch (animatable.type) {
1925
2018
  case "vector2":
@@ -2642,7 +2735,9 @@ function buildRigGraphSpec({
2642
2735
  bindings,
2643
2736
  inputsById,
2644
2737
  inputBindings,
2645
- inputMetadata
2738
+ inputMetadata,
2739
+ inputComposeModesById,
2740
+ pipelineV1
2646
2741
  }) {
2647
2742
  const metadataByInputId = inputMetadata ?? /* @__PURE__ */ new Map();
2648
2743
  const irBuilder = createIrGraphBuilder({
@@ -2665,8 +2760,634 @@ function buildRigGraphSpec({
2665
2760
  const computedInputs = /* @__PURE__ */ new Set();
2666
2761
  const summaryBindings = [];
2667
2762
  const bindingIssues = /* @__PURE__ */ new Map();
2763
+ const stagedPipelineByInputId = /* @__PURE__ */ new Map();
2668
2764
  const animatableEntries = /* @__PURE__ */ new Map();
2669
2765
  const outputs = /* @__PURE__ */ new Set();
2766
+ const composeModeByInputId = /* @__PURE__ */ new Map();
2767
+ Object.entries(inputComposeModesById ?? {}).forEach(([inputId, mode]) => {
2768
+ if (!inputsById.has(inputId)) {
2769
+ return;
2770
+ }
2771
+ composeModeByInputId.set(inputId, resolveInputComposeMode(mode));
2772
+ });
2773
+ const shouldComposeInputWithPoseControl = (input) => {
2774
+ if (!composeModeByInputId.has(input.id)) {
2775
+ return false;
2776
+ }
2777
+ if (isPoseWeightInputPath(input.path)) {
2778
+ return false;
2779
+ }
2780
+ if (isPoseControlPath(input.path)) {
2781
+ return false;
2782
+ }
2783
+ return true;
2784
+ };
2785
+ const buildNormalizedAdditiveBlendNodeId = ({
2786
+ nodeIdPrefix,
2787
+ sourceNodeIds,
2788
+ baseline
2789
+ }) => {
2790
+ if (sourceNodeIds.length === 0) {
2791
+ const fallbackNodeId = `${nodeIdPrefix}_baseline`;
2792
+ nodes.push({
2793
+ id: fallbackNodeId,
2794
+ type: "constant",
2795
+ params: {
2796
+ value: baseline
2797
+ }
2798
+ });
2799
+ return fallbackNodeId;
2800
+ }
2801
+ if (sourceNodeIds.length === 1) {
2802
+ return sourceNodeIds[0];
2803
+ }
2804
+ const addNodeId = `${nodeIdPrefix}_add`;
2805
+ nodes.push({
2806
+ id: addNodeId,
2807
+ type: "add"
2808
+ });
2809
+ sourceNodeIds.forEach((sourceNodeId, index) => {
2810
+ edges.push({
2811
+ from: { nodeId: sourceNodeId },
2812
+ to: { nodeId: addNodeId, portId: `operand_${index + 1}` }
2813
+ });
2814
+ });
2815
+ const normalizedNodeId = `${nodeIdPrefix}_normalized_add`;
2816
+ nodes.push({
2817
+ id: normalizedNodeId,
2818
+ type: "subtract",
2819
+ inputDefaults: {
2820
+ rhs: (sourceNodeIds.length - 1) * baseline
2821
+ }
2822
+ });
2823
+ edges.push({
2824
+ from: { nodeId: addNodeId },
2825
+ to: { nodeId: normalizedNodeId, portId: "lhs" }
2826
+ });
2827
+ return normalizedNodeId;
2828
+ };
2829
+ const splitTopLevelCommaSeparated = (value) => {
2830
+ const segments = [];
2831
+ let depthParen = 0;
2832
+ let depthBracket = 0;
2833
+ let start = 0;
2834
+ for (let index = 0; index < value.length; index += 1) {
2835
+ const char = value[index];
2836
+ if (char === "(") {
2837
+ depthParen += 1;
2838
+ continue;
2839
+ }
2840
+ if (char === ")") {
2841
+ depthParen = Math.max(0, depthParen - 1);
2842
+ continue;
2843
+ }
2844
+ if (char === "[") {
2845
+ depthBracket += 1;
2846
+ continue;
2847
+ }
2848
+ if (char === "]") {
2849
+ depthBracket = Math.max(0, depthBracket - 1);
2850
+ continue;
2851
+ }
2852
+ if (char === "," && depthParen === 0 && depthBracket === 0) {
2853
+ segments.push(value.slice(start, index));
2854
+ start = index + 1;
2855
+ }
2856
+ }
2857
+ segments.push(value.slice(start));
2858
+ return segments;
2859
+ };
2860
+ const stripTopLevelAssignment = (value) => {
2861
+ let depthParen = 0;
2862
+ let depthBracket = 0;
2863
+ for (let index = 0; index < value.length; index += 1) {
2864
+ const char = value[index];
2865
+ if (char === "(") {
2866
+ depthParen += 1;
2867
+ continue;
2868
+ }
2869
+ if (char === ")") {
2870
+ depthParen = Math.max(0, depthParen - 1);
2871
+ continue;
2872
+ }
2873
+ if (char === "[") {
2874
+ depthBracket += 1;
2875
+ continue;
2876
+ }
2877
+ if (char === "]") {
2878
+ depthBracket = Math.max(0, depthBracket - 1);
2879
+ continue;
2880
+ }
2881
+ if (char !== "=" || depthParen !== 0 || depthBracket !== 0) {
2882
+ continue;
2883
+ }
2884
+ const previous = value[index - 1];
2885
+ const next = value[index + 1];
2886
+ if (previous === "=" || next === "=") {
2887
+ continue;
2888
+ }
2889
+ return value.slice(index + 1).trim();
2890
+ }
2891
+ return value.trim();
2892
+ };
2893
+ const isNormalizedAdditiveFunctionName = (value) => {
2894
+ const normalized = value.trim().toLowerCase();
2895
+ return normalized === "normalizedadditive" || normalized === "normalizedaddative" || normalized === "noramalizedadditive" || normalized === "noramalizedaddative";
2896
+ };
2897
+ const buildNormalizedAdditiveExpression = (value) => {
2898
+ const args = splitTopLevelCommaSeparated(value).map(
2899
+ (entry) => entry.trim()
2900
+ );
2901
+ if (args.length === 0) {
2902
+ return "default";
2903
+ }
2904
+ const parentTerms = [];
2905
+ let baselineExpression = "default";
2906
+ const firstArg = args[0];
2907
+ if (firstArg && firstArg.startsWith("[") && firstArg.endsWith("]")) {
2908
+ const inner = firstArg.slice(1, -1).trim();
2909
+ if (inner.length > 0) {
2910
+ splitTopLevelCommaSeparated(inner).forEach((entry) => {
2911
+ const term = entry.trim();
2912
+ if (term.length > 0) {
2913
+ parentTerms.push(term);
2914
+ }
2915
+ });
2916
+ }
2917
+ args.slice(1).forEach((entry) => {
2918
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
2919
+ if (baselineMatch?.[1]) {
2920
+ baselineExpression = baselineMatch[1].trim();
2921
+ return;
2922
+ }
2923
+ const term = entry.trim();
2924
+ if (term.length > 0) {
2925
+ parentTerms.push(term);
2926
+ }
2927
+ });
2928
+ } else {
2929
+ args.forEach((entry) => {
2930
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
2931
+ if (baselineMatch?.[1]) {
2932
+ baselineExpression = baselineMatch[1].trim();
2933
+ return;
2934
+ }
2935
+ const term = entry.trim();
2936
+ if (term.length > 0) {
2937
+ parentTerms.push(term);
2938
+ }
2939
+ });
2940
+ }
2941
+ if (parentTerms.length === 0) {
2942
+ return `(${baselineExpression})`;
2943
+ }
2944
+ if (parentTerms.length === 1) {
2945
+ return `(${parentTerms[0]})`;
2946
+ }
2947
+ return `((${parentTerms.join(" + ")}) - (${parentTerms.length - 1}) * (${baselineExpression}))`;
2948
+ };
2949
+ const rewriteNormalizedAdditiveCalls = (value) => {
2950
+ let cursor = 0;
2951
+ let rewritten = "";
2952
+ while (cursor < value.length) {
2953
+ const remaining = value.slice(cursor);
2954
+ const match = remaining.match(
2955
+ /(normalizedadditive|normalizedaddative|noramalizedadditive|noramalizedaddative)\s*\(/i
2956
+ );
2957
+ if (!match || match.index === void 0) {
2958
+ rewritten += remaining;
2959
+ break;
2960
+ }
2961
+ const matchStart = cursor + match.index;
2962
+ const functionName = match[1] ?? "";
2963
+ const openParenIndex = value.indexOf(
2964
+ "(",
2965
+ matchStart + functionName.length
2966
+ );
2967
+ if (openParenIndex < 0) {
2968
+ rewritten += value.slice(cursor);
2969
+ break;
2970
+ }
2971
+ rewritten += value.slice(cursor, matchStart);
2972
+ let depth = 1;
2973
+ let closeParenIndex = openParenIndex + 1;
2974
+ while (closeParenIndex < value.length && depth > 0) {
2975
+ const char = value[closeParenIndex];
2976
+ if (char === "(") {
2977
+ depth += 1;
2978
+ } else if (char === ")") {
2979
+ depth -= 1;
2980
+ }
2981
+ closeParenIndex += 1;
2982
+ }
2983
+ if (depth !== 0) {
2984
+ rewritten += value.slice(matchStart);
2985
+ break;
2986
+ }
2987
+ const argsContent = value.slice(openParenIndex + 1, closeParenIndex - 1);
2988
+ if (isNormalizedAdditiveFunctionName(functionName)) {
2989
+ rewritten += buildNormalizedAdditiveExpression(argsContent);
2990
+ } else {
2991
+ rewritten += value.slice(matchStart, closeParenIndex);
2992
+ }
2993
+ cursor = closeParenIndex;
2994
+ }
2995
+ return rewritten;
2996
+ };
2997
+ const normalizeStagedFormulaExpression = (expression) => {
2998
+ const rhs = stripTopLevelAssignment(expression);
2999
+ return rewriteNormalizedAdditiveCalls(rhs);
3000
+ };
3001
+ const normalizeFormulaSignature = (expression) => normalizeStagedFormulaExpression(expression).replace(/\s+/g, "").toLowerCase();
3002
+ const buildDefaultParentTransformNodeId = (params) => {
3003
+ let transformedNodeId = params.sourceNodeId;
3004
+ if (params.scale !== 1) {
3005
+ const scaledNodeId = `input_parent_scale_${params.nodeSuffix}`;
3006
+ nodes.push({
3007
+ id: scaledNodeId,
3008
+ type: "multiply",
3009
+ inputDefaults: {
3010
+ operand_2: params.scale
3011
+ }
3012
+ });
3013
+ edges.push({
3014
+ from: { nodeId: transformedNodeId },
3015
+ to: { nodeId: scaledNodeId, portId: "operand_1" }
3016
+ });
3017
+ transformedNodeId = scaledNodeId;
3018
+ }
3019
+ if (params.offset !== 0) {
3020
+ const offsetNodeId = `input_parent_offset_${params.nodeSuffix}`;
3021
+ nodes.push({
3022
+ id: offsetNodeId,
3023
+ type: "add",
3024
+ inputDefaults: {
3025
+ operand_2: params.offset
3026
+ }
3027
+ });
3028
+ edges.push({
3029
+ from: { nodeId: transformedNodeId },
3030
+ to: { nodeId: offsetNodeId, portId: "operand_1" }
3031
+ });
3032
+ transformedNodeId = offsetNodeId;
3033
+ }
3034
+ return transformedNodeId;
3035
+ };
3036
+ const buildStagedFormulaNodeId = (params) => {
3037
+ const normalizedExpression = normalizeStagedFormulaExpression(
3038
+ params.expression
3039
+ );
3040
+ if (!normalizedExpression) {
3041
+ return params.fallbackNodeId;
3042
+ }
3043
+ const parseResult = parseControlExpression(normalizedExpression);
3044
+ const issues = [];
3045
+ if (!parseResult.node) {
3046
+ parseResult.errors.forEach((error) => {
3047
+ issues.push(
3048
+ `${params.issuePrefix}: ${error.message} (index ${error.index}).`
3049
+ );
3050
+ });
3051
+ }
3052
+ if (!parseResult.node || issues.length > 0) {
3053
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3054
+ issues.forEach((issue) => issueSet.add(issue));
3055
+ if (issues.length > 0) {
3056
+ bindingIssues.set(params.inputId, issueSet);
3057
+ }
3058
+ return params.fallbackNodeId;
3059
+ }
3060
+ const exprContext = {
3061
+ componentSafeId: params.componentSafeId,
3062
+ nodes,
3063
+ edges,
3064
+ constants: /* @__PURE__ */ new Map(),
3065
+ counter: 1,
3066
+ reservedNodes: /* @__PURE__ */ new Map(),
3067
+ nodeValueTypes: /* @__PURE__ */ new Map(),
3068
+ graphReservedNodes,
3069
+ generateReservedNodeId
3070
+ };
3071
+ const variableTable = createExpressionVariableTable();
3072
+ const registerVariableName = (name, nodeId2) => {
3073
+ const trimmed = name.trim();
3074
+ if (!trimmed) {
3075
+ return;
3076
+ }
3077
+ variableTable.registerReservedVariable({
3078
+ name: trimmed,
3079
+ nodeId: nodeId2,
3080
+ description: "Staged pipeline formula variable"
3081
+ });
3082
+ const lower = trimmed.toLowerCase();
3083
+ if (lower !== trimmed) {
3084
+ variableTable.registerReservedVariable({
3085
+ name: lower,
3086
+ nodeId: nodeId2,
3087
+ description: "Staged pipeline formula variable"
3088
+ });
3089
+ }
3090
+ };
3091
+ Object.entries(params.variables).forEach(([name, variable]) => {
3092
+ const nodeId2 = variable.nodeId ?? (typeof variable.value === "number" && Number.isFinite(variable.value) ? getConstantNodeId(exprContext, variable.value) : null);
3093
+ if (!nodeId2) {
3094
+ return;
3095
+ }
3096
+ registerVariableName(name, nodeId2);
3097
+ });
3098
+ const references = collectExpressionReferences(parseResult.node);
3099
+ const missingVariables = variableTable.missing(references);
3100
+ if (missingVariables.length > 0) {
3101
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3102
+ missingVariables.forEach((entry) => {
3103
+ issueSet.add(
3104
+ `${params.issuePrefix}: unknown formula variable "${entry.name}".`
3105
+ );
3106
+ });
3107
+ bindingIssues.set(params.inputId, issueSet);
3108
+ return params.fallbackNodeId;
3109
+ }
3110
+ validateLiteralParamArguments(parseResult.node, issues);
3111
+ const nodeId = materializeExpression(
3112
+ parseResult.node,
3113
+ exprContext,
3114
+ variableTable,
3115
+ issues
3116
+ );
3117
+ if (issues.length > 0) {
3118
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3119
+ issues.forEach(
3120
+ (issue) => issueSet.add(`${params.issuePrefix}: ${issue}`)
3121
+ );
3122
+ bindingIssues.set(params.inputId, issueSet);
3123
+ return params.fallbackNodeId;
3124
+ }
3125
+ return nodeId;
3126
+ };
3127
+ const buildLegacyEffectiveInputNodeId = (input, directNodeId) => {
3128
+ if (!shouldComposeInputWithPoseControl(input)) {
3129
+ return directNodeId;
3130
+ }
3131
+ const safeInputId = sanitizeNodeId(input.id);
3132
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3133
+ const poseControlNodeId = `input_pose_control_${safeInputId}`;
3134
+ nodes.push({
3135
+ id: poseControlNodeId,
3136
+ type: "input",
3137
+ params: {
3138
+ path: buildPoseControlInputPath(faceId, input.id),
3139
+ value: { float: composeBaseline }
3140
+ }
3141
+ });
3142
+ const composeAddNodeId = `input_compose_add_${safeInputId}`;
3143
+ nodes.push({
3144
+ id: composeAddNodeId,
3145
+ type: "add"
3146
+ });
3147
+ edges.push(
3148
+ {
3149
+ from: { nodeId: directNodeId },
3150
+ to: { nodeId: composeAddNodeId, portId: "operand_1" }
3151
+ },
3152
+ {
3153
+ from: { nodeId: poseControlNodeId },
3154
+ to: { nodeId: composeAddNodeId, portId: "operand_2" }
3155
+ }
3156
+ );
3157
+ const composeMode = composeModeByInputId.get(input.id) ?? "add";
3158
+ let composeOutputNodeId = composeAddNodeId;
3159
+ if (composeMode === "average") {
3160
+ composeOutputNodeId = `input_compose_average_${safeInputId}`;
3161
+ nodes.push({
3162
+ id: composeOutputNodeId,
3163
+ type: "divide",
3164
+ inputDefaults: { rhs: 2 }
3165
+ });
3166
+ edges.push({
3167
+ from: { nodeId: composeAddNodeId },
3168
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3169
+ });
3170
+ } else {
3171
+ composeOutputNodeId = `input_compose_normalized_add_${safeInputId}`;
3172
+ nodes.push({
3173
+ id: composeOutputNodeId,
3174
+ type: "subtract",
3175
+ inputDefaults: { rhs: composeBaseline }
3176
+ });
3177
+ edges.push({
3178
+ from: { nodeId: composeAddNodeId },
3179
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3180
+ });
3181
+ }
3182
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3183
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3184
+ const clampNodeId = `input_effective_${safeInputId}`;
3185
+ nodes.push({
3186
+ id: clampNodeId,
3187
+ type: "clamp",
3188
+ inputDefaults: { min: minValue, max: maxValue }
3189
+ });
3190
+ edges.push({
3191
+ from: { nodeId: composeOutputNodeId },
3192
+ to: { nodeId: clampNodeId, portId: "in" }
3193
+ });
3194
+ return clampNodeId;
3195
+ };
3196
+ const buildStagedEffectiveInputNodeId = (input, stagedConfig) => {
3197
+ const safeInputId = sanitizeNodeId(input.id);
3198
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3199
+ const parentContributionNodes = [];
3200
+ const parentNodeIdByAlias = /* @__PURE__ */ new Map();
3201
+ stagedConfig.parents.forEach((parent, index) => {
3202
+ if (!parent.enabled) {
3203
+ return;
3204
+ }
3205
+ const resolvedParentInputId = (0, import_utils4.resolveStandardRigInputId)(
3206
+ parent.inputId,
3207
+ inputsById
3208
+ );
3209
+ const parentInput = ensureInputNode(resolvedParentInputId);
3210
+ if (!parentInput) {
3211
+ const issueSet = bindingIssues.get(input.id) ?? /* @__PURE__ */ new Set();
3212
+ issueSet.add(
3213
+ `Staged parent "${resolvedParentInputId}" missing for "${input.id}".`
3214
+ );
3215
+ bindingIssues.set(input.id, issueSet);
3216
+ return;
3217
+ }
3218
+ const nodeSuffix = `${safeInputId}_${index + 1}`;
3219
+ const fallbackParentNodeId = buildDefaultParentTransformNodeId({
3220
+ sourceNodeId: parentInput.nodeId,
3221
+ nodeSuffix,
3222
+ scale: parent.scale,
3223
+ offset: parent.offset
3224
+ });
3225
+ const defaultParentFormulaExpression = `${parent.alias} = parent * scale + offset`;
3226
+ const parentFormulaNodeId = normalizeFormulaSignature(parent.expression) === normalizeFormulaSignature(defaultParentFormulaExpression) ? fallbackParentNodeId : buildStagedFormulaNodeId({
3227
+ expression: parent.expression,
3228
+ fallbackNodeId: fallbackParentNodeId,
3229
+ componentSafeId: `staged_parent_${nodeSuffix}`,
3230
+ inputId: input.id,
3231
+ issuePrefix: `Parent formula "${parent.alias}"`,
3232
+ variables: {
3233
+ parent: { nodeId: parentInput.nodeId },
3234
+ scale: { value: parent.scale },
3235
+ offset: { value: parent.offset },
3236
+ default: { value: composeBaseline },
3237
+ baseline: { value: composeBaseline }
3238
+ }
3239
+ });
3240
+ parentContributionNodes.push(parentFormulaNodeId);
3241
+ parentNodeIdByAlias.set(parent.alias, parentFormulaNodeId);
3242
+ const normalizedAlias = parent.alias.toLowerCase();
3243
+ if (normalizedAlias !== parent.alias) {
3244
+ parentNodeIdByAlias.set(normalizedAlias, parentFormulaNodeId);
3245
+ }
3246
+ });
3247
+ const parentContributionNodeId = parentContributionNodes.length > 0 ? (() => {
3248
+ const defaultParentContributionNodeId = buildNormalizedAdditiveBlendNodeId({
3249
+ nodeIdPrefix: `input_parent_blend_${safeInputId}`,
3250
+ sourceNodeIds: parentContributionNodes,
3251
+ baseline: composeBaseline
3252
+ });
3253
+ const defaultParentContributionExpression = `parentContribution = normalizedAdditive([${stagedConfig.parents.filter((entry) => entry.enabled).map((entry) => entry.alias).join(", ")}], baseline=default)`;
3254
+ if (normalizeFormulaSignature(stagedConfig.parentBlend.expression) === normalizeFormulaSignature(defaultParentContributionExpression)) {
3255
+ return defaultParentContributionNodeId;
3256
+ }
3257
+ return buildStagedFormulaNodeId({
3258
+ expression: stagedConfig.parentBlend.expression,
3259
+ fallbackNodeId: defaultParentContributionNodeId,
3260
+ componentSafeId: `staged_parent_contribution_${safeInputId}`,
3261
+ inputId: input.id,
3262
+ issuePrefix: "Parent contribution formula",
3263
+ variables: {
3264
+ ...Object.fromEntries(
3265
+ Array.from(parentNodeIdByAlias.entries()).map(
3266
+ ([alias, nodeId]) => [alias, { nodeId }]
3267
+ )
3268
+ ),
3269
+ default: { value: composeBaseline },
3270
+ baseline: { value: composeBaseline }
3271
+ }
3272
+ });
3273
+ })() : null;
3274
+ let poseContributionNodeId = null;
3275
+ const hasPoseContribution = stagedConfig.poseSource.targetIds.length > 0 || shouldComposeInputWithPoseControl(input);
3276
+ if (hasPoseContribution) {
3277
+ poseContributionNodeId = `input_pose_control_${safeInputId}`;
3278
+ nodes.push({
3279
+ id: poseContributionNodeId,
3280
+ type: "input",
3281
+ params: {
3282
+ path: buildPoseControlInputPath(faceId, input.id),
3283
+ value: { float: composeBaseline }
3284
+ }
3285
+ });
3286
+ }
3287
+ const sourceBranchNodeIds = [];
3288
+ if (parentContributionNodeId) {
3289
+ sourceBranchNodeIds.push(parentContributionNodeId);
3290
+ }
3291
+ if (poseContributionNodeId) {
3292
+ sourceBranchNodeIds.push(poseContributionNodeId);
3293
+ }
3294
+ if (stagedConfig.directInput.enabled) {
3295
+ const directNodeId = `input_direct_${safeInputId}`;
3296
+ nodes.push({
3297
+ id: directNodeId,
3298
+ type: "input",
3299
+ params: {
3300
+ path: stagedConfig.directInput.valuePath,
3301
+ value: { float: composeBaseline }
3302
+ }
3303
+ });
3304
+ sourceBranchNodeIds.push(directNodeId);
3305
+ }
3306
+ const sourceBlendNodeId = buildNormalizedAdditiveBlendNodeId({
3307
+ nodeIdPrefix: `input_source_blend_${safeInputId}`,
3308
+ sourceNodeIds: sourceBranchNodeIds,
3309
+ baseline: composeBaseline
3310
+ });
3311
+ const overrideEnabledNodeId = `input_override_enabled_${safeInputId}`;
3312
+ nodes.push({
3313
+ id: overrideEnabledNodeId,
3314
+ type: "input",
3315
+ params: {
3316
+ path: stagedConfig.override.enabledPath,
3317
+ value: { float: stagedConfig.override.enabledDefault ? 1 : 0 }
3318
+ }
3319
+ });
3320
+ const overrideValueNodeId = `input_override_value_${safeInputId}`;
3321
+ nodes.push({
3322
+ id: overrideValueNodeId,
3323
+ type: "input",
3324
+ params: {
3325
+ path: stagedConfig.override.valuePath,
3326
+ value: { float: stagedConfig.override.valueDefault }
3327
+ }
3328
+ });
3329
+ const overrideDeltaNodeId = `input_override_delta_${safeInputId}`;
3330
+ nodes.push({
3331
+ id: overrideDeltaNodeId,
3332
+ type: "subtract"
3333
+ });
3334
+ edges.push(
3335
+ {
3336
+ from: { nodeId: overrideValueNodeId },
3337
+ to: { nodeId: overrideDeltaNodeId, portId: "lhs" }
3338
+ },
3339
+ {
3340
+ from: { nodeId: sourceBlendNodeId },
3341
+ to: { nodeId: overrideDeltaNodeId, portId: "rhs" }
3342
+ }
3343
+ );
3344
+ const overrideScaleNodeId = `input_override_scale_${safeInputId}`;
3345
+ nodes.push({
3346
+ id: overrideScaleNodeId,
3347
+ type: "multiply"
3348
+ });
3349
+ edges.push(
3350
+ {
3351
+ from: { nodeId: overrideEnabledNodeId },
3352
+ to: { nodeId: overrideScaleNodeId, portId: "operand_1" }
3353
+ },
3354
+ {
3355
+ from: { nodeId: overrideDeltaNodeId },
3356
+ to: { nodeId: overrideScaleNodeId, portId: "operand_2" }
3357
+ }
3358
+ );
3359
+ const overrideSelectedNodeId = `input_override_selected_${safeInputId}`;
3360
+ nodes.push({
3361
+ id: overrideSelectedNodeId,
3362
+ type: "add"
3363
+ });
3364
+ edges.push(
3365
+ {
3366
+ from: { nodeId: sourceBlendNodeId },
3367
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_1" }
3368
+ },
3369
+ {
3370
+ from: { nodeId: overrideScaleNodeId },
3371
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_2" }
3372
+ }
3373
+ );
3374
+ if (!stagedConfig.clamp.enabled) {
3375
+ return overrideSelectedNodeId;
3376
+ }
3377
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3378
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3379
+ const clampNodeId = `input_effective_${safeInputId}`;
3380
+ nodes.push({
3381
+ id: clampNodeId,
3382
+ type: "clamp",
3383
+ inputDefaults: { min: minValue, max: maxValue }
3384
+ });
3385
+ edges.push({
3386
+ from: { nodeId: overrideSelectedNodeId },
3387
+ to: { nodeId: clampNodeId, portId: "in" }
3388
+ });
3389
+ return clampNodeId;
3390
+ };
2670
3391
  const ensureInputNode = (inputId) => {
2671
3392
  const existing = inputNodes.get(inputId);
2672
3393
  if (existing) {
@@ -2678,7 +3399,8 @@ function buildRigGraphSpec({
2678
3399
  }
2679
3400
  const defaultValue = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
2680
3401
  const inputBindingRaw = inputBindings[inputId];
2681
- if (inputBindingRaw) {
3402
+ const isStagedInput = (0, import_utils4.hasRigPipelineV1InputConfig)(pipelineV1, inputId);
3403
+ if (isStagedInput || inputBindingRaw) {
2682
3404
  if (buildingDerived.has(inputId)) {
2683
3405
  const issueSet = bindingIssues.get(inputId) ?? /* @__PURE__ */ new Set();
2684
3406
  issueSet.add("Derived input cycle detected.");
@@ -2687,6 +3409,21 @@ function buildRigGraphSpec({
2687
3409
  }
2688
3410
  buildingDerived.add(inputId);
2689
3411
  try {
3412
+ if (isStagedInput) {
3413
+ const stagedConfig = (0, import_utils4.resolveRigPipelineV1InputConfig)({
3414
+ faceId,
3415
+ input,
3416
+ pipelineV1
3417
+ });
3418
+ stagedPipelineByInputId.set(input.id, stagedConfig);
3419
+ computedInputs.add(inputId);
3420
+ const record3 = {
3421
+ nodeId: buildStagedEffectiveInputNodeId(input, stagedConfig),
3422
+ input
3423
+ };
3424
+ inputNodes.set(inputId, record3);
3425
+ return record3;
3426
+ }
2690
3427
  const target = bindingTargetFromInput(input);
2691
3428
  const binding = ensureBindingStructure(inputBindingRaw, target);
2692
3429
  const requiresSelf = binding.inputId === import_utils5.SELF_BINDING_ID || binding.slots.some((slot) => slot.inputId === import_utils5.SELF_BINDING_ID);
@@ -2710,7 +3447,10 @@ function buildRigGraphSpec({
2710
3447
  animatableId: inputId,
2711
3448
  component: void 0,
2712
3449
  safeId: sanitizeNodeId(inputId),
3450
+ enforceRigBoundaryRules: false,
2713
3451
  context: {
3452
+ inputsById,
3453
+ inputBindings,
2714
3454
  nodes,
2715
3455
  edges,
2716
3456
  ensureInputNode,
@@ -2730,12 +3470,18 @@ function buildRigGraphSpec({
2730
3470
  value: input.defaultValue
2731
3471
  }
2732
3472
  });
2733
- const record3 = { nodeId: constNodeId, input };
3473
+ const record3 = {
3474
+ nodeId: buildLegacyEffectiveInputNodeId(input, constNodeId),
3475
+ input
3476
+ };
2734
3477
  inputNodes.set(inputId, record3);
2735
3478
  return record3;
2736
3479
  }
2737
3480
  computedInputs.add(inputId);
2738
- const record2 = { nodeId: valueNodeId, input };
3481
+ const record2 = {
3482
+ nodeId: buildLegacyEffectiveInputNodeId(input, valueNodeId),
3483
+ input
3484
+ };
2739
3485
  inputNodes.set(inputId, record2);
2740
3486
  return record2;
2741
3487
  } finally {
@@ -2751,7 +3497,10 @@ function buildRigGraphSpec({
2751
3497
  value: { float: defaultValue }
2752
3498
  }
2753
3499
  });
2754
- const record = { nodeId, input };
3500
+ const record = {
3501
+ nodeId: buildLegacyEffectiveInputNodeId(input, nodeId),
3502
+ input
3503
+ };
2755
3504
  inputNodes.set(inputId, record);
2756
3505
  return record;
2757
3506
  };
@@ -2792,7 +3541,10 @@ function buildRigGraphSpec({
2792
3541
  animatableId: component.animatableId,
2793
3542
  component: component.component,
2794
3543
  safeId: component.safeId,
3544
+ enforceRigBoundaryRules: true,
2795
3545
  context: {
3546
+ inputsById,
3547
+ inputBindings,
2796
3548
  nodes,
2797
3549
  edges,
2798
3550
  ensureInputNode,
@@ -2940,6 +3692,61 @@ function buildRigGraphSpec({
2940
3692
  const filteredSummaryBindings = summaryBindings.filter(
2941
3693
  (binding) => outputs.has(binding.animatableId) || computedInputs.has(binding.animatableId)
2942
3694
  );
3695
+ const pipelineV1ByInputId = stagedPipelineByInputId.size > 0 ? Object.fromEntries(
3696
+ Array.from(stagedPipelineByInputId.entries()).map(
3697
+ ([inputId, stagedConfig]) => [
3698
+ inputId,
3699
+ {
3700
+ inputId: stagedConfig.inputId,
3701
+ parents: stagedConfig.parents.map((parent) => ({
3702
+ linkId: parent.linkId,
3703
+ inputId: parent.inputId,
3704
+ alias: parent.alias,
3705
+ scale: parent.scale,
3706
+ offset: parent.offset,
3707
+ enabled: parent.enabled,
3708
+ expression: parent.expression
3709
+ })),
3710
+ children: stagedConfig.children.map((child) => ({
3711
+ linkId: child.linkId,
3712
+ childInputId: child.childInputId
3713
+ })),
3714
+ parentBlend: {
3715
+ mode: stagedConfig.parentBlend.mode,
3716
+ expression: stagedConfig.parentBlend.expression
3717
+ },
3718
+ poseSource: {
3719
+ targetIds: [...stagedConfig.poseSource.targetIds]
3720
+ },
3721
+ directInput: {
3722
+ enabled: stagedConfig.directInput.enabled,
3723
+ valuePath: stagedConfig.directInput.valuePath
3724
+ },
3725
+ sourceBlend: {
3726
+ mode: stagedConfig.sourceBlend.mode
3727
+ },
3728
+ sourceFallback: {
3729
+ whenNoSources: stagedConfig.sourceFallback.whenNoSources
3730
+ },
3731
+ clamp: {
3732
+ enabled: stagedConfig.clamp.enabled
3733
+ },
3734
+ override: {
3735
+ enabledDefault: stagedConfig.override.enabledDefault,
3736
+ valueDefault: stagedConfig.override.valueDefault,
3737
+ enabledPath: stagedConfig.override.enabledPath,
3738
+ valuePath: stagedConfig.override.valuePath
3739
+ }
3740
+ }
3741
+ ]
3742
+ )
3743
+ ) : void 0;
3744
+ const hasPipelineLinks = pipelineV1?.links && typeof pipelineV1.links === "object" && Object.keys(pipelineV1.links).length > 0;
3745
+ const pipelineV1Metadata = pipelineV1ByInputId || hasPipelineLinks ? {
3746
+ version: import_utils4.RIG_PIPELINE_V1_VERSION,
3747
+ ...pipelineV1ByInputId ? { byInputId: pipelineV1ByInputId } : {},
3748
+ ...hasPipelineLinks ? { links: cloneJsonLike2(pipelineV1?.links) } : {}
3749
+ } : void 0;
2943
3750
  const vizijMetadata = {
2944
3751
  vizij: {
2945
3752
  faceId,
@@ -2976,7 +3783,8 @@ function buildRigGraphSpec({
2976
3783
  valueType: binding.valueType,
2977
3784
  issues: binding.issues ? [...binding.issues] : void 0,
2978
3785
  metadata: binding.metadata ? cloneJsonLike2(binding.metadata) : void 0
2979
- }))
3786
+ })),
3787
+ ...pipelineV1Metadata ? { pipelineV1: pipelineV1Metadata } : {}
2980
3788
  }
2981
3789
  };
2982
3790
  const issuesByTarget = {};
@@ -3372,6 +4180,9 @@ function normalizeRegistryVariadicSpec(spec) {
3372
4180
  if (typeof spec.max === "number") {
3373
4181
  normalized.max = spec.max;
3374
4182
  }
4183
+ if (spec.keyed) {
4184
+ normalized.keyed = true;
4185
+ }
3375
4186
  return normalized;
3376
4187
  }
3377
4188
  function normalizeRegistryParamSpec(param) {