ultravisor 1.0.22 → 1.0.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultravisor",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "Cyclic process execution with ai integration.",
5
5
  "main": "source/Ultravisor.cjs",
6
6
  "bin": {
@@ -991,10 +991,23 @@ class UltravisorExecutionEngine extends libPictService
991
991
  tmpSourceValue = this._resolveTemplate(tmpConn.Data.Template, tmpTemplateContext);
992
992
  }
993
993
 
994
+ // Determine which settings key to write under. When a
995
+ // state connection explicitly declares `Data.StateKey`,
996
+ // honor it — this lets operations fan state into a
997
+ // setting whose name doesn't match any physical port
998
+ // (e.g. the storyboard's parameter-sweep connection
999
+ // routes a value-input's InputValue into the sweep
1000
+ // task's `ParameterSets` setting even though the sweep
1001
+ // only exposes event trigger ports). Falls through to
1002
+ // the target port name for backward compatibility.
1003
+ let tmpSettingsKey = (tmpConn.Data && typeof(tmpConn.Data.StateKey) === 'string' && tmpConn.Data.StateKey)
1004
+ ? tmpConn.Data.StateKey
1005
+ : tmpTargetPortName;
1006
+
994
1007
  // Write the resolved value into settings
995
- if (tmpTargetPortName && tmpSourceValue !== undefined)
1008
+ if (tmpSettingsKey && tmpSourceValue !== undefined)
996
1009
  {
997
- tmpSettings[tmpTargetPortName] = tmpSourceValue;
1010
+ tmpSettings[tmpSettingsKey] = tmpSourceValue;
998
1011
  }
999
1012
  }
1000
1013
 
@@ -760,6 +760,138 @@ suite
760
760
  });
761
761
  }
762
762
  );
763
+
764
+ test
765
+ (
766
+ 'State connection Data.StateKey should override target port name when resolving settings.',
767
+ function()
768
+ {
769
+ // The storyboard — long-form video operation wires
770
+ // a value-input's InputValue state output into a
771
+ // parameter-sweep task's `ParameterSets` setting
772
+ // via an event-trigger target port. The target
773
+ // port name can't match the setting name in that
774
+ // shape, so the connection declares
775
+ // `Data.StateKey: "ParameterSets"` and the engine
776
+ // has to honor it. Without the StateKey override,
777
+ // the value would land on `tmpSettings[<port name>]`
778
+ // and the sweep task's `pResolvedSettings.ParameterSets`
779
+ // would be undefined, causing "ParameterSets must be
780
+ // a JSON array." at runtime.
781
+ let tmpFable = createTestFable();
782
+ let tmpEngine = Object.values(tmpFable.servicesMap['UltravisorExecutionEngine'])[0];
783
+
784
+ let tmpNode = {
785
+ Hash: 'sweep-node',
786
+ Type: 'parameter-sweep',
787
+ Data: {},
788
+ Settings: {},
789
+ Ports:
790
+ [
791
+ { Direction: 'input', Hash: 'sweep-node-ei-begin', Label: 'BeginSweep', Side: 'left-bottom' }
792
+ ]
793
+ };
794
+
795
+ let tmpContext = {
796
+ TaskOutputs: {
797
+ 'value-input-node': { InputValue: [ { prompt: 'beat 1' }, { prompt: 'beat 2' } ] }
798
+ },
799
+ _ConnectionMap: {
800
+ stateTargets:
801
+ {
802
+ 'sweep-node':
803
+ [
804
+ {
805
+ Hash: 'state-conn',
806
+ ConnectionType: 'state',
807
+ SourceNodeHash: 'value-input-node',
808
+ SourcePortHash: 'value-input-node-so-InputValue',
809
+ TargetNodeHash: 'sweep-node',
810
+ TargetPortHash: 'sweep-node-ei-begin',
811
+ Data: { StateKey: 'ParameterSets' }
812
+ }
813
+ ]
814
+ }
815
+ },
816
+ _PortLabelMap:
817
+ {
818
+ 'value-input-node-so-InputValue': 'InputValue',
819
+ 'sweep-node-ei-begin': 'begin'
820
+ }
821
+ };
822
+
823
+ let tmpResolved = tmpEngine._resolveStateConnections('sweep-node', tmpNode, tmpContext);
824
+
825
+ // The StateKey override routes InputValue into the
826
+ // setting named ParameterSets, not into the target
827
+ // port's label ("begin").
828
+ Expect(Array.isArray(tmpResolved.ParameterSets)).to.equal(true);
829
+ Expect(tmpResolved.ParameterSets.length).to.equal(2);
830
+ Expect(tmpResolved.ParameterSets[0].prompt).to.equal('beat 1');
831
+ // The target port's label-named key should NOT
832
+ // have been populated when StateKey is present.
833
+ Expect(tmpResolved.begin).to.equal(undefined);
834
+ }
835
+ );
836
+
837
+ test
838
+ (
839
+ 'State connection without Data.StateKey should still route by target port name.',
840
+ function()
841
+ {
842
+ // Regression guard for the StateKey fallback: when
843
+ // the state connection has no StateKey, the engine
844
+ // must continue to use the target port name as the
845
+ // settings key (backward compatibility for every
846
+ // operation wired the old way, including the
847
+ // template-transform test above).
848
+ let tmpFable = createTestFable();
849
+ let tmpEngine = Object.values(tmpFable.servicesMap['UltravisorExecutionEngine'])[0];
850
+
851
+ let tmpNode = {
852
+ Hash: 'write-node',
853
+ Type: 'write-file',
854
+ Data: {},
855
+ Settings: {},
856
+ Ports:
857
+ [
858
+ { Direction: 'input', Hash: 'write-node-si-Content', Label: 'Content', Side: 'left-top' }
859
+ ]
860
+ };
861
+
862
+ let tmpContext = {
863
+ TaskOutputs: {
864
+ 'read-node': { FileContent: 'hello world' }
865
+ },
866
+ _ConnectionMap: {
867
+ stateTargets:
868
+ {
869
+ 'write-node':
870
+ [
871
+ {
872
+ Hash: 'legacy-state-conn',
873
+ ConnectionType: 'state',
874
+ SourceNodeHash: 'read-node',
875
+ SourcePortHash: 'read-node-so-FileContent',
876
+ TargetNodeHash: 'write-node',
877
+ TargetPortHash: 'write-node-si-Content'
878
+ // No Data.StateKey — fall through
879
+ }
880
+ ]
881
+ }
882
+ },
883
+ _PortLabelMap:
884
+ {
885
+ 'read-node-so-FileContent': 'FileContent',
886
+ 'write-node-si-Content': 'Content'
887
+ }
888
+ };
889
+
890
+ let tmpResolved = tmpEngine._resolveStateConnections('write-node', tmpNode, tmpContext);
891
+
892
+ Expect(tmpResolved.Content).to.equal('hello world');
893
+ }
894
+ );
763
895
  }
764
896
  );
765
897