@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.
package/dist/index.cjs CHANGED
@@ -2124,6 +2124,61 @@ function buildBindingMetadataFromExpression(node, variables) {
2124
2124
  }
2125
2125
 
2126
2126
  // src/graphBuilder.ts
2127
+ function resolveBindingSlotInputId(bindingInputId, inputsById) {
2128
+ if (!bindingInputId || bindingInputId === import_utils5.SELF_BINDING_ID) {
2129
+ return bindingInputId;
2130
+ }
2131
+ const resolvedInputId = (0, import_utils4.resolveStandardRigInputId)(bindingInputId, inputsById);
2132
+ if (inputsById.has(resolvedInputId)) {
2133
+ return resolvedInputId;
2134
+ }
2135
+ return bindingInputId;
2136
+ }
2137
+ function isRigElementAliasInput(inputId, inputsById) {
2138
+ const input = inputsById.get(inputId);
2139
+ if (!input?.path) {
2140
+ return false;
2141
+ }
2142
+ return (0, import_utils4.isRigElementStandardInputPath)(input.path);
2143
+ }
2144
+ function isHigherOrderRigBindingInput(inputId, inputsById) {
2145
+ const input = inputsById.get(inputId);
2146
+ if (!input?.path) {
2147
+ return false;
2148
+ }
2149
+ if (isRigElementAliasInput(inputId, inputsById)) {
2150
+ return false;
2151
+ }
2152
+ return true;
2153
+ }
2154
+ function bindingReferencesRigElementInput(binding, inputsById) {
2155
+ const candidateInputIds = /* @__PURE__ */ new Set();
2156
+ if (binding.inputId && binding.inputId !== import_utils5.SELF_BINDING_ID) {
2157
+ candidateInputIds.add(binding.inputId);
2158
+ }
2159
+ binding.slots.forEach((slot) => {
2160
+ if (slot.inputId && slot.inputId !== import_utils5.SELF_BINDING_ID) {
2161
+ candidateInputIds.add(slot.inputId);
2162
+ }
2163
+ });
2164
+ for (const candidateInputId of candidateInputIds) {
2165
+ if ((0, import_utils4.isRigElementStandardInputPath)(candidateInputId)) {
2166
+ return true;
2167
+ }
2168
+ const resolvedCandidateId = resolveBindingSlotInputId(
2169
+ candidateInputId,
2170
+ inputsById
2171
+ );
2172
+ if (!resolvedCandidateId || resolvedCandidateId === import_utils5.SELF_BINDING_ID) {
2173
+ continue;
2174
+ }
2175
+ const resolvedInput = inputsById.get(resolvedCandidateId);
2176
+ if (resolvedInput && (0, import_utils4.isRigElementStandardInputPath)(resolvedInput.path)) {
2177
+ return true;
2178
+ }
2179
+ }
2180
+ return false;
2181
+ }
2127
2182
  function evaluateBinding({
2128
2183
  binding,
2129
2184
  target,
@@ -2132,9 +2187,18 @@ function evaluateBinding({
2132
2187
  component,
2133
2188
  safeId,
2134
2189
  context,
2135
- selfNodeId
2190
+ selfNodeId,
2191
+ enforceRigBoundaryRules = false
2136
2192
  }) {
2137
- const { nodes, edges, ensureInputNode, bindingIssues, summaryBindings } = context;
2193
+ const {
2194
+ nodes,
2195
+ edges,
2196
+ ensureInputNode,
2197
+ bindingIssues,
2198
+ summaryBindings,
2199
+ inputsById,
2200
+ inputBindings
2201
+ } = context;
2138
2202
  const exprContext = {
2139
2203
  componentSafeId: safeId,
2140
2204
  nodes,
@@ -2158,6 +2222,10 @@ function evaluateBinding({
2158
2222
  const fallbackAlias = `s${index + 1}`;
2159
2223
  const alias = aliasBase.length > 0 ? aliasBase : fallbackAlias;
2160
2224
  const slotId = slot.id && slot.id.length > 0 ? slot.id : alias;
2225
+ const resolvedSlotInputId = resolveBindingSlotInputId(
2226
+ slot.inputId,
2227
+ inputsById
2228
+ );
2161
2229
  const slotValueType = slot.valueType === "vector" ? "vector" : "scalar";
2162
2230
  let slotOutputId;
2163
2231
  if (slot.inputId === import_utils5.SELF_BINDING_ID) {
@@ -2173,19 +2241,30 @@ function evaluateBinding({
2173
2241
  expressionIssues.push("Self reference unavailable for this input.");
2174
2242
  slotOutputId = getConstantNodeId(exprContext, target.defaultValue);
2175
2243
  }
2176
- } else if (slot.inputId) {
2177
- const inputNode = ensureInputNode(slot.inputId);
2178
- if (inputNode) {
2179
- slotOutputId = inputNode.nodeId;
2180
- hasActiveSlot = true;
2181
- setNodeValueType(
2182
- exprContext,
2183
- slotOutputId,
2184
- slotValueType === "vector" ? "vector" : "scalar"
2244
+ } else if (resolvedSlotInputId) {
2245
+ const inputId = resolvedSlotInputId;
2246
+ const sourceBinding = inputBindings[inputId];
2247
+ const allowedHigherOrderViaRigElementSource = sourceBinding !== void 0 && bindingReferencesRigElementInput(sourceBinding, inputsById);
2248
+ if (enforceRigBoundaryRules && isHigherOrderRigBindingInput(inputId, inputsById) && !allowedHigherOrderViaRigElementSource && inputId !== import_utils5.SELF_BINDING_ID) {
2249
+ expressionIssues.push(
2250
+ `Input "${inputId}" is a higher-order rig input and cannot directly drive animatable "${target.id}".`
2185
2251
  );
2186
- } else {
2187
- expressionIssues.push(`Missing standard input "${slot.inputId}".`);
2188
2252
  slotOutputId = getConstantNodeId(exprContext, 0);
2253
+ hasActiveSlot = true;
2254
+ } else {
2255
+ const inputNode = ensureInputNode(inputId);
2256
+ if (inputNode) {
2257
+ slotOutputId = inputNode.nodeId;
2258
+ hasActiveSlot = true;
2259
+ setNodeValueType(
2260
+ exprContext,
2261
+ slotOutputId,
2262
+ slotValueType === "vector" ? "vector" : "scalar"
2263
+ );
2264
+ } else {
2265
+ expressionIssues.push(`Missing standard input "${inputId}".`);
2266
+ slotOutputId = getConstantNodeId(exprContext, 0);
2267
+ }
2189
2268
  }
2190
2269
  } else {
2191
2270
  slotOutputId = getConstantNodeId(exprContext, 0);
@@ -2195,7 +2274,7 @@ function evaluateBinding({
2195
2274
  nodeId: slotOutputId,
2196
2275
  slotId,
2197
2276
  slotAlias: alias,
2198
- inputId: slot.inputId ?? null,
2277
+ inputId: resolvedSlotInputId ?? null,
2199
2278
  targetId,
2200
2279
  animatableId,
2201
2280
  component,
@@ -2212,7 +2291,7 @@ function evaluateBinding({
2212
2291
  component,
2213
2292
  slotId,
2214
2293
  slotAlias: alias,
2215
- inputId: slot.inputId ?? null,
2294
+ inputId: resolvedSlotInputId ?? null,
2216
2295
  expression: trimmedExpression,
2217
2296
  valueType: slotValueType,
2218
2297
  nodeId: slotOutputId,
@@ -2359,6 +2438,20 @@ function buildRigInputPath(faceId, inputPath) {
2359
2438
  const suffix = trimmed ? `/${trimmed}` : "";
2360
2439
  return `rig/${faceId}${suffix}`;
2361
2440
  }
2441
+ function buildPoseControlInputPath(faceId, inputId) {
2442
+ return `rig/${faceId}/pose/control/${inputId}`;
2443
+ }
2444
+ function isPoseWeightInputPath(path) {
2445
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2446
+ return normalized.startsWith("/poses/") && normalized.endsWith(".weight");
2447
+ }
2448
+ function isPoseControlPath(path) {
2449
+ const normalized = (0, import_utils4.normalizeStandardRigInputPath)(path);
2450
+ return normalized.startsWith("/pose/control/");
2451
+ }
2452
+ function resolveInputComposeMode(mode) {
2453
+ return mode === "average" ? "average" : "add";
2454
+ }
2362
2455
  function getComponentOrder(animatable) {
2363
2456
  switch (animatable.type) {
2364
2457
  case "vector2":
@@ -3081,7 +3174,9 @@ function buildRigGraphSpec({
3081
3174
  bindings,
3082
3175
  inputsById,
3083
3176
  inputBindings,
3084
- inputMetadata
3177
+ inputMetadata,
3178
+ inputComposeModesById,
3179
+ pipelineV1
3085
3180
  }) {
3086
3181
  const metadataByInputId = inputMetadata ?? /* @__PURE__ */ new Map();
3087
3182
  const irBuilder = createIrGraphBuilder({
@@ -3104,8 +3199,634 @@ function buildRigGraphSpec({
3104
3199
  const computedInputs = /* @__PURE__ */ new Set();
3105
3200
  const summaryBindings = [];
3106
3201
  const bindingIssues = /* @__PURE__ */ new Map();
3202
+ const stagedPipelineByInputId = /* @__PURE__ */ new Map();
3107
3203
  const animatableEntries = /* @__PURE__ */ new Map();
3108
3204
  const outputs = /* @__PURE__ */ new Set();
3205
+ const composeModeByInputId = /* @__PURE__ */ new Map();
3206
+ Object.entries(inputComposeModesById ?? {}).forEach(([inputId, mode]) => {
3207
+ if (!inputsById.has(inputId)) {
3208
+ return;
3209
+ }
3210
+ composeModeByInputId.set(inputId, resolveInputComposeMode(mode));
3211
+ });
3212
+ const shouldComposeInputWithPoseControl = (input) => {
3213
+ if (!composeModeByInputId.has(input.id)) {
3214
+ return false;
3215
+ }
3216
+ if (isPoseWeightInputPath(input.path)) {
3217
+ return false;
3218
+ }
3219
+ if (isPoseControlPath(input.path)) {
3220
+ return false;
3221
+ }
3222
+ return true;
3223
+ };
3224
+ const buildNormalizedAdditiveBlendNodeId = ({
3225
+ nodeIdPrefix,
3226
+ sourceNodeIds,
3227
+ baseline
3228
+ }) => {
3229
+ if (sourceNodeIds.length === 0) {
3230
+ const fallbackNodeId = `${nodeIdPrefix}_baseline`;
3231
+ nodes.push({
3232
+ id: fallbackNodeId,
3233
+ type: "constant",
3234
+ params: {
3235
+ value: baseline
3236
+ }
3237
+ });
3238
+ return fallbackNodeId;
3239
+ }
3240
+ if (sourceNodeIds.length === 1) {
3241
+ return sourceNodeIds[0];
3242
+ }
3243
+ const addNodeId = `${nodeIdPrefix}_add`;
3244
+ nodes.push({
3245
+ id: addNodeId,
3246
+ type: "add"
3247
+ });
3248
+ sourceNodeIds.forEach((sourceNodeId, index) => {
3249
+ edges.push({
3250
+ from: { nodeId: sourceNodeId },
3251
+ to: { nodeId: addNodeId, portId: `operand_${index + 1}` }
3252
+ });
3253
+ });
3254
+ const normalizedNodeId = `${nodeIdPrefix}_normalized_add`;
3255
+ nodes.push({
3256
+ id: normalizedNodeId,
3257
+ type: "subtract",
3258
+ inputDefaults: {
3259
+ rhs: (sourceNodeIds.length - 1) * baseline
3260
+ }
3261
+ });
3262
+ edges.push({
3263
+ from: { nodeId: addNodeId },
3264
+ to: { nodeId: normalizedNodeId, portId: "lhs" }
3265
+ });
3266
+ return normalizedNodeId;
3267
+ };
3268
+ const splitTopLevelCommaSeparated = (value) => {
3269
+ const segments = [];
3270
+ let depthParen = 0;
3271
+ let depthBracket = 0;
3272
+ let start = 0;
3273
+ for (let index = 0; index < value.length; index += 1) {
3274
+ const char = value[index];
3275
+ if (char === "(") {
3276
+ depthParen += 1;
3277
+ continue;
3278
+ }
3279
+ if (char === ")") {
3280
+ depthParen = Math.max(0, depthParen - 1);
3281
+ continue;
3282
+ }
3283
+ if (char === "[") {
3284
+ depthBracket += 1;
3285
+ continue;
3286
+ }
3287
+ if (char === "]") {
3288
+ depthBracket = Math.max(0, depthBracket - 1);
3289
+ continue;
3290
+ }
3291
+ if (char === "," && depthParen === 0 && depthBracket === 0) {
3292
+ segments.push(value.slice(start, index));
3293
+ start = index + 1;
3294
+ }
3295
+ }
3296
+ segments.push(value.slice(start));
3297
+ return segments;
3298
+ };
3299
+ const stripTopLevelAssignment = (value) => {
3300
+ let depthParen = 0;
3301
+ let depthBracket = 0;
3302
+ for (let index = 0; index < value.length; index += 1) {
3303
+ const char = value[index];
3304
+ if (char === "(") {
3305
+ depthParen += 1;
3306
+ continue;
3307
+ }
3308
+ if (char === ")") {
3309
+ depthParen = Math.max(0, depthParen - 1);
3310
+ continue;
3311
+ }
3312
+ if (char === "[") {
3313
+ depthBracket += 1;
3314
+ continue;
3315
+ }
3316
+ if (char === "]") {
3317
+ depthBracket = Math.max(0, depthBracket - 1);
3318
+ continue;
3319
+ }
3320
+ if (char !== "=" || depthParen !== 0 || depthBracket !== 0) {
3321
+ continue;
3322
+ }
3323
+ const previous = value[index - 1];
3324
+ const next = value[index + 1];
3325
+ if (previous === "=" || next === "=") {
3326
+ continue;
3327
+ }
3328
+ return value.slice(index + 1).trim();
3329
+ }
3330
+ return value.trim();
3331
+ };
3332
+ const isNormalizedAdditiveFunctionName = (value) => {
3333
+ const normalized = value.trim().toLowerCase();
3334
+ return normalized === "normalizedadditive" || normalized === "normalizedaddative" || normalized === "noramalizedadditive" || normalized === "noramalizedaddative";
3335
+ };
3336
+ const buildNormalizedAdditiveExpression = (value) => {
3337
+ const args = splitTopLevelCommaSeparated(value).map(
3338
+ (entry) => entry.trim()
3339
+ );
3340
+ if (args.length === 0) {
3341
+ return "default";
3342
+ }
3343
+ const parentTerms = [];
3344
+ let baselineExpression = "default";
3345
+ const firstArg = args[0];
3346
+ if (firstArg && firstArg.startsWith("[") && firstArg.endsWith("]")) {
3347
+ const inner = firstArg.slice(1, -1).trim();
3348
+ if (inner.length > 0) {
3349
+ splitTopLevelCommaSeparated(inner).forEach((entry) => {
3350
+ const term = entry.trim();
3351
+ if (term.length > 0) {
3352
+ parentTerms.push(term);
3353
+ }
3354
+ });
3355
+ }
3356
+ args.slice(1).forEach((entry) => {
3357
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
3358
+ if (baselineMatch?.[1]) {
3359
+ baselineExpression = baselineMatch[1].trim();
3360
+ return;
3361
+ }
3362
+ const term = entry.trim();
3363
+ if (term.length > 0) {
3364
+ parentTerms.push(term);
3365
+ }
3366
+ });
3367
+ } else {
3368
+ args.forEach((entry) => {
3369
+ const baselineMatch = entry.match(/^baseline\s*=\s*(.+)$/i);
3370
+ if (baselineMatch?.[1]) {
3371
+ baselineExpression = baselineMatch[1].trim();
3372
+ return;
3373
+ }
3374
+ const term = entry.trim();
3375
+ if (term.length > 0) {
3376
+ parentTerms.push(term);
3377
+ }
3378
+ });
3379
+ }
3380
+ if (parentTerms.length === 0) {
3381
+ return `(${baselineExpression})`;
3382
+ }
3383
+ if (parentTerms.length === 1) {
3384
+ return `(${parentTerms[0]})`;
3385
+ }
3386
+ return `((${parentTerms.join(" + ")}) - (${parentTerms.length - 1}) * (${baselineExpression}))`;
3387
+ };
3388
+ const rewriteNormalizedAdditiveCalls = (value) => {
3389
+ let cursor = 0;
3390
+ let rewritten = "";
3391
+ while (cursor < value.length) {
3392
+ const remaining = value.slice(cursor);
3393
+ const match = remaining.match(
3394
+ /(normalizedadditive|normalizedaddative|noramalizedadditive|noramalizedaddative)\s*\(/i
3395
+ );
3396
+ if (!match || match.index === void 0) {
3397
+ rewritten += remaining;
3398
+ break;
3399
+ }
3400
+ const matchStart = cursor + match.index;
3401
+ const functionName = match[1] ?? "";
3402
+ const openParenIndex = value.indexOf(
3403
+ "(",
3404
+ matchStart + functionName.length
3405
+ );
3406
+ if (openParenIndex < 0) {
3407
+ rewritten += value.slice(cursor);
3408
+ break;
3409
+ }
3410
+ rewritten += value.slice(cursor, matchStart);
3411
+ let depth = 1;
3412
+ let closeParenIndex = openParenIndex + 1;
3413
+ while (closeParenIndex < value.length && depth > 0) {
3414
+ const char = value[closeParenIndex];
3415
+ if (char === "(") {
3416
+ depth += 1;
3417
+ } else if (char === ")") {
3418
+ depth -= 1;
3419
+ }
3420
+ closeParenIndex += 1;
3421
+ }
3422
+ if (depth !== 0) {
3423
+ rewritten += value.slice(matchStart);
3424
+ break;
3425
+ }
3426
+ const argsContent = value.slice(openParenIndex + 1, closeParenIndex - 1);
3427
+ if (isNormalizedAdditiveFunctionName(functionName)) {
3428
+ rewritten += buildNormalizedAdditiveExpression(argsContent);
3429
+ } else {
3430
+ rewritten += value.slice(matchStart, closeParenIndex);
3431
+ }
3432
+ cursor = closeParenIndex;
3433
+ }
3434
+ return rewritten;
3435
+ };
3436
+ const normalizeStagedFormulaExpression = (expression) => {
3437
+ const rhs = stripTopLevelAssignment(expression);
3438
+ return rewriteNormalizedAdditiveCalls(rhs);
3439
+ };
3440
+ const normalizeFormulaSignature = (expression) => normalizeStagedFormulaExpression(expression).replace(/\s+/g, "").toLowerCase();
3441
+ const buildDefaultParentTransformNodeId = (params) => {
3442
+ let transformedNodeId = params.sourceNodeId;
3443
+ if (params.scale !== 1) {
3444
+ const scaledNodeId = `input_parent_scale_${params.nodeSuffix}`;
3445
+ nodes.push({
3446
+ id: scaledNodeId,
3447
+ type: "multiply",
3448
+ inputDefaults: {
3449
+ operand_2: params.scale
3450
+ }
3451
+ });
3452
+ edges.push({
3453
+ from: { nodeId: transformedNodeId },
3454
+ to: { nodeId: scaledNodeId, portId: "operand_1" }
3455
+ });
3456
+ transformedNodeId = scaledNodeId;
3457
+ }
3458
+ if (params.offset !== 0) {
3459
+ const offsetNodeId = `input_parent_offset_${params.nodeSuffix}`;
3460
+ nodes.push({
3461
+ id: offsetNodeId,
3462
+ type: "add",
3463
+ inputDefaults: {
3464
+ operand_2: params.offset
3465
+ }
3466
+ });
3467
+ edges.push({
3468
+ from: { nodeId: transformedNodeId },
3469
+ to: { nodeId: offsetNodeId, portId: "operand_1" }
3470
+ });
3471
+ transformedNodeId = offsetNodeId;
3472
+ }
3473
+ return transformedNodeId;
3474
+ };
3475
+ const buildStagedFormulaNodeId = (params) => {
3476
+ const normalizedExpression = normalizeStagedFormulaExpression(
3477
+ params.expression
3478
+ );
3479
+ if (!normalizedExpression) {
3480
+ return params.fallbackNodeId;
3481
+ }
3482
+ const parseResult = parseControlExpression(normalizedExpression);
3483
+ const issues = [];
3484
+ if (!parseResult.node) {
3485
+ parseResult.errors.forEach((error) => {
3486
+ issues.push(
3487
+ `${params.issuePrefix}: ${error.message} (index ${error.index}).`
3488
+ );
3489
+ });
3490
+ }
3491
+ if (!parseResult.node || issues.length > 0) {
3492
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3493
+ issues.forEach((issue) => issueSet.add(issue));
3494
+ if (issues.length > 0) {
3495
+ bindingIssues.set(params.inputId, issueSet);
3496
+ }
3497
+ return params.fallbackNodeId;
3498
+ }
3499
+ const exprContext = {
3500
+ componentSafeId: params.componentSafeId,
3501
+ nodes,
3502
+ edges,
3503
+ constants: /* @__PURE__ */ new Map(),
3504
+ counter: 1,
3505
+ reservedNodes: /* @__PURE__ */ new Map(),
3506
+ nodeValueTypes: /* @__PURE__ */ new Map(),
3507
+ graphReservedNodes,
3508
+ generateReservedNodeId
3509
+ };
3510
+ const variableTable = createExpressionVariableTable();
3511
+ const registerVariableName = (name, nodeId2) => {
3512
+ const trimmed = name.trim();
3513
+ if (!trimmed) {
3514
+ return;
3515
+ }
3516
+ variableTable.registerReservedVariable({
3517
+ name: trimmed,
3518
+ nodeId: nodeId2,
3519
+ description: "Staged pipeline formula variable"
3520
+ });
3521
+ const lower = trimmed.toLowerCase();
3522
+ if (lower !== trimmed) {
3523
+ variableTable.registerReservedVariable({
3524
+ name: lower,
3525
+ nodeId: nodeId2,
3526
+ description: "Staged pipeline formula variable"
3527
+ });
3528
+ }
3529
+ };
3530
+ Object.entries(params.variables).forEach(([name, variable]) => {
3531
+ const nodeId2 = variable.nodeId ?? (typeof variable.value === "number" && Number.isFinite(variable.value) ? getConstantNodeId(exprContext, variable.value) : null);
3532
+ if (!nodeId2) {
3533
+ return;
3534
+ }
3535
+ registerVariableName(name, nodeId2);
3536
+ });
3537
+ const references = collectExpressionReferences(parseResult.node);
3538
+ const missingVariables = variableTable.missing(references);
3539
+ if (missingVariables.length > 0) {
3540
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3541
+ missingVariables.forEach((entry) => {
3542
+ issueSet.add(
3543
+ `${params.issuePrefix}: unknown formula variable "${entry.name}".`
3544
+ );
3545
+ });
3546
+ bindingIssues.set(params.inputId, issueSet);
3547
+ return params.fallbackNodeId;
3548
+ }
3549
+ validateLiteralParamArguments(parseResult.node, issues);
3550
+ const nodeId = materializeExpression(
3551
+ parseResult.node,
3552
+ exprContext,
3553
+ variableTable,
3554
+ issues
3555
+ );
3556
+ if (issues.length > 0) {
3557
+ const issueSet = bindingIssues.get(params.inputId) ?? /* @__PURE__ */ new Set();
3558
+ issues.forEach(
3559
+ (issue) => issueSet.add(`${params.issuePrefix}: ${issue}`)
3560
+ );
3561
+ bindingIssues.set(params.inputId, issueSet);
3562
+ return params.fallbackNodeId;
3563
+ }
3564
+ return nodeId;
3565
+ };
3566
+ const buildLegacyEffectiveInputNodeId = (input, directNodeId) => {
3567
+ if (!shouldComposeInputWithPoseControl(input)) {
3568
+ return directNodeId;
3569
+ }
3570
+ const safeInputId = sanitizeNodeId(input.id);
3571
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3572
+ const poseControlNodeId = `input_pose_control_${safeInputId}`;
3573
+ nodes.push({
3574
+ id: poseControlNodeId,
3575
+ type: "input",
3576
+ params: {
3577
+ path: buildPoseControlInputPath(faceId, input.id),
3578
+ value: { float: composeBaseline }
3579
+ }
3580
+ });
3581
+ const composeAddNodeId = `input_compose_add_${safeInputId}`;
3582
+ nodes.push({
3583
+ id: composeAddNodeId,
3584
+ type: "add"
3585
+ });
3586
+ edges.push(
3587
+ {
3588
+ from: { nodeId: directNodeId },
3589
+ to: { nodeId: composeAddNodeId, portId: "operand_1" }
3590
+ },
3591
+ {
3592
+ from: { nodeId: poseControlNodeId },
3593
+ to: { nodeId: composeAddNodeId, portId: "operand_2" }
3594
+ }
3595
+ );
3596
+ const composeMode = composeModeByInputId.get(input.id) ?? "add";
3597
+ let composeOutputNodeId = composeAddNodeId;
3598
+ if (composeMode === "average") {
3599
+ composeOutputNodeId = `input_compose_average_${safeInputId}`;
3600
+ nodes.push({
3601
+ id: composeOutputNodeId,
3602
+ type: "divide",
3603
+ inputDefaults: { rhs: 2 }
3604
+ });
3605
+ edges.push({
3606
+ from: { nodeId: composeAddNodeId },
3607
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3608
+ });
3609
+ } else {
3610
+ composeOutputNodeId = `input_compose_normalized_add_${safeInputId}`;
3611
+ nodes.push({
3612
+ id: composeOutputNodeId,
3613
+ type: "subtract",
3614
+ inputDefaults: { rhs: composeBaseline }
3615
+ });
3616
+ edges.push({
3617
+ from: { nodeId: composeAddNodeId },
3618
+ to: { nodeId: composeOutputNodeId, portId: "lhs" }
3619
+ });
3620
+ }
3621
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3622
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3623
+ const clampNodeId = `input_effective_${safeInputId}`;
3624
+ nodes.push({
3625
+ id: clampNodeId,
3626
+ type: "clamp",
3627
+ inputDefaults: { min: minValue, max: maxValue }
3628
+ });
3629
+ edges.push({
3630
+ from: { nodeId: composeOutputNodeId },
3631
+ to: { nodeId: clampNodeId, portId: "in" }
3632
+ });
3633
+ return clampNodeId;
3634
+ };
3635
+ const buildStagedEffectiveInputNodeId = (input, stagedConfig) => {
3636
+ const safeInputId = sanitizeNodeId(input.id);
3637
+ const composeBaseline = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3638
+ const parentContributionNodes = [];
3639
+ const parentNodeIdByAlias = /* @__PURE__ */ new Map();
3640
+ stagedConfig.parents.forEach((parent, index) => {
3641
+ if (!parent.enabled) {
3642
+ return;
3643
+ }
3644
+ const resolvedParentInputId = (0, import_utils4.resolveStandardRigInputId)(
3645
+ parent.inputId,
3646
+ inputsById
3647
+ );
3648
+ const parentInput = ensureInputNode(resolvedParentInputId);
3649
+ if (!parentInput) {
3650
+ const issueSet = bindingIssues.get(input.id) ?? /* @__PURE__ */ new Set();
3651
+ issueSet.add(
3652
+ `Staged parent "${resolvedParentInputId}" missing for "${input.id}".`
3653
+ );
3654
+ bindingIssues.set(input.id, issueSet);
3655
+ return;
3656
+ }
3657
+ const nodeSuffix = `${safeInputId}_${index + 1}`;
3658
+ const fallbackParentNodeId = buildDefaultParentTransformNodeId({
3659
+ sourceNodeId: parentInput.nodeId,
3660
+ nodeSuffix,
3661
+ scale: parent.scale,
3662
+ offset: parent.offset
3663
+ });
3664
+ const defaultParentFormulaExpression = `${parent.alias} = parent * scale + offset`;
3665
+ const parentFormulaNodeId = normalizeFormulaSignature(parent.expression) === normalizeFormulaSignature(defaultParentFormulaExpression) ? fallbackParentNodeId : buildStagedFormulaNodeId({
3666
+ expression: parent.expression,
3667
+ fallbackNodeId: fallbackParentNodeId,
3668
+ componentSafeId: `staged_parent_${nodeSuffix}`,
3669
+ inputId: input.id,
3670
+ issuePrefix: `Parent formula "${parent.alias}"`,
3671
+ variables: {
3672
+ parent: { nodeId: parentInput.nodeId },
3673
+ scale: { value: parent.scale },
3674
+ offset: { value: parent.offset },
3675
+ default: { value: composeBaseline },
3676
+ baseline: { value: composeBaseline }
3677
+ }
3678
+ });
3679
+ parentContributionNodes.push(parentFormulaNodeId);
3680
+ parentNodeIdByAlias.set(parent.alias, parentFormulaNodeId);
3681
+ const normalizedAlias = parent.alias.toLowerCase();
3682
+ if (normalizedAlias !== parent.alias) {
3683
+ parentNodeIdByAlias.set(normalizedAlias, parentFormulaNodeId);
3684
+ }
3685
+ });
3686
+ const parentContributionNodeId = parentContributionNodes.length > 0 ? (() => {
3687
+ const defaultParentContributionNodeId = buildNormalizedAdditiveBlendNodeId({
3688
+ nodeIdPrefix: `input_parent_blend_${safeInputId}`,
3689
+ sourceNodeIds: parentContributionNodes,
3690
+ baseline: composeBaseline
3691
+ });
3692
+ const defaultParentContributionExpression = `parentContribution = normalizedAdditive([${stagedConfig.parents.filter((entry) => entry.enabled).map((entry) => entry.alias).join(", ")}], baseline=default)`;
3693
+ if (normalizeFormulaSignature(stagedConfig.parentBlend.expression) === normalizeFormulaSignature(defaultParentContributionExpression)) {
3694
+ return defaultParentContributionNodeId;
3695
+ }
3696
+ return buildStagedFormulaNodeId({
3697
+ expression: stagedConfig.parentBlend.expression,
3698
+ fallbackNodeId: defaultParentContributionNodeId,
3699
+ componentSafeId: `staged_parent_contribution_${safeInputId}`,
3700
+ inputId: input.id,
3701
+ issuePrefix: "Parent contribution formula",
3702
+ variables: {
3703
+ ...Object.fromEntries(
3704
+ Array.from(parentNodeIdByAlias.entries()).map(
3705
+ ([alias, nodeId]) => [alias, { nodeId }]
3706
+ )
3707
+ ),
3708
+ default: { value: composeBaseline },
3709
+ baseline: { value: composeBaseline }
3710
+ }
3711
+ });
3712
+ })() : null;
3713
+ let poseContributionNodeId = null;
3714
+ const hasPoseContribution = stagedConfig.poseSource.targetIds.length > 0 || shouldComposeInputWithPoseControl(input);
3715
+ if (hasPoseContribution) {
3716
+ poseContributionNodeId = `input_pose_control_${safeInputId}`;
3717
+ nodes.push({
3718
+ id: poseContributionNodeId,
3719
+ type: "input",
3720
+ params: {
3721
+ path: buildPoseControlInputPath(faceId, input.id),
3722
+ value: { float: composeBaseline }
3723
+ }
3724
+ });
3725
+ }
3726
+ const sourceBranchNodeIds = [];
3727
+ if (parentContributionNodeId) {
3728
+ sourceBranchNodeIds.push(parentContributionNodeId);
3729
+ }
3730
+ if (poseContributionNodeId) {
3731
+ sourceBranchNodeIds.push(poseContributionNodeId);
3732
+ }
3733
+ if (stagedConfig.directInput.enabled) {
3734
+ const directNodeId = `input_direct_${safeInputId}`;
3735
+ nodes.push({
3736
+ id: directNodeId,
3737
+ type: "input",
3738
+ params: {
3739
+ path: stagedConfig.directInput.valuePath,
3740
+ value: { float: composeBaseline }
3741
+ }
3742
+ });
3743
+ sourceBranchNodeIds.push(directNodeId);
3744
+ }
3745
+ const sourceBlendNodeId = buildNormalizedAdditiveBlendNodeId({
3746
+ nodeIdPrefix: `input_source_blend_${safeInputId}`,
3747
+ sourceNodeIds: sourceBranchNodeIds,
3748
+ baseline: composeBaseline
3749
+ });
3750
+ const overrideEnabledNodeId = `input_override_enabled_${safeInputId}`;
3751
+ nodes.push({
3752
+ id: overrideEnabledNodeId,
3753
+ type: "input",
3754
+ params: {
3755
+ path: stagedConfig.override.enabledPath,
3756
+ value: { float: stagedConfig.override.enabledDefault ? 1 : 0 }
3757
+ }
3758
+ });
3759
+ const overrideValueNodeId = `input_override_value_${safeInputId}`;
3760
+ nodes.push({
3761
+ id: overrideValueNodeId,
3762
+ type: "input",
3763
+ params: {
3764
+ path: stagedConfig.override.valuePath,
3765
+ value: { float: stagedConfig.override.valueDefault }
3766
+ }
3767
+ });
3768
+ const overrideDeltaNodeId = `input_override_delta_${safeInputId}`;
3769
+ nodes.push({
3770
+ id: overrideDeltaNodeId,
3771
+ type: "subtract"
3772
+ });
3773
+ edges.push(
3774
+ {
3775
+ from: { nodeId: overrideValueNodeId },
3776
+ to: { nodeId: overrideDeltaNodeId, portId: "lhs" }
3777
+ },
3778
+ {
3779
+ from: { nodeId: sourceBlendNodeId },
3780
+ to: { nodeId: overrideDeltaNodeId, portId: "rhs" }
3781
+ }
3782
+ );
3783
+ const overrideScaleNodeId = `input_override_scale_${safeInputId}`;
3784
+ nodes.push({
3785
+ id: overrideScaleNodeId,
3786
+ type: "multiply"
3787
+ });
3788
+ edges.push(
3789
+ {
3790
+ from: { nodeId: overrideEnabledNodeId },
3791
+ to: { nodeId: overrideScaleNodeId, portId: "operand_1" }
3792
+ },
3793
+ {
3794
+ from: { nodeId: overrideDeltaNodeId },
3795
+ to: { nodeId: overrideScaleNodeId, portId: "operand_2" }
3796
+ }
3797
+ );
3798
+ const overrideSelectedNodeId = `input_override_selected_${safeInputId}`;
3799
+ nodes.push({
3800
+ id: overrideSelectedNodeId,
3801
+ type: "add"
3802
+ });
3803
+ edges.push(
3804
+ {
3805
+ from: { nodeId: sourceBlendNodeId },
3806
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_1" }
3807
+ },
3808
+ {
3809
+ from: { nodeId: overrideScaleNodeId },
3810
+ to: { nodeId: overrideSelectedNodeId, portId: "operand_2" }
3811
+ }
3812
+ );
3813
+ if (!stagedConfig.clamp.enabled) {
3814
+ return overrideSelectedNodeId;
3815
+ }
3816
+ const minValue = Number.isFinite(input.range.min) ? input.range.min : -1;
3817
+ const maxValue = Number.isFinite(input.range.max) ? input.range.max : 1;
3818
+ const clampNodeId = `input_effective_${safeInputId}`;
3819
+ nodes.push({
3820
+ id: clampNodeId,
3821
+ type: "clamp",
3822
+ inputDefaults: { min: minValue, max: maxValue }
3823
+ });
3824
+ edges.push({
3825
+ from: { nodeId: overrideSelectedNodeId },
3826
+ to: { nodeId: clampNodeId, portId: "in" }
3827
+ });
3828
+ return clampNodeId;
3829
+ };
3109
3830
  const ensureInputNode = (inputId) => {
3110
3831
  const existing = inputNodes.get(inputId);
3111
3832
  if (existing) {
@@ -3117,7 +3838,8 @@ function buildRigGraphSpec({
3117
3838
  }
3118
3839
  const defaultValue = Number.isFinite(input.defaultValue) ? input.defaultValue : 0;
3119
3840
  const inputBindingRaw = inputBindings[inputId];
3120
- if (inputBindingRaw) {
3841
+ const isStagedInput = (0, import_utils4.hasRigPipelineV1InputConfig)(pipelineV1, inputId);
3842
+ if (isStagedInput || inputBindingRaw) {
3121
3843
  if (buildingDerived.has(inputId)) {
3122
3844
  const issueSet = bindingIssues.get(inputId) ?? /* @__PURE__ */ new Set();
3123
3845
  issueSet.add("Derived input cycle detected.");
@@ -3126,6 +3848,21 @@ function buildRigGraphSpec({
3126
3848
  }
3127
3849
  buildingDerived.add(inputId);
3128
3850
  try {
3851
+ if (isStagedInput) {
3852
+ const stagedConfig = (0, import_utils4.resolveRigPipelineV1InputConfig)({
3853
+ faceId,
3854
+ input,
3855
+ pipelineV1
3856
+ });
3857
+ stagedPipelineByInputId.set(input.id, stagedConfig);
3858
+ computedInputs.add(inputId);
3859
+ const record3 = {
3860
+ nodeId: buildStagedEffectiveInputNodeId(input, stagedConfig),
3861
+ input
3862
+ };
3863
+ inputNodes.set(inputId, record3);
3864
+ return record3;
3865
+ }
3129
3866
  const target = bindingTargetFromInput(input);
3130
3867
  const binding = ensureBindingStructure(inputBindingRaw, target);
3131
3868
  const requiresSelf = binding.inputId === import_utils5.SELF_BINDING_ID || binding.slots.some((slot) => slot.inputId === import_utils5.SELF_BINDING_ID);
@@ -3149,7 +3886,10 @@ function buildRigGraphSpec({
3149
3886
  animatableId: inputId,
3150
3887
  component: void 0,
3151
3888
  safeId: sanitizeNodeId(inputId),
3889
+ enforceRigBoundaryRules: false,
3152
3890
  context: {
3891
+ inputsById,
3892
+ inputBindings,
3153
3893
  nodes,
3154
3894
  edges,
3155
3895
  ensureInputNode,
@@ -3169,12 +3909,18 @@ function buildRigGraphSpec({
3169
3909
  value: input.defaultValue
3170
3910
  }
3171
3911
  });
3172
- const record3 = { nodeId: constNodeId, input };
3912
+ const record3 = {
3913
+ nodeId: buildLegacyEffectiveInputNodeId(input, constNodeId),
3914
+ input
3915
+ };
3173
3916
  inputNodes.set(inputId, record3);
3174
3917
  return record3;
3175
3918
  }
3176
3919
  computedInputs.add(inputId);
3177
- const record2 = { nodeId: valueNodeId, input };
3920
+ const record2 = {
3921
+ nodeId: buildLegacyEffectiveInputNodeId(input, valueNodeId),
3922
+ input
3923
+ };
3178
3924
  inputNodes.set(inputId, record2);
3179
3925
  return record2;
3180
3926
  } finally {
@@ -3190,7 +3936,10 @@ function buildRigGraphSpec({
3190
3936
  value: { float: defaultValue }
3191
3937
  }
3192
3938
  });
3193
- const record = { nodeId, input };
3939
+ const record = {
3940
+ nodeId: buildLegacyEffectiveInputNodeId(input, nodeId),
3941
+ input
3942
+ };
3194
3943
  inputNodes.set(inputId, record);
3195
3944
  return record;
3196
3945
  };
@@ -3231,7 +3980,10 @@ function buildRigGraphSpec({
3231
3980
  animatableId: component.animatableId,
3232
3981
  component: component.component,
3233
3982
  safeId: component.safeId,
3983
+ enforceRigBoundaryRules: true,
3234
3984
  context: {
3985
+ inputsById,
3986
+ inputBindings,
3235
3987
  nodes,
3236
3988
  edges,
3237
3989
  ensureInputNode,
@@ -3379,6 +4131,61 @@ function buildRigGraphSpec({
3379
4131
  const filteredSummaryBindings = summaryBindings.filter(
3380
4132
  (binding) => outputs.has(binding.animatableId) || computedInputs.has(binding.animatableId)
3381
4133
  );
4134
+ const pipelineV1ByInputId = stagedPipelineByInputId.size > 0 ? Object.fromEntries(
4135
+ Array.from(stagedPipelineByInputId.entries()).map(
4136
+ ([inputId, stagedConfig]) => [
4137
+ inputId,
4138
+ {
4139
+ inputId: stagedConfig.inputId,
4140
+ parents: stagedConfig.parents.map((parent) => ({
4141
+ linkId: parent.linkId,
4142
+ inputId: parent.inputId,
4143
+ alias: parent.alias,
4144
+ scale: parent.scale,
4145
+ offset: parent.offset,
4146
+ enabled: parent.enabled,
4147
+ expression: parent.expression
4148
+ })),
4149
+ children: stagedConfig.children.map((child) => ({
4150
+ linkId: child.linkId,
4151
+ childInputId: child.childInputId
4152
+ })),
4153
+ parentBlend: {
4154
+ mode: stagedConfig.parentBlend.mode,
4155
+ expression: stagedConfig.parentBlend.expression
4156
+ },
4157
+ poseSource: {
4158
+ targetIds: [...stagedConfig.poseSource.targetIds]
4159
+ },
4160
+ directInput: {
4161
+ enabled: stagedConfig.directInput.enabled,
4162
+ valuePath: stagedConfig.directInput.valuePath
4163
+ },
4164
+ sourceBlend: {
4165
+ mode: stagedConfig.sourceBlend.mode
4166
+ },
4167
+ sourceFallback: {
4168
+ whenNoSources: stagedConfig.sourceFallback.whenNoSources
4169
+ },
4170
+ clamp: {
4171
+ enabled: stagedConfig.clamp.enabled
4172
+ },
4173
+ override: {
4174
+ enabledDefault: stagedConfig.override.enabledDefault,
4175
+ valueDefault: stagedConfig.override.valueDefault,
4176
+ enabledPath: stagedConfig.override.enabledPath,
4177
+ valuePath: stagedConfig.override.valuePath
4178
+ }
4179
+ }
4180
+ ]
4181
+ )
4182
+ ) : void 0;
4183
+ const hasPipelineLinks = pipelineV1?.links && typeof pipelineV1.links === "object" && Object.keys(pipelineV1.links).length > 0;
4184
+ const pipelineV1Metadata = pipelineV1ByInputId || hasPipelineLinks ? {
4185
+ version: import_utils4.RIG_PIPELINE_V1_VERSION,
4186
+ ...pipelineV1ByInputId ? { byInputId: pipelineV1ByInputId } : {},
4187
+ ...hasPipelineLinks ? { links: cloneJsonLike2(pipelineV1?.links) } : {}
4188
+ } : void 0;
3382
4189
  const vizijMetadata = {
3383
4190
  vizij: {
3384
4191
  faceId,
@@ -3415,7 +4222,8 @@ function buildRigGraphSpec({
3415
4222
  valueType: binding.valueType,
3416
4223
  issues: binding.issues ? [...binding.issues] : void 0,
3417
4224
  metadata: binding.metadata ? cloneJsonLike2(binding.metadata) : void 0
3418
- }))
4225
+ })),
4226
+ ...pipelineV1Metadata ? { pipelineV1: pipelineV1Metadata } : {}
3419
4227
  }
3420
4228
  };
3421
4229
  const issuesByTarget = {};
@@ -3811,6 +4619,9 @@ function normalizeRegistryVariadicSpec(spec) {
3811
4619
  if (typeof spec.max === "number") {
3812
4620
  normalized.max = spec.max;
3813
4621
  }
4622
+ if (spec.keyed) {
4623
+ normalized.keyed = true;
4624
+ }
3814
4625
  return normalized;
3815
4626
  }
3816
4627
  function normalizeRegistryParamSpec(param) {