@bian-womp/spark-graph 0.3.37 → 0.3.38

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/lib/cjs/index.cjs CHANGED
@@ -3136,22 +3136,33 @@ class GraphRuntime {
3136
3136
  if (value !== undefined && registry) {
3137
3137
  const desc = registry.nodes.get(node.typeId);
3138
3138
  const resolved = this.graph.getResolvedHandles(nodeId);
3139
- // Get typeId from resolved handles first, then registry statics
3140
- const typeId = resolved
3141
- ? getInputTypeId(resolved.inputs, handle)
3139
+ // Get declared types (may be union); prefer resolved handles over registry statics
3140
+ const declaredTypes = resolved
3141
+ ? getInputDeclaredTypes(resolved.inputs, handle)
3142
3142
  : desc
3143
- ? getInputTypeId(desc.inputs, handle)
3143
+ ? getInputDeclaredTypes(desc.inputs, handle)
3144
3144
  : undefined;
3145
- if (typeId) {
3146
- const typeDesc = registry.types.get(typeId);
3147
- if (typeDesc?.validate && !typeDesc.validate(value)) {
3148
- // Emit error event for invalid input value and reject it
3149
- const errorMessage = `Invalid value for input ${nodeId}.${handle} (type ${typeId}): ${JSON.stringify(value)}`;
3145
+ const typeIds = Array.isArray(declaredTypes)
3146
+ ? declaredTypes
3147
+ : declaredTypes
3148
+ ? [declaredTypes]
3149
+ : [];
3150
+ if (typeIds.length > 0) {
3151
+ const isValidForAny = typeIds.some((tId) => {
3152
+ const typeDesc = registry.types.get(tId);
3153
+ // If no validate function, consider it valid
3154
+ if (!typeDesc?.validate)
3155
+ return true;
3156
+ return typeDesc.validate(value);
3157
+ });
3158
+ if (!isValidForAny) {
3159
+ const typeLabel = typeIds.join("|");
3160
+ const errorMessage = `Invalid value for input ${nodeId}.${handle} (type ${typeLabel}): ${JSON.stringify(value)}`;
3150
3161
  this.eventEmitter.emit("error", {
3151
3162
  kind: "input-validation",
3152
3163
  nodeId,
3153
3164
  handle,
3154
- typeId,
3165
+ typeId: typeLabel,
3155
3166
  value,
3156
3167
  message: errorMessage,
3157
3168
  });
@@ -3741,11 +3752,30 @@ class GraphBuilder {
3741
3752
  return { inputs: {}, outputs: {} };
3742
3753
  const desc = this.registry.nodes.get(n.typeId);
3743
3754
  const resolved = n.resolvedHandles || {};
3744
- const inputs = { ...desc?.inputs, ...resolved.inputs };
3755
+ // Merge inputs properly, handling union types and metadata
3756
+ const inputs = {};
3757
+ // First, add all static handles
3758
+ if (desc?.inputs) {
3759
+ for (const [handle, staticDesc] of Object.entries(desc.inputs)) {
3760
+ inputs[handle] = staticDesc;
3761
+ }
3762
+ }
3763
+ // Then, merge resolved handles (which may override/extend static handles)
3764
+ if (resolved.inputs) {
3765
+ for (const [handle, resolvedDesc] of Object.entries(resolved.inputs)) {
3766
+ const staticDesc = desc?.inputs?.[handle];
3767
+ const merged = mergeInputHandleDescriptors(staticDesc, resolvedDesc);
3768
+ if (merged) {
3769
+ inputs[handle] = merged;
3770
+ }
3771
+ }
3772
+ }
3745
3773
  const outputs = { ...desc?.outputs, ...resolved.outputs };
3746
3774
  return { inputs, outputs };
3747
3775
  };
3748
3776
  const normOut = (decl) => Array.isArray(decl) ? decl : decl ? [decl] : [];
3777
+ const getEnumTypes = (decl) => normOut(decl).filter((t) => t.startsWith("enum:"));
3778
+ const hasArrayType = (decl) => normOut(decl).some((t) => t.endsWith("[]"));
3749
3779
  const canFlow = (from, to) => {
3750
3780
  if (!to || !from)
3751
3781
  return true;
@@ -3795,19 +3825,20 @@ class GraphBuilder {
3795
3825
  if (paramKey === "policy")
3796
3826
  continue;
3797
3827
  // Check if this param corresponds to an input handle
3798
- const inputTypeId = getInputTypeId(effectiveHandles.inputs, paramKey);
3799
- if (inputTypeId && inputTypeId.startsWith("enum:")) {
3800
- if (!validateEnumValue(inputTypeId, paramValue, n.nodeId)) {
3801
- const enumDef = this.registry.enums.get(inputTypeId);
3802
- const validValues = enumDef
3803
- ? Array.from(enumDef.valueToLabel.keys()).join(", ")
3804
- : "unknown";
3805
- pushIssue("error", "ENUM_VALUE_INVALID", `Node ${n.nodeId} param ${paramKey} has invalid enum value ${paramValue}. Valid values: ${validValues}`, {
3806
- nodeId: n.nodeId,
3807
- input: paramKey,
3808
- typeId: inputTypeId,
3809
- });
3810
- }
3828
+ const declaredTypes = getInputDeclaredTypes(effectiveHandles.inputs, paramKey);
3829
+ const enumTypes = getEnumTypes(declaredTypes);
3830
+ if (enumTypes.length > 0 &&
3831
+ typeof paramValue === "number" &&
3832
+ !enumTypes.some((et) => validateEnumValue(et, paramValue, n.nodeId))) {
3833
+ const enumDef = this.registry.enums.get(enumTypes[0]);
3834
+ const validValues = enumDef
3835
+ ? Array.from(enumDef.valueToLabel.keys()).join(", ")
3836
+ : "unknown";
3837
+ pushIssue("error", "ENUM_VALUE_INVALID", `Node ${n.nodeId} param ${paramKey} has invalid enum value ${paramValue}. Valid values: ${validValues}`, {
3838
+ nodeId: n.nodeId,
3839
+ input: paramKey,
3840
+ typeId: enumTypes.join("|"),
3841
+ });
3811
3842
  }
3812
3843
  }
3813
3844
  }
@@ -3816,19 +3847,20 @@ class GraphBuilder {
3816
3847
  if (resolved?.inputDefaults) {
3817
3848
  const effectiveHandles = getEffectiveHandles(n);
3818
3849
  for (const [handle, defaultValue] of Object.entries(resolved.inputDefaults)) {
3819
- const inputTypeId = getInputTypeId(effectiveHandles.inputs, handle);
3820
- if (inputTypeId && inputTypeId.startsWith("enum:")) {
3821
- if (!validateEnumValue(inputTypeId, defaultValue, n.nodeId)) {
3822
- const enumDef = this.registry.enums.get(inputTypeId);
3823
- const validValues = enumDef
3824
- ? Array.from(enumDef.valueToLabel.keys()).join(", ")
3825
- : "unknown";
3826
- pushIssue("warning", "ENUM_DEFAULT_INVALID", `Node ${n.nodeId} input default ${handle} has invalid enum value ${defaultValue}. Valid values: ${validValues}`, {
3827
- nodeId: n.nodeId,
3828
- input: handle,
3829
- typeId: inputTypeId,
3830
- });
3831
- }
3850
+ const declaredTypes = getInputDeclaredTypes(effectiveHandles.inputs, handle);
3851
+ const enumTypes = getEnumTypes(declaredTypes);
3852
+ if (enumTypes.length > 0 &&
3853
+ typeof defaultValue === "number" &&
3854
+ !enumTypes.some((et) => validateEnumValue(et, defaultValue, n.nodeId))) {
3855
+ const enumDef = this.registry.enums.get(enumTypes[0]);
3856
+ const validValues = enumDef
3857
+ ? Array.from(enumDef.valueToLabel.keys()).join(", ")
3858
+ : "unknown";
3859
+ pushIssue("warning", "ENUM_DEFAULT_INVALID", `Node ${n.nodeId} input default ${handle} has invalid enum value ${defaultValue}. Valid values: ${validValues}`, {
3860
+ nodeId: n.nodeId,
3861
+ input: handle,
3862
+ typeId: enumTypes.join("|"),
3863
+ });
3832
3864
  }
3833
3865
  }
3834
3866
  }
@@ -3928,8 +3960,8 @@ class GraphBuilder {
3928
3960
  inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
3929
3961
  // If the target input is declared as an array type, allow multi-inbound (runtime will append)
3930
3962
  if (dstNode) {
3931
- const declaredIn = getInputTypeId((effByNodeId.get(dstNode.nodeId) || { inputs: {} }).inputs, e.target.handle);
3932
- if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
3963
+ const declaredIn = getInputDeclaredTypes((effByNodeId.get(dstNode.nodeId) || { inputs: {} }).inputs, e.target.handle);
3964
+ if (hasArrayType(declaredIn)) {
3933
3965
  inboundArrayOk.add(inboundKey);
3934
3966
  }
3935
3967
  }
@@ -3958,10 +3990,10 @@ class GraphBuilder {
3958
3990
  const innerDesc = innerNode
3959
3991
  ? this.registry.nodes.get(innerNode.typeId)
3960
3992
  : undefined;
3961
- const typeId = innerDesc
3962
- ? getInputTypeId(innerDesc.inputs, map.handle)
3993
+ const typeIds = innerDesc
3994
+ ? getInputDeclaredTypes(innerDesc.inputs, map.handle)
3963
3995
  : undefined;
3964
- inputTypes[outerIn] = typeId ?? "untyped";
3996
+ inputTypes[outerIn] = typeIds ?? "unknown";
3965
3997
  }
3966
3998
  for (const [outerOut, map] of Object.entries(exposure.outputs)) {
3967
3999
  const innerNode = def.nodes.find((n) => n.nodeId === map.nodeId);
@@ -3970,7 +4002,7 @@ class GraphBuilder {
3970
4002
  : undefined;
3971
4003
  const typeId = innerDesc ? innerDesc.outputs[map.handle] : undefined;
3972
4004
  const single = Array.isArray(typeId) ? typeId[0] : typeId;
3973
- outputTypes[outerOut] = single ?? "untyped";
4005
+ outputTypes[outerOut] = single ?? "unknown";
3974
4006
  }
3975
4007
  return {
3976
4008
  id: nodeTypeId,