@rkmodules/rules 0.0.170 → 0.0.172

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.
@@ -544,31 +544,6 @@ function toArray(a) {
544
544
  return Object.values(a).flat();
545
545
  }
546
546
 
547
- var getType = {
548
- name: "getType",
549
- label: "Get Type",
550
- description: "Gets the type of a value as a string",
551
- inputs: {
552
- input: { type: "any" },
553
- },
554
- outputs: {
555
- type: "string",
556
- },
557
- impl: function (inputs, params) { return __awaiter(void 0, void 0, void 0, function () {
558
- return __generator(this, function (_a) {
559
- return [2 /*return*/, {
560
- type: mapTree(inputs.input, function (x) {
561
- if (x === undefined)
562
- return "empty";
563
- if (x === null)
564
- return "empty";
565
- return typeof x;
566
- }),
567
- }];
568
- });
569
- }); },
570
- };
571
-
572
547
  // utilities
573
548
  /**
574
549
  * merges two trees by concatenating the branches with the same path
@@ -640,6 +615,27 @@ function hasReference(value) {
640
615
  function parseReference(ref) {
641
616
  return ref.slice(1, -1);
642
617
  }
618
+ /** Reference paths using the same literal-array and token rules as interpolation. */
619
+ function getReferencePaths(value) {
620
+ var paths = new Set();
621
+ var visit = function (value) {
622
+ if (Array.isArray(value)) {
623
+ if (!value.every(Array.isArray))
624
+ value.forEach(visit);
625
+ }
626
+ else if (value && typeof value === "object") {
627
+ Object.values(value).forEach(visit);
628
+ }
629
+ else if (typeof value === "string") {
630
+ (value.match(/<.*?>/g) || []).forEach(function (token) {
631
+ if (isReference(token))
632
+ paths.add(parseReference(token));
633
+ });
634
+ }
635
+ };
636
+ visit(value);
637
+ return __spreadArray([], __read(paths), false);
638
+ }
643
639
  function uid$1() {
644
640
  return Math.random().toString(36).substring(2, 15);
645
641
  }
@@ -746,7 +742,250 @@ function interpolate(inputs, scope) {
746
742
  }));
747
743
  }
748
744
 
745
+ // Bounds count entries, not bytes. Each location keeps only its last successful result.
746
+ var MAX_SCOPES = 32;
747
+ var MAX_ENTRIES = 1024;
748
+ var cachePath = function (path) { return JSON.stringify(path); };
749
+ function sameRecord(a, b) {
750
+ var keys = Object.keys(a);
751
+ var otherKeys = Object.keys(b);
752
+ return keys.length === otherKeys.length && keys.every(function (key, i) { return key === otherKeys[i] && Object.is(a[key], b[key]); });
753
+ }
754
+ /** Compare immutable graph configuration, never runtime dependency trees.
755
+ * Preserve key order (interpolation observes it); opaque objects retain identity
756
+ * semantics. Cyclic configurations conservatively miss instead of recursing forever.
757
+ */
758
+ function sameConfiguration(a, b, ancestors) {
759
+ if (Object.is(a, b))
760
+ return true;
761
+ if (!a || !b || typeof a !== "object" || typeof b !== "object")
762
+ return false;
763
+ var prototype = Object.getPrototypeOf(a);
764
+ if (prototype !== Object.getPrototypeOf(b))
765
+ return false;
766
+ if (Array.isArray(a)) {
767
+ if (!Array.isArray(b) || a.length !== b.length)
768
+ return false;
769
+ }
770
+ else if (prototype !== Object.prototype && prototype !== null)
771
+ return false;
772
+ if ((ancestors === null || ancestors === void 0 ? void 0 : ancestors.has(a)) || (ancestors === null || ancestors === void 0 ? void 0 : ancestors.has(b)))
773
+ return false;
774
+ var keys = Reflect.ownKeys(a);
775
+ var otherKeys = Reflect.ownKeys(b);
776
+ if (keys.length !== otherKeys.length)
777
+ return false;
778
+ ancestors = ancestors || new Set();
779
+ ancestors.add(a);
780
+ ancestors.add(b);
781
+ try {
782
+ return keys.every(function (key, i) {
783
+ if (key !== otherKeys[i])
784
+ return false;
785
+ var left = Object.getOwnPropertyDescriptor(a, key);
786
+ var right = Object.getOwnPropertyDescriptor(b, key);
787
+ // Accessors may read external state; do not invoke them for comparison.
788
+ return "value" in left && "value" in right &&
789
+ left.enumerable === right.enumerable &&
790
+ sameConfiguration(left.value, right.value, ancestors);
791
+ });
792
+ }
793
+ finally {
794
+ ancestors.delete(a);
795
+ ancestors.delete(b);
796
+ }
797
+ }
798
+ function put(map, key, value) {
799
+ map.delete(key);
800
+ map.set(key, value);
801
+ if (map.size > MAX_ENTRIES)
802
+ map.delete(map.keys().next().value);
803
+ }
804
+ /** Engine-owned caches; execution state is carried explicitly across async boundaries. */
805
+ var ExecutionCache = /** @class */ (function () {
806
+ function ExecutionCache() {
807
+ this.scopes = new Map();
808
+ }
809
+ ExecutionCache.prototype.begin = function (graph, options) {
810
+ var _a;
811
+ var cacheKey = (_a = options.cacheKey) !== null && _a !== void 0 ? _a : "";
812
+ var key = cachePath([graph, cacheKey]);
813
+ var scope = this.scopes.get(key);
814
+ if (!scope) {
815
+ scope = { graph: graph, cacheKey: cacheKey, generation: 0, run: 0,
816
+ interpolation: new Map(), pure: new Map(), preview: new Map() };
817
+ }
818
+ this.scopes.delete(key);
819
+ this.scopes.set(key, scope);
820
+ if (this.scopes.size > MAX_SCOPES) {
821
+ var oldest = this.scopes.keys().next().value;
822
+ this.scopes.get(oldest).generation++;
823
+ this.scopes.delete(oldest);
824
+ }
825
+ return { scope: scope, generation: scope.generation, run: ++scope.run,
826
+ options: __assign({}, options), visited: new Set() };
827
+ };
828
+ ExecutionCache.prototype.current = function (run) {
829
+ return run.generation === run.scope.generation && run.run === run.scope.run;
830
+ };
831
+ ExecutionCache.prototype.clear = function (options) {
832
+ var e_1, _a;
833
+ if (options === void 0) { options = {}; }
834
+ try {
835
+ for (var _b = __values(this.scopes.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
836
+ var scope = _c.value;
837
+ if (options.graph !== undefined && scope.graph !== options.graph)
838
+ continue;
839
+ if (options.cacheKey !== undefined && scope.cacheKey !== options.cacheKey)
840
+ continue;
841
+ // Pending runs may finish for their callers, but cannot refill cleared caches.
842
+ scope.generation++;
843
+ if (!options.kind || options.kind === "all") {
844
+ scope.interpolation.clear();
845
+ scope.pure.clear();
846
+ scope.preview.clear();
847
+ }
848
+ else {
849
+ scope[options.kind].clear();
850
+ }
851
+ }
852
+ }
853
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
854
+ finally {
855
+ try {
856
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
857
+ }
858
+ finally { if (e_1) throw e_1.error; }
859
+ }
860
+ };
861
+ ExecutionCache.prototype.visit = function (run, path) {
862
+ run.visited.add(cachePath(path));
863
+ };
864
+ ExecutionCache.prototype.finish = function (run) {
865
+ var e_2, _a, e_3, _b;
866
+ if (!this.current(run))
867
+ return;
868
+ try {
869
+ for (var _c = __values([run.scope.interpolation, run.scope.pure, run.scope.preview]), _d = _c.next(); !_d.done; _d = _c.next()) {
870
+ var map = _d.value;
871
+ try {
872
+ for (var _e = (e_3 = void 0, __values(map.keys())), _f = _e.next(); !_f.done; _f = _e.next()) {
873
+ var key = _f.value;
874
+ if (!run.visited.has(key))
875
+ map.delete(key);
876
+ }
877
+ }
878
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
879
+ finally {
880
+ try {
881
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
882
+ }
883
+ finally { if (e_3) throw e_3.error; }
884
+ }
885
+ }
886
+ }
887
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
888
+ finally {
889
+ try {
890
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
891
+ }
892
+ finally { if (e_2) throw e_2.error; }
893
+ }
894
+ };
895
+ ExecutionCache.prototype.interpolate = function (run, path, raw, scope, configuration) {
896
+ if (configuration === void 0) { configuration = false; }
897
+ var key = cachePath(path);
898
+ run.visited.add(key);
899
+ if (!this.current(run))
900
+ return interpolate(raw, scope);
901
+ var previous = run.scope.interpolation.get(key);
902
+ var ports = new Map();
903
+ var entries = Object.entries(raw).map(function (_a) {
904
+ var _b = __read(_a, 2), name = _b[0], value = _b[1];
905
+ // Deep references depend on the base tree's identity. Unrelated scope
906
+ // entries never invalidate this port; a fresh execution context is fine.
907
+ var old = previous === null || previous === void 0 ? void 0 : previous.ports.get(name);
908
+ var sameRaw = old && (configuration
909
+ ? sameConfiguration(old.raw, value) : Object.is(old.raw, value));
910
+ var paths = sameRaw ? old.paths :
911
+ getReferencePaths(value).map(function (path) { return path.split(".").slice(0, 2).join("."); });
912
+ var dependencies = paths.map(function (path) { return getValue$1(scope, path); });
913
+ var same = sameRaw &&
914
+ old.dependencies.length === dependencies.length &&
915
+ dependencies.every(function (value, i) { return Object.is(value, old.dependencies[i]); });
916
+ // Remember the latest raw reference so subsequent runs take the fast path.
917
+ var port = same ? __assign(__assign({}, old), { raw: value }) : {
918
+ raw: value,
919
+ paths: paths,
920
+ dependencies: dependencies,
921
+ tree: interpolate({ value: value }, scope).value,
922
+ };
923
+ ports.set(name, port);
924
+ return [name, port.tree];
925
+ });
926
+ var next = Object.fromEntries(entries);
927
+ var result = previous && sameRecord(previous.result, next) ? previous.result : next;
928
+ put(run.scope.interpolation, key, { ports: ports, result: result });
929
+ return result;
930
+ };
931
+ ExecutionCache.prototype.key = function (call) {
932
+ call.inputs; call.params; var metadata = __rest(call, ["inputs", "params"]);
933
+ return metadata;
934
+ };
935
+ ExecutionCache.prototype.get = function (run, path, kind, fn, call, inputs, params) {
936
+ var key = cachePath(path);
937
+ run.visited.add(key);
938
+ if (!this.current(run))
939
+ return;
940
+ var entry = run.scope[kind].get(key);
941
+ if (entry && entry.definition === fn && entry.impl === fn.impl &&
942
+ sameConfiguration(entry.metadata, this.key(call)) && sameRecord(entry.inputs, inputs) &&
943
+ sameConfiguration(entry.params, params)) {
944
+ entry.metadata = this.key(call);
945
+ entry.params = __assign({}, params);
946
+ put(run.scope[kind], key, entry);
947
+ return entry;
948
+ }
949
+ };
950
+ ExecutionCache.prototype.set = function (run, path, kind, fn, call, inputs, params, result, impl, createdAt) {
951
+ if (!this.current(run))
952
+ return;
953
+ var key = cachePath(path);
954
+ run.visited.add(key);
955
+ put(run.scope[kind], key, { definition: fn, impl: impl, metadata: this.key(call),
956
+ inputs: __assign({}, inputs), params: __assign({}, params), result: result, info: { kind: kind, createdAt: createdAt } });
957
+ };
958
+ return ExecutionCache;
959
+ }());
960
+
961
+ var getType = {
962
+ pure: true,
963
+ name: "getType",
964
+ label: "Get Type",
965
+ description: "Gets the type of a value as a string",
966
+ inputs: {
967
+ input: { type: "any" },
968
+ },
969
+ outputs: {
970
+ type: "string",
971
+ },
972
+ impl: function (inputs, params) { return __awaiter(void 0, void 0, void 0, function () {
973
+ return __generator(this, function (_a) {
974
+ return [2 /*return*/, {
975
+ type: mapTree(inputs.input, function (x) {
976
+ if (x === undefined)
977
+ return "empty";
978
+ if (x === null)
979
+ return "empty";
980
+ return typeof x;
981
+ }),
982
+ }];
983
+ });
984
+ }); },
985
+ };
986
+
749
987
  var getValue = {
988
+ pure: true,
750
989
  name: "getValue",
751
990
  label: "Get Value",
752
991
  description: "Get a value from an object at a specified path.",
@@ -777,6 +1016,8 @@ var getValue = {
777
1016
  };
778
1017
 
779
1018
  var log = {
1019
+ // Logging is a required side effect, even when the data is unchanged.
1020
+ pure: false,
780
1021
  name: "log",
781
1022
  label: "Log",
782
1023
  description: "Logs the input data to the console",
@@ -801,6 +1042,7 @@ var log = {
801
1042
  * value primitive function
802
1043
  */
803
1044
  var value = {
1045
+ pure: true,
804
1046
  name: "value",
805
1047
  label: "Value",
806
1048
  description: "Outputs a constant value of a specified type",
@@ -852,6 +1094,7 @@ function isNumber() {
852
1094
  }
853
1095
 
854
1096
  var add$1 = {
1097
+ pure: true,
855
1098
  name: "add",
856
1099
  label: "Add",
857
1100
  description: "Add two numbers or strings",
@@ -897,6 +1140,8 @@ function evaluate(expression, scope) {
897
1140
  * TODO: need rank polymorhism on the tree item sizes
898
1141
  */
899
1142
  var calc = {
1143
+ // Expressions can use randomness; failed evaluations also log diagnostics.
1144
+ pure: false,
900
1145
  name: "calc",
901
1146
  label: "Calc",
902
1147
  nodeType: "Calc",
@@ -1035,6 +1280,7 @@ function getUnboundSymbols(expr, scope, extraBound) {
1035
1280
  }
1036
1281
 
1037
1282
  var cosDeg = {
1283
+ pure: true,
1038
1284
  name: "cosDeg",
1039
1285
  label: "Cosine",
1040
1286
  description: "Cosine of angle in degrees",
@@ -1058,6 +1304,7 @@ var cosDeg = {
1058
1304
  };
1059
1305
 
1060
1306
  var divide = {
1307
+ pure: true,
1061
1308
  name: "divide",
1062
1309
  label: "Divide",
1063
1310
  description: "Divide two numbers",
@@ -1084,6 +1331,7 @@ var divide = {
1084
1331
  };
1085
1332
 
1086
1333
  var equalTo = {
1334
+ pure: true,
1087
1335
  name: "equalTo",
1088
1336
  label: "Equal to",
1089
1337
  description: "Compare equal and not equal",
@@ -1114,6 +1362,7 @@ var equalTo = {
1114
1362
  };
1115
1363
 
1116
1364
  var greaterThan = {
1365
+ pure: true,
1117
1366
  name: "greaterThan",
1118
1367
  label: "Greater Than",
1119
1368
  description: "Compare on greater than or greater than or equal to",
@@ -1144,6 +1393,7 @@ var greaterThan = {
1144
1393
  };
1145
1394
 
1146
1395
  var lessThan = {
1396
+ pure: true,
1147
1397
  name: "lessThan",
1148
1398
  label: "Less Than",
1149
1399
  description: "Compare on less than or less than or equal to",
@@ -1174,6 +1424,7 @@ var lessThan = {
1174
1424
  };
1175
1425
 
1176
1426
  var modulo = {
1427
+ pure: true,
1177
1428
  name: "modulo",
1178
1429
  label: "Modulo",
1179
1430
  description: "Division remainder of two numbers",
@@ -1198,6 +1449,7 @@ var modulo = {
1198
1449
  };
1199
1450
 
1200
1451
  var multiply = {
1452
+ pure: true,
1201
1453
  name: "multiply",
1202
1454
  label: "Multiply",
1203
1455
  description: "Multiply two numbers",
@@ -1222,6 +1474,7 @@ var multiply = {
1222
1474
  };
1223
1475
 
1224
1476
  var pow = {
1477
+ pure: true,
1225
1478
  name: "pow",
1226
1479
  label: "Power",
1227
1480
  description: "Power a number",
@@ -1246,6 +1499,7 @@ var pow = {
1246
1499
  };
1247
1500
 
1248
1501
  var sinDeg = {
1502
+ pure: true,
1249
1503
  name: "sinDeg",
1250
1504
  label: "Sine",
1251
1505
  description: "Sine of angle in degrees",
@@ -1269,6 +1523,7 @@ var sinDeg = {
1269
1523
  };
1270
1524
 
1271
1525
  var sqrt = {
1526
+ pure: true,
1272
1527
  name: "sqrt",
1273
1528
  label: "Square root",
1274
1529
  description: "Square root of a number",
@@ -1294,6 +1549,7 @@ var sqrt = {
1294
1549
  };
1295
1550
 
1296
1551
  var subtract$1 = {
1552
+ pure: true,
1297
1553
  name: "subtract",
1298
1554
  label: "Subtract",
1299
1555
  description: "Subtract two numbers",
@@ -1318,6 +1574,7 @@ var subtract$1 = {
1318
1574
  };
1319
1575
 
1320
1576
  var tanDeg = {
1577
+ pure: true,
1321
1578
  name: "tanDeg",
1322
1579
  label: "Tangent",
1323
1580
  description: "Tangent of angle in degrees",
@@ -1341,6 +1598,7 @@ var tanDeg = {
1341
1598
  };
1342
1599
 
1343
1600
  var ceil = {
1601
+ pure: true,
1344
1602
  name: "ceil",
1345
1603
  label: "Round up",
1346
1604
  description: "Round a number up to the nearest integer",
@@ -1369,6 +1627,7 @@ var ceil = {
1369
1627
  };
1370
1628
 
1371
1629
  var floor = {
1630
+ pure: true,
1372
1631
  name: "floor",
1373
1632
  label: "Round down",
1374
1633
  description: "Round a number down to the nearest integer",
@@ -1397,6 +1656,7 @@ var floor = {
1397
1656
  };
1398
1657
 
1399
1658
  var round = {
1659
+ pure: true,
1400
1660
  name: "round",
1401
1661
  label: "Round",
1402
1662
  description: "Round a number to the nearest integer",
@@ -1425,6 +1685,7 @@ var round = {
1425
1685
  };
1426
1686
 
1427
1687
  var sum = {
1688
+ pure: true,
1428
1689
  name: "sum",
1429
1690
  label: "Sum",
1430
1691
  description: "Sum of all items in a group",
@@ -1475,6 +1736,7 @@ var primitives$8 = (_a$8 = {},
1475
1736
  _a$8);
1476
1737
 
1477
1738
  var joinStrings = {
1739
+ pure: true,
1478
1740
  name: "joinStrings",
1479
1741
  label: "Join Strings",
1480
1742
  description: "Join all strings in a group",
@@ -1503,6 +1765,7 @@ var joinStrings = {
1503
1765
  };
1504
1766
 
1505
1767
  var stringEqualTo = {
1768
+ pure: true,
1506
1769
  name: "stringEqualTo",
1507
1770
  label: "String Equal to",
1508
1771
  description: "Compare string equal and not equal",
@@ -1529,6 +1792,7 @@ var stringEqualTo = {
1529
1792
  };
1530
1793
 
1531
1794
  var stringTemplate = {
1795
+ pure: true,
1532
1796
  name: "stringTemplate",
1533
1797
  label: "String Template",
1534
1798
  description: "Assembles a string from inputs, use ${...} to interpolate inputs",
@@ -1568,6 +1832,7 @@ var stringTemplate = {
1568
1832
  };
1569
1833
 
1570
1834
  var toString = {
1835
+ pure: true,
1571
1836
  name: "toString",
1572
1837
  label: "To String",
1573
1838
  description: "convert to string",
@@ -1599,6 +1864,7 @@ var primitives$7 = (_a$7 = {},
1599
1864
  _a$7);
1600
1865
 
1601
1866
  var filterList = {
1867
+ pure: true,
1602
1868
  name: "filterList",
1603
1869
  label: "Filter List",
1604
1870
  description: "Filters the list by a boolean value",
@@ -1642,6 +1908,7 @@ var filterList = {
1642
1908
  };
1643
1909
 
1644
1910
  var listItem = {
1911
+ pure: true,
1645
1912
  name: "listItem",
1646
1913
  label: "List Item",
1647
1914
  description: "Retrieve the items from the list at the specified indices",
@@ -1690,6 +1957,7 @@ var listItem = {
1690
1957
  };
1691
1958
 
1692
1959
  var listLength = {
1960
+ pure: true,
1693
1961
  name: "listLength",
1694
1962
  label: "List Length",
1695
1963
  description: "Get the length of a list",
@@ -1711,6 +1979,7 @@ var listLength = {
1711
1979
  };
1712
1980
 
1713
1981
  var concat = {
1982
+ pure: true,
1714
1983
  name: "concat",
1715
1984
  label: "Concat Lists",
1716
1985
  description: "Concatenates two lists back to back",
@@ -1737,6 +2006,7 @@ var concat = {
1737
2006
  };
1738
2007
 
1739
2008
  var relativeItem = {
2009
+ pure: true,
1740
2010
  name: "relativeItem",
1741
2011
  label: "Relative Item",
1742
2012
  description: "Pairs each item in the tree with its next item",
@@ -1775,6 +2045,7 @@ var relativeItem = {
1775
2045
  };
1776
2046
 
1777
2047
  var relativeItems = {
2048
+ pure: true,
1778
2049
  name: "relativeItems",
1779
2050
  label: "Relative Items",
1780
2051
  description: "Pairs each item in the tree with an item in another tree at a specified offset",
@@ -1820,6 +2091,7 @@ function isNumStr(str) {
1820
2091
  return !isNaN(Number(str));
1821
2092
  }
1822
2093
  var listInput = {
2094
+ pure: true,
1823
2095
  name: "listInput",
1824
2096
  label: "List Input",
1825
2097
  description: "Input a list",
@@ -1860,6 +2132,7 @@ var listInput = {
1860
2132
  };
1861
2133
 
1862
2134
  var uniqueItems = {
2135
+ pure: true,
1863
2136
  name: "uniqueItems",
1864
2137
  label: "Unique Items",
1865
2138
  description: "Removes duplicate items from a list",
@@ -1896,6 +2169,7 @@ var uniqueItems = {
1896
2169
  };
1897
2170
 
1898
2171
  var coalesce = {
2172
+ pure: true,
1899
2173
  name: "coalesce",
1900
2174
  label: "Coalesce Items",
1901
2175
  description: "Outputs the first item if it's not empty, otherwise the second item",
@@ -1920,6 +2194,7 @@ var coalesce = {
1920
2194
  };
1921
2195
 
1922
2196
  var destructureList = {
2197
+ pure: true,
1923
2198
  name: "destructureList",
1924
2199
  label: "Destructure List",
1925
2200
  description: "Separates an list into a part with the specified values and the rest",
@@ -1944,7 +2219,7 @@ var destructureList = {
1944
2219
  var included = [];
1945
2220
  var excluded = [];
1946
2221
  var matches = [];
1947
- console.log("Destructuring list", list, items);
2222
+ // console.log("Destructuring list", list, items);
1948
2223
  list.forEach(function (value) {
1949
2224
  var m = items.includes(value);
1950
2225
  matches.push(m);
@@ -1990,6 +2265,7 @@ var primitives$6 = (_a$6 = {},
1990
2265
  _a$6);
1991
2266
 
1992
2267
  var splitGroup = {
2268
+ pure: true,
1993
2269
  name: "splitGroup",
1994
2270
  label: "Split Group",
1995
2271
  description: "Splits every item of a group into its own group",
@@ -2009,6 +2285,7 @@ var splitGroup = {
2009
2285
  };
2010
2286
 
2011
2287
  var mergeGroup = {
2288
+ pure: true,
2012
2289
  name: "mergeGroup",
2013
2290
  label: "Merge Group",
2014
2291
  description: "Merge the deepest groups into their parent.",
@@ -2034,6 +2311,7 @@ var mergeGroup = {
2034
2311
  };
2035
2312
 
2036
2313
  var cartesianGroups = {
2314
+ pure: true,
2037
2315
  name: "cartesianGroups",
2038
2316
  label: "Cartesian Groups",
2039
2317
  description: "Duplicate groups such that all pairings are created.",
@@ -2073,6 +2351,7 @@ var cartesianGroups = {
2073
2351
  };
2074
2352
 
2075
2353
  var mergeTree = {
2354
+ pure: true,
2076
2355
  name: "mergeTree",
2077
2356
  label: "Merge Tree",
2078
2357
  description: "Merge groups with the same index into a single tree.",
@@ -2101,6 +2380,7 @@ var mergeTree = {
2101
2380
  };
2102
2381
 
2103
2382
  var simplifyTree = {
2383
+ pure: true,
2104
2384
  name: "simplifyTree",
2105
2385
  label: "Simplify Groups",
2106
2386
  description: "Simplify a tree by removing redundant groups.",
@@ -2123,6 +2403,7 @@ var simplifyTree = {
2123
2403
  };
2124
2404
 
2125
2405
  var groupAll = {
2406
+ pure: true,
2126
2407
  name: "groupAll",
2127
2408
  label: "Group All",
2128
2409
  description: "Adds a group around all groups in the input tree",
@@ -2147,6 +2428,7 @@ var groupAll = {
2147
2428
  };
2148
2429
 
2149
2430
  var normalizeTree = {
2431
+ pure: true,
2150
2432
  name: "normalizeTree",
2151
2433
  label: "Normalize Groups",
2152
2434
  description: "Normalize a tree to consequtive groups and removed empty groups.",
@@ -2176,6 +2458,7 @@ var normalizeTree = {
2176
2458
  };
2177
2459
 
2178
2460
  var filterTree = {
2461
+ pure: true,
2179
2462
  name: "filterTree",
2180
2463
  label: "Filter Tree",
2181
2464
  description: "Filter groups by path matchers",
@@ -2221,6 +2504,7 @@ var filterTree = {
2221
2504
  };
2222
2505
 
2223
2506
  var treeItem = {
2507
+ pure: true,
2224
2508
  name: "treeItem",
2225
2509
  label: "Tree Item",
2226
2510
  description: "Retrieve the groups from a tree at specified indexes",
@@ -2263,6 +2547,7 @@ var primitives$5 = (_a$5 = {},
2263
2547
  _a$5);
2264
2548
 
2265
2549
  var series = {
2550
+ pure: true,
2266
2551
  name: "series",
2267
2552
  label: "Series",
2268
2553
  description: "Create a series of numbers.",
@@ -2303,6 +2588,7 @@ var primitives$4 = (_a$4 = {},
2303
2588
  _a$4);
2304
2589
 
2305
2590
  var booleanAnd = {
2591
+ pure: true,
2306
2592
  name: "booleanAnd",
2307
2593
  label: "And",
2308
2594
  description: "Returns true if both of the items are true",
@@ -2326,6 +2612,7 @@ var booleanAnd = {
2326
2612
  };
2327
2613
 
2328
2614
  var booleanNot = {
2615
+ pure: true,
2329
2616
  name: "booleanNot",
2330
2617
  label: "Not",
2331
2618
  description: "Inverts the input boolean value",
@@ -2348,6 +2635,7 @@ var booleanNot = {
2348
2635
  };
2349
2636
 
2350
2637
  var booleanOr = {
2638
+ pure: true,
2351
2639
  name: "booleanOr",
2352
2640
  label: "Or",
2353
2641
  description: "Returns true if one of the items is true",
@@ -2371,6 +2659,7 @@ var booleanOr = {
2371
2659
  };
2372
2660
 
2373
2661
  var groupAnd = {
2662
+ pure: true,
2374
2663
  name: "groupAnd",
2375
2664
  label: "Group and",
2376
2665
  description: "Returns true if all of the items in a group are true",
@@ -2400,6 +2689,7 @@ var groupAnd = {
2400
2689
  };
2401
2690
 
2402
2691
  var groupOr = {
2692
+ pure: true,
2403
2693
  name: "groupOr",
2404
2694
  label: "Group or",
2405
2695
  description: "Returns true if any of the items in a group are true",
@@ -2429,6 +2719,7 @@ var groupOr = {
2429
2719
  };
2430
2720
 
2431
2721
  var groupStats = {
2722
+ pure: true,
2432
2723
  name: "groupStats",
2433
2724
  label: "Group stats",
2434
2725
  description: "Returns statistics about a group of numbers",
@@ -2493,6 +2784,7 @@ var primitives$3 = (_a$3 = {},
2493
2784
  _a$3);
2494
2785
 
2495
2786
  var inputs = {
2787
+ pure: true,
2496
2788
  name: "inputs",
2497
2789
  label: "Input",
2498
2790
  description: "Data input.",
@@ -2509,6 +2801,7 @@ var inputs = {
2509
2801
  };
2510
2802
 
2511
2803
  var output = {
2804
+ pure: true,
2512
2805
  name: "output",
2513
2806
  label: "Output",
2514
2807
  description: "Data output.",
@@ -2529,6 +2822,7 @@ var primitives$2 = (_a$2 = {},
2529
2822
  _a$2);
2530
2823
 
2531
2824
  var mergeObject = {
2825
+ pure: true,
2532
2826
  name: "mergeObject",
2533
2827
  label: "Merge Object",
2534
2828
  description: "Merges objects in groups",
@@ -2557,6 +2851,7 @@ var mergeObject = {
2557
2851
  };
2558
2852
 
2559
2853
  var object = {
2854
+ pure: true,
2560
2855
  name: "object",
2561
2856
  label: "Object",
2562
2857
  description: "Creates an object",
@@ -2584,6 +2879,7 @@ var object = {
2584
2879
  };
2585
2880
 
2586
2881
  var listToArray = {
2882
+ pure: true,
2587
2883
  name: "listToArray",
2588
2884
  label: "To Array",
2589
2885
  description: "Converts each branch to an array",
@@ -2606,6 +2902,7 @@ var listToArray = {
2606
2902
  };
2607
2903
 
2608
2904
  var objectEntries = {
2905
+ pure: true,
2609
2906
  name: "objectEntries",
2610
2907
  label: "Entries",
2611
2908
  description: "Gets keys and pairs of an object",
@@ -2639,6 +2936,7 @@ var objectEntries = {
2639
2936
  };
2640
2937
 
2641
2938
  var arrayToList = {
2939
+ pure: true,
2642
2940
  name: "arrayToList",
2643
2941
  label: "Array to List",
2644
2942
  description: "Separates array",
@@ -2667,6 +2965,7 @@ var arrayToList = {
2667
2965
  };
2668
2966
 
2669
2967
  var explodeObject = {
2968
+ pure: true,
2670
2969
  name: "explodeObject",
2671
2970
  label: "Explode Object",
2672
2971
  description: "Separates all fields of an object",
@@ -2726,6 +3025,7 @@ var explodeObject = {
2726
3025
  };
2727
3026
 
2728
3027
  var setField = {
3028
+ pure: true,
2729
3029
  name: "setField",
2730
3030
  label: "Set Field",
2731
3031
  description: "Sets a field in an object to a specified value.",
@@ -2765,6 +3065,7 @@ var setField = {
2765
3065
  };
2766
3066
 
2767
3067
  var destructureObject = {
3068
+ pure: true,
2768
3069
  name: "destructureObject",
2769
3070
  label: "Destructure Object",
2770
3071
  description: "Separates an object into a part with the specified fields and the rest",
@@ -2834,6 +3135,8 @@ var primitives$1 = (_a$1 = {},
2834
3135
  var INFERRED = "inferred";
2835
3136
  var ary$1 = __spreadArray([INFERRED], __read(Intl.supportedValuesOf("timeZone")), false);
2836
3137
  var parseDate = {
3138
+ // Empty values fall back to the clock; parsing also logs to the console.
3139
+ pure: false,
2837
3140
  name: "parseDate",
2838
3141
  label: "Parse",
2839
3142
  description: "Parses a date string",
@@ -2871,6 +3174,8 @@ var parseDate = {
2871
3174
 
2872
3175
  var ary = Intl.supportedValuesOf("timeZone");
2873
3176
  var formatDate = {
3177
+ // Empty values fall back to the clock; omitted options use host locale/timezone.
3178
+ pure: false,
2874
3179
  name: "formatDate",
2875
3180
  label: "Format",
2876
3181
  description: "Formats a date",
@@ -2990,6 +3295,7 @@ var formatDate = {
2990
3295
  };
2991
3296
 
2992
3297
  var duration = {
3298
+ pure: true,
2993
3299
  name: "duration",
2994
3300
  label: "Duration",
2995
3301
  description: "Creates a duration",
@@ -3030,6 +3336,8 @@ var duration = {
3030
3336
  };
3031
3337
 
3032
3338
  var isoDate = {
3339
+ // Empty values fall back to the current clock.
3340
+ pure: false,
3033
3341
  name: "isoDate",
3034
3342
  label: "To ISO",
3035
3343
  description: "ISO 8601 format of a date",
@@ -3055,6 +3363,8 @@ var isoDate = {
3055
3363
  };
3056
3364
 
3057
3365
  var today = {
3366
+ // Reads the current clock on every evaluation.
3367
+ pure: false,
3058
3368
  name: "today",
3059
3369
  label: "Today",
3060
3370
  description: "The date of today",
@@ -3121,31 +3431,49 @@ function normalizeVarDef(varDef) {
3121
3431
  return varDef;
3122
3432
  }
3123
3433
  function getReferences(inputs) {
3124
- var references = [];
3125
- Object.entries(inputs).forEach(function (_a) {
3434
+ return Object.entries(inputs).flatMap(function (_a) {
3126
3435
  var _b = __read(_a, 2), name = _b[0], value = _b[1];
3127
- var values = Array.isArray(value) ? value : [value];
3128
- values.forEach(function (value) {
3129
- if (!isReference(value)) {
3130
- return;
3131
- }
3132
- var path = parseReference(value);
3436
+ return getReferencePaths(value).map(function (path) {
3133
3437
  var parts = path.split(".");
3134
- references.push({
3438
+ return {
3135
3439
  name: name,
3136
3440
  path: path,
3137
3441
  parts: parts,
3138
3442
  refNode: parts[0],
3139
3443
  refField: parts.slice(1).join("."),
3140
- });
3444
+ };
3141
3445
  });
3142
3446
  });
3143
- return references;
3144
3447
  }
3145
- function topSort(fn) {
3448
+ function getExecutionOrder(fn) {
3146
3449
  var ordered = [];
3147
3450
  var visiting = new Set();
3148
3451
  var done = new Set();
3452
+ var body = fn.body || {};
3453
+ var nodeIds = new Map();
3454
+ Object.keys(body).forEach(function (id) {
3455
+ var referenceId = safeName(id);
3456
+ if (referenceId === "inputs") {
3457
+ throw new Error("Node ID \"".concat(id, "\" is reserved for graph inputs"));
3458
+ }
3459
+ if (nodeIds.has(referenceId)) {
3460
+ throw new Error("Ambiguous node reference \"".concat(referenceId, "\": \"").concat(nodeIds.get(referenceId), "\" and \"").concat(id, "\""));
3461
+ }
3462
+ nodeIds.set(referenceId, id);
3463
+ });
3464
+ var resolve = function (ref, location) {
3465
+ if (ref.parts.length < 2 || ref.parts.some(function (part) { return !part; })) {
3466
+ throw new Error("Invalid reference <".concat(ref.path, "> in ").concat(location, ".").concat(ref.name));
3467
+ }
3468
+ // Public inputs can be supplied at runtime without static definitions.
3469
+ if (ref.refNode === "inputs")
3470
+ return;
3471
+ var id = nodeIds.get(ref.refNode);
3472
+ if (id === undefined) {
3473
+ throw new Error("Reference <".concat(ref.path, "> in ").concat(location, ".").concat(ref.name, " points to missing node \"").concat(ref.refNode, "\""));
3474
+ }
3475
+ return id;
3476
+ };
3149
3477
  var visit = function (id) {
3150
3478
  // if already done, return
3151
3479
  if (done.has(id))
@@ -3156,19 +3484,11 @@ function topSort(fn) {
3156
3484
  }
3157
3485
  // mark as visiting
3158
3486
  visiting.add(id);
3159
- var fnCall = fn.body[id];
3160
- if (!fnCall) {
3161
- console.warn("Function ".concat(id, " not found in body"), fn);
3162
- }
3163
- // check the inputs and visit them
3164
- Object.values(fnCall.inputs || {}).forEach(function (input) {
3165
- if (isReference(input)) {
3166
- var _a = __read(parseReference(input).split("."), 1), id_1 = _a[0];
3167
- // visit inputs
3168
- if (id_1 !== "inputs") {
3169
- visit(id_1);
3170
- }
3171
- }
3487
+ var fnCall = body[id];
3488
+ getReferences(fnCall.inputs || {}).forEach(function (ref) {
3489
+ var dependency = resolve(ref, "node \"".concat(id, "\" input"));
3490
+ if (dependency !== undefined)
3491
+ visit(dependency);
3172
3492
  });
3173
3493
  // push to end of the list
3174
3494
  ordered.push(id);
@@ -3177,7 +3497,12 @@ function topSort(fn) {
3177
3497
  // mark as done
3178
3498
  done.add(id);
3179
3499
  };
3180
- Object.keys(fn.body).forEach(visit);
3500
+ Object.keys(body).forEach(visit);
3501
+ getReferences(fn.outputs || {}).forEach(function (ref) { return resolve(ref, "output"); });
3502
+ return ordered;
3503
+ }
3504
+ function topSort(fn) {
3505
+ var ordered = getExecutionOrder(fn);
3181
3506
  var orderedBody = Object.fromEntries(ordered.map(function (id) { return [id, fn.body[id]]; }));
3182
3507
  return __assign(__assign({}, fn), { body: orderedBody });
3183
3508
  }
@@ -3189,41 +3514,161 @@ function getDefault(varDef, inputs) {
3189
3514
  }
3190
3515
  return normDef.default;
3191
3516
  }
3517
+ var removedReference = Symbol("removedReference");
3518
+ // Copy only containers that change, retaining unrelated values and references.
3519
+ // A removed standalone reference is omitted; in a template it becomes empty text.
3520
+ function removeReferences(value, matches) {
3521
+ if (isReference(value)) {
3522
+ return matches(value) ? removedReference : value;
3523
+ }
3524
+ if (typeof value === "string") {
3525
+ return value.replace(/<.*?>/g, function (ref) {
3526
+ return isReference(ref) && matches(ref) ? "" : ref;
3527
+ });
3528
+ }
3529
+ if (Array.isArray(value)) {
3530
+ var changed_1 = false;
3531
+ var items_1 = [];
3532
+ value.forEach(function (item) {
3533
+ var next = removeReferences(item, matches);
3534
+ if (next !== item)
3535
+ changed_1 = true;
3536
+ if (next !== removedReference)
3537
+ items_1.push(next);
3538
+ });
3539
+ return changed_1 ? items_1 : value;
3540
+ }
3541
+ if (value && typeof value === "object") {
3542
+ var prototype = Object.getPrototypeOf(value);
3543
+ if (prototype !== Object.prototype && prototype !== null)
3544
+ return value;
3545
+ var changed_2 = false;
3546
+ var entries_1 = [];
3547
+ Object.entries(value).forEach(function (_a) {
3548
+ var _b = __read(_a, 2), key = _b[0], item = _b[1];
3549
+ var next = removeReferences(item, matches);
3550
+ if (next !== item)
3551
+ changed_2 = true;
3552
+ if (next !== removedReference)
3553
+ entries_1.push([key, next]);
3554
+ });
3555
+ return changed_2 ? Object.fromEntries(entries_1) : value;
3556
+ }
3557
+ return value;
3558
+ }
3192
3559
  var Engine = /** @class */ (function () {
3193
3560
  function Engine(morePrimitives, env) {
3194
3561
  if (morePrimitives === void 0) { morePrimitives = {}; }
3195
3562
  if (env === void 0) { env = {}; }
3563
+ this.graphDefinitions = new WeakMap();
3564
+ this.automaticMounts = new Map();
3196
3565
  this.listeners = {};
3197
- this.cache = {};
3198
- this.cacheMiss = Symbol("cacheMiss");
3566
+ this.cache = new ExecutionCache();
3567
+ this.graphRunners = new WeakMap();
3568
+ this.executions = new WeakMap();
3569
+ this.latestRuns = new WeakMap();
3199
3570
  this.context = null;
3200
3571
  this.fnIndex = __assign(__assign({}, primitives), morePrimitives);
3201
3572
  this.env = env;
3202
3573
  }
3203
- Engine.prototype.checkCache = function (fnName, inputs) {
3204
- if (!this.cache[fnName]) {
3205
- return this.cacheMiss;
3206
- }
3207
- // TODO: need to check the interpolated inputs, as the references can be the same
3208
- // however, the interpolation does create new objects
3209
- return {};
3574
+ /** Clear successful results; running calls finish but cannot refill invalidated entries. */
3575
+ Engine.prototype.clearCache = function (options) {
3576
+ if (options === void 0) { options = {}; }
3577
+ this.cache.clear(options);
3578
+ };
3579
+ Engine.prototype.executeCall = function (fn_1, call_1, inputs_1, params_1, context_1, name_1, executionId_1, run_1, path_1) {
3580
+ return __awaiter(this, arguments, void 0, function (fn, call, inputs, params, context, name, executionId, run, path, root) {
3581
+ var graphRunner, kind, hit, startTime, start, result, createdAt, impl, _a, endTime, event;
3582
+ if (root === void 0) { root = false; }
3583
+ return __generator(this, function (_b) {
3584
+ switch (_b.label) {
3585
+ case 0:
3586
+ this.cache.visit(run, path);
3587
+ graphRunner = this.graphRunners.get(fn);
3588
+ kind = graphRunner
3589
+ ? undefined
3590
+ : fn.pure
3591
+ ? "pure"
3592
+ : run.options.mode === "preview" && fn.previewCache
3593
+ ? "preview"
3594
+ : undefined;
3595
+ hit = kind
3596
+ ? this.cache.get(run, path, kind, fn, call, inputs, params)
3597
+ : undefined;
3598
+ startTime = performance.now();
3599
+ start = {
3600
+ name: name,
3601
+ fn: call.name,
3602
+ inputs: inputs,
3603
+ params: params,
3604
+ executionId: executionId,
3605
+ context: context,
3606
+ startTime: startTime,
3607
+ };
3608
+ if (!hit) return [3 /*break*/, 1];
3609
+ result = hit.result;
3610
+ createdAt = hit.info.createdAt;
3611
+ return [3 /*break*/, 8];
3612
+ case 1:
3613
+ context.activations[name] = true;
3614
+ impl = fn.impl;
3615
+ console.log("Executing function call: ".concat(name));
3616
+ _b.label = 2;
3617
+ case 2:
3618
+ _b.trys.push([2, , 7, 8]);
3619
+ this.fireEvent("functionCall", start);
3620
+ if (!graphRunner) return [3 /*break*/, 4];
3621
+ return [4 /*yield*/, graphRunner(inputs, params, run, path)];
3622
+ case 3:
3623
+ _a = (_b.sent()).result;
3624
+ return [3 /*break*/, 6];
3625
+ case 4: return [4 /*yield*/, impl.call(fn, inputs, params, this, root ? null : context, name)];
3626
+ case 5:
3627
+ _a = _b.sent();
3628
+ _b.label = 6;
3629
+ case 6:
3630
+ result = _a;
3631
+ createdAt = Date.now();
3632
+ if (kind)
3633
+ this.cache.set(run, path, kind, fn, call, inputs, params, result, impl, createdAt);
3634
+ return [3 /*break*/, 8];
3635
+ case 7:
3636
+ context.activations[name] = false;
3637
+ context.timings[name] = performance.now() - startTime;
3638
+ return [7 /*endfinally*/];
3639
+ case 8:
3640
+ endTime = performance.now();
3641
+ context.activations[name] = false;
3642
+ context.timings[name] = endTime - startTime;
3643
+ context.scope[name] = result;
3644
+ event = __assign(__assign({}, start), { result: result, endTime: endTime, duration: endTime - startTime, cached: !!hit, cache: kind ? { kind: kind, createdAt: createdAt } : undefined });
3645
+ // Hits still notify subscribers and populate a fresh context for the editor.
3646
+ this.fireEvent("functionResult", event);
3647
+ return [2 /*return*/, event];
3648
+ }
3649
+ });
3650
+ });
3210
3651
  };
3211
- Engine.prototype.runGraph = function (node, inputs, params) {
3652
+ Engine.prototype.runGraph = function (node, suppliedInputs, params, executionOrder, run, path) {
3212
3653
  return __awaiter(this, void 0, void 0, function () {
3213
- var _a, body, executionId, context, startTime, _b, _c, _d, _i, name_1, fnCall, fn, inputs_1, params_1, result_1, cacheResult, startTime_1, endTime_1, result, endTime, response;
3214
- return __generator(this, function (_e) {
3215
- switch (_e.label) {
3654
+ var _a, body, executionId, defaults, mappedDefaults, inputs, context, startTime, executionOrder_1, executionOrder_1_1, name_1, fnCall, fn, callPath, inputs_1, params_1, e_1_1, result, endTime, response;
3655
+ var e_1, _b;
3656
+ return __generator(this, function (_c) {
3657
+ switch (_c.label) {
3216
3658
  case 0:
3217
3659
  _a = node.body, body = _a === void 0 ? {} : _a;
3218
3660
  executionId = uid$1();
3219
- // add input defaults
3661
+ defaults = {};
3220
3662
  Object.entries(node.inputs || {}).forEach(function (_a) {
3221
3663
  var _b = __read(_a, 2), name = _b[0], def = _b[1];
3222
3664
  var normDef = normalizeVarDef(def);
3223
- if (inputs[name] === undefined && normDef.default !== undefined) {
3224
- inputs[name] = getDefault(normDef, inputs);
3665
+ if (suppliedInputs[name] === undefined &&
3666
+ normDef.default !== undefined) {
3667
+ defaults[name] = getDefault(normDef, suppliedInputs);
3225
3668
  }
3226
3669
  });
3670
+ mappedDefaults = this.cache.interpolate(run, __spreadArray(__spreadArray([], __read(path), false), ["$defaults"], false), defaults, {});
3671
+ inputs = __assign(__assign({}, suppliedInputs), mappedDefaults);
3227
3672
  context = {
3228
3673
  scope: { inputs: inputs },
3229
3674
  calls: {},
@@ -3232,76 +3677,46 @@ var Engine = /** @class */ (function () {
3232
3677
  timings: {},
3233
3678
  activations: {},
3234
3679
  };
3680
+ this.executions.set(context, { run: run, path: path });
3235
3681
  startTime = performance.now();
3236
- _b = body;
3237
- _c = [];
3238
- for (_d in _b)
3239
- _c.push(_d);
3240
- _i = 0;
3241
- _e.label = 1;
3682
+ _c.label = 1;
3242
3683
  case 1:
3243
- if (!(_i < _c.length)) return [3 /*break*/, 5];
3244
- _d = _c[_i];
3245
- if (!(_d in _b)) return [3 /*break*/, 4];
3246
- name_1 = _d;
3684
+ _c.trys.push([1, 6, 7, 8]);
3685
+ executionOrder_1 = __values(executionOrder), executionOrder_1_1 = executionOrder_1.next();
3686
+ _c.label = 2;
3687
+ case 2:
3688
+ if (!!executionOrder_1_1.done) return [3 /*break*/, 5];
3689
+ name_1 = executionOrder_1_1.value;
3247
3690
  fnCall = body[name_1];
3248
3691
  fn = this.fnIndex[fnCall.name];
3249
- if (!fn) {
3692
+ if (!fn)
3250
3693
  throw new Error("function ".concat(fnCall.name, " not found in index"));
3251
- }
3252
- inputs_1 = interpolate(fnCall.inputs || {}, context.scope);
3694
+ callPath = __spreadArray(__spreadArray([], __read(path), false), [name_1], false);
3695
+ inputs_1 = this.cache.interpolate(run, __spreadArray(__spreadArray([], __read(callPath), false), ["$inputs"], false), fnCall.inputs || {}, context.scope, true);
3253
3696
  params_1 = fnCall.params || {};
3254
- result_1 = {};
3255
3697
  context.calls[name_1] = fnCall;
3256
3698
  context.inputs[name_1] = inputs_1;
3257
3699
  context.params[name_1] = params_1;
3258
- cacheResult = this.checkCache(name_1, inputs_1);
3259
- if (!(cacheResult !== this.cacheMiss)) return [3 /*break*/, 2];
3260
- // use as result
3261
- result_1 = cacheResult;
3262
- // store the result in the scope
3263
- context.scope[name_1] = result_1;
3264
- return [3 /*break*/, 4];
3265
- case 2:
3266
- // cache miss, call the function implementation
3267
- context.activations[name_1] = true;
3268
- startTime_1 = performance.now();
3269
- this.fireEvent("functionCall", {
3270
- name: name_1,
3271
- fn: fnCall.name,
3272
- inputs: inputs_1,
3273
- params: params_1,
3274
- executionId: executionId,
3275
- context: context,
3276
- startTime: startTime_1,
3277
- });
3278
- return [4 /*yield*/, fn.impl(inputs_1, params_1, this, context, name_1)];
3700
+ return [4 /*yield*/, this.executeCall(fn, fnCall, inputs_1, params_1, context, name_1, executionId, run, callPath)];
3279
3701
  case 3:
3280
- // call the implentation of sub functions
3281
- result_1 = _e.sent();
3282
- endTime_1 = performance.now();
3283
- context.activations[name_1] = false;
3284
- context.timings[name_1] = endTime_1 - startTime_1;
3285
- // store the result in the scope
3286
- context.scope[name_1] = result_1;
3287
- this.fireEvent("functionResult", {
3288
- name: name_1,
3289
- fn: fnCall.name,
3290
- inputs: inputs_1,
3291
- params: params_1,
3292
- executionId: executionId,
3293
- context: context,
3294
- result: result_1,
3295
- startTime: startTime_1,
3296
- endTime: endTime_1,
3297
- duration: endTime_1 - startTime_1,
3298
- });
3299
- _e.label = 4;
3702
+ _c.sent();
3703
+ _c.label = 4;
3300
3704
  case 4:
3301
- _i++;
3302
- return [3 /*break*/, 1];
3303
- case 5:
3304
- result = interpolate(node.outputs || {}, context.scope);
3705
+ executionOrder_1_1 = executionOrder_1.next();
3706
+ return [3 /*break*/, 2];
3707
+ case 5: return [3 /*break*/, 8];
3708
+ case 6:
3709
+ e_1_1 = _c.sent();
3710
+ e_1 = { error: e_1_1 };
3711
+ return [3 /*break*/, 8];
3712
+ case 7:
3713
+ try {
3714
+ if (executionOrder_1_1 && !executionOrder_1_1.done && (_b = executionOrder_1.return)) _b.call(executionOrder_1);
3715
+ }
3716
+ finally { if (e_1) throw e_1.error; }
3717
+ return [7 /*endfinally*/];
3718
+ case 8:
3719
+ result = this.cache.interpolate(run, __spreadArray(__spreadArray([], __read(path), false), ["$outputs"], false), node.outputs || {}, context.scope, true);
3305
3720
  endTime = performance.now();
3306
3721
  response = {
3307
3722
  name: node.name,
@@ -3337,6 +3752,16 @@ var Engine = /** @class */ (function () {
3337
3752
  return node;
3338
3753
  }
3339
3754
  var _a = node.body, body = _a === void 0 ? {} : _a, props = __rest(node, ["body"]);
3755
+ // Validate before registering or mounting, preserving the last working
3756
+ // definition if an edit introduces a broken reference or cycle.
3757
+ var executionOrder = getExecutionOrder(node);
3758
+ executionOrder.forEach(function (id) {
3759
+ var call = body[id];
3760
+ if (!Object.prototype.hasOwnProperty.call(_this.fnIndex, call.name) ||
3761
+ !_this.fnIndex[call.name]) {
3762
+ throw new Error("function ".concat(call.name, " not found in index (node \"").concat(id, "\")"));
3763
+ }
3764
+ });
3340
3765
  // create a primitive function, one that has an implementation and optional mounting function
3341
3766
  var primitive = __assign(__assign({}, props), { outputs: Object.fromEntries(Object.entries(node.outputs || {}).map(function (_a) {
3342
3767
  var _b = __read(_a, 2), key = _b[0]; _b[1];
@@ -3344,8 +3769,9 @@ var Engine = /** @class */ (function () {
3344
3769
  })), mount: function (trigger) {
3345
3770
  // mount all the nodes
3346
3771
  var unsubs = [];
3347
- Object.values(body).forEach(function (n) {
3772
+ executionOrder.forEach(function (id) {
3348
3773
  var _a;
3774
+ var n = body[id];
3349
3775
  var fn = _this.fnIndex[n.name];
3350
3776
  if (!fn) {
3351
3777
  console.warn("function ".concat(n.name, " not found in index"));
@@ -3357,7 +3783,7 @@ var Engine = /** @class */ (function () {
3357
3783
  }
3358
3784
  });
3359
3785
  return function () {
3360
- var e_1, _a;
3786
+ var e_2, _a;
3361
3787
  try {
3362
3788
  // console.log(`Unmounting function: ${node.name}`);
3363
3789
  // unmount all the nodes
@@ -3366,35 +3792,48 @@ var Engine = /** @class */ (function () {
3366
3792
  unsub();
3367
3793
  }
3368
3794
  }
3369
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
3795
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
3370
3796
  finally {
3371
3797
  try {
3372
3798
  if (unsubs_1_1 && !unsubs_1_1.done && (_a = unsubs_1.return)) _a.call(unsubs_1);
3373
3799
  }
3374
- finally { if (e_1) throw e_1.error; }
3800
+ finally { if (e_2) throw e_2.error; }
3375
3801
  }
3376
3802
  };
3377
- }, impl: function (inputs, params, engine) { return __awaiter(_this, void 0, void 0, function () {
3378
- var response;
3803
+ }, impl: function (inputs, params, engine, context, name) { return __awaiter(_this, void 0, void 0, function () {
3804
+ var parent, run, path, response;
3379
3805
  return __generator(this, function (_a) {
3380
3806
  switch (_a.label) {
3381
- case 0: return [4 /*yield*/, engine.runGraph(node, inputs, params)];
3807
+ case 0:
3808
+ parent = context && engine.executions.get(context);
3809
+ run = (parent === null || parent === void 0 ? void 0 : parent.run) || engine.cache.begin(node.name, {});
3810
+ path = parent ? __spreadArray(__spreadArray([], __read(parent.path), false), [name], false) : ["_root"];
3811
+ return [4 /*yield*/, engine.runGraph(node, inputs, params, executionOrder, run, path)];
3382
3812
  case 1:
3383
3813
  response = _a.sent();
3814
+ if (!parent)
3815
+ engine.cache.finish(run);
3384
3816
  return [2 /*return*/, response.result];
3385
3817
  }
3386
3818
  });
3387
3819
  }); } });
3820
+ this.graphRunners.set(primitive, function (inputs, params, run, path) {
3821
+ return _this.runGraph(node, inputs, params, executionOrder, run, path);
3822
+ });
3388
3823
  // register the primitive
3389
3824
  this.fnIndex[primitive.name] = primitive;
3825
+ this.graphDefinitions.set(primitive, node);
3390
3826
  return primitive;
3391
3827
  };
3392
- Engine.prototype.mount = function (node) {
3828
+ Engine.prototype.mount = function (node, inputs, options) {
3393
3829
  var _this = this;
3394
3830
  var _a;
3831
+ if (inputs === void 0) { inputs = {}; }
3832
+ if (options === void 0) { options = {}; }
3395
3833
  return (_a = node.mount) === null || _a === void 0 ? void 0 : _a.call(node, function () {
3396
- // console.log(`Triggering node: ${node.name}`);
3397
- _this.run(node);
3834
+ var _a, _b;
3835
+ var latest = _this.latestRuns.get(node);
3836
+ void _this.run(node, (_a = latest === null || latest === void 0 ? void 0 : latest.inputs) !== null && _a !== void 0 ? _a : inputs, (_b = latest === null || latest === void 0 ? void 0 : latest.options) !== null && _b !== void 0 ? _b : options);
3398
3837
  });
3399
3838
  };
3400
3839
  Engine.prototype.getContext = function () {
@@ -3413,138 +3852,188 @@ var Engine = /** @class */ (function () {
3413
3852
  };
3414
3853
  };
3415
3854
  Engine.prototype.fireEvent = function (eventName, event) {
3416
- var e_2, _a;
3855
+ var e_3, _a;
3417
3856
  try {
3418
3857
  for (var _b = __values(this.listeners[eventName] || []), _c = _b.next(); !_c.done; _c = _b.next()) {
3419
3858
  var listener = _c.value;
3420
3859
  listener(event);
3421
3860
  }
3422
3861
  }
3423
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
3862
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
3424
3863
  finally {
3425
3864
  try {
3426
3865
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
3427
3866
  }
3428
- finally { if (e_2) throw e_2.error; }
3867
+ finally { if (e_3) throw e_3.error; }
3429
3868
  }
3430
3869
  };
3431
3870
  Engine.prototype.run = function (node_1) {
3432
- return __awaiter(this, arguments, void 0, function (node, inputs) {
3433
- var builtFn, mappedInputs, result;
3871
+ return __awaiter(this, arguments, void 0, function (node, inputs, options) {
3872
+ if (inputs === void 0) { inputs = {}; }
3873
+ if (options === void 0) { options = {}; }
3874
+ return __generator(this, function (_a) {
3875
+ switch (_a.label) {
3876
+ case 0: return [4 /*yield*/, this.runDetailed(node, inputs, options)];
3877
+ case 1: return [2 /*return*/, (_a.sent()).result];
3878
+ }
3879
+ });
3880
+ });
3881
+ };
3882
+ Engine.prototype.runDetailed = function (node_1) {
3883
+ return __awaiter(this, arguments, void 0, function (node, inputs, options) {
3884
+ var builtFn, rebuilt, mounted, unmount, run, mappedInputs, runner, response, context;
3434
3885
  var _a;
3435
3886
  if (inputs === void 0) { inputs = {}; }
3887
+ if (options === void 0) { options = {}; }
3436
3888
  return __generator(this, function (_b) {
3437
3889
  switch (_b.label) {
3438
3890
  case 0:
3439
3891
  if (!isPrimitive(node)) {
3440
3892
  builtFn = this.getFunction(node.name);
3441
- if (!builtFn) {
3442
- // not yet build
3893
+ rebuilt = false;
3894
+ // Graphs are edited immutably: the name identifies a registry slot,
3895
+ // while object identity tells us whether its definition is current.
3896
+ if (!builtFn || this.graphDefinitions.get(builtFn) !== node) {
3443
3897
  builtFn = this.build(node);
3444
- // mount if needed
3445
- this.mount(builtFn);
3898
+ rebuilt = true;
3446
3899
  }
3447
- return [2 /*return*/, this.run(builtFn, inputs)];
3900
+ mounted = this.automaticMounts.get(node.name);
3901
+ if (rebuilt || (mounted && mounted.fn !== builtFn)) {
3902
+ // Only replace subscriptions owned by run(); explicit mount()
3903
+ // callers retain ownership of their cleanup callbacks.
3904
+ (_a = mounted === null || mounted === void 0 ? void 0 : mounted.unmount) === null || _a === void 0 ? void 0 : _a.call(mounted);
3905
+ this.automaticMounts.delete(node.name);
3906
+ unmount = this.mount(builtFn);
3907
+ this.automaticMounts.set(node.name, {
3908
+ fn: builtFn,
3909
+ unmount: unmount || undefined,
3910
+ });
3911
+ }
3912
+ node = builtFn;
3448
3913
  }
3449
- mappedInputs = interpolate(inputs, {});
3450
- return [4 /*yield*/, ((_a = node.impl) === null || _a === void 0 ? void 0 : _a.call(node, mappedInputs, {}, this, null, "_root"))];
3914
+ this.latestRuns.set(node, { inputs: inputs, options: __assign({}, options) });
3915
+ run = this.cache.begin(node.name, options);
3916
+ mappedInputs = this.cache.interpolate(run, ["$inputs"], inputs, {});
3917
+ runner = this.graphRunners.get(node);
3918
+ if (!runner) return [3 /*break*/, 2];
3919
+ return [4 /*yield*/, runner(mappedInputs, {}, run, ["_root"])];
3451
3920
  case 1:
3452
- result = _b.sent();
3453
- // console.log("Engine run complete", result);
3454
- return [2 /*return*/, result];
3921
+ response = _b.sent();
3922
+ return [3 /*break*/, 4];
3923
+ case 2:
3924
+ context = {
3925
+ scope: { inputs: mappedInputs },
3926
+ calls: { _root: { name: node.name } },
3927
+ inputs: { _root: mappedInputs },
3928
+ params: { _root: {} },
3929
+ timings: {},
3930
+ activations: {},
3931
+ };
3932
+ return [4 /*yield*/, this.executeCall(node, context.calls._root, mappedInputs, {}, context, "_root", uid$1(), run, ["_root"], true)];
3933
+ case 3:
3934
+ response = _b.sent();
3935
+ response = __assign(__assign({}, response), { name: node.name, fn: "root" });
3936
+ this.fireEvent("result", response);
3937
+ this.context = context;
3938
+ _b.label = 4;
3939
+ case 4:
3940
+ this.cache.finish(run);
3941
+ return [2 /*return*/, response];
3455
3942
  }
3456
3943
  });
3457
3944
  });
3458
3945
  };
3459
- Engine.prototype.runDetailed = function (node_1) {
3460
- return __awaiter(this, arguments, void 0, function (node, inputs) {
3461
- var _this = this;
3462
- if (inputs === void 0) { inputs = {}; }
3463
- return __generator(this, function (_a) {
3464
- return [2 /*return*/, new Promise(function (resolve, reject) {
3465
- var unsub = _this.subscribe("result", function (event) {
3466
- if (event.name === node.name) {
3467
- resolve(event);
3468
- unsub();
3469
- }
3470
- });
3471
- _this.run(node, inputs).catch(function (e) {
3472
- unsub();
3473
- reject(e);
3474
- });
3475
- })];
3476
- });
3477
- });
3478
- };
3479
3946
  Engine.prototype.getFunction = function (name) {
3480
3947
  return this.fnIndex[name];
3481
3948
  };
3482
3949
  // node handlers
3483
3950
  Engine.prototype.applyNodeDelete = function (oldFn, nodeIds) {
3484
- var newFn = __assign(__assign({}, oldFn), { body: __assign({}, oldFn.body) });
3485
- if (nodeIds.every(function (id) { return id === "output"; })) {
3486
- // cannot delete output node
3951
+ var deletedIds = new Set(nodeIds.filter(function (id) { return id !== "output"; }));
3952
+ if (!deletedIds.size) {
3953
+ // The generated output node is protected, including in bulk deletes.
3487
3954
  return oldFn;
3488
3955
  }
3489
- if (nodeIds.includes("inputs")) {
3956
+ var newFn = __assign(__assign({}, oldFn), { body: __assign({}, oldFn.body) });
3957
+ if (deletedIds.has("inputs")) {
3490
3958
  delete newFn.inputs;
3491
3959
  }
3492
- nodeIds.forEach(function (id) {
3960
+ deletedIds.forEach(function (id) {
3493
3961
  delete newFn.body[id];
3494
3962
  });
3495
- // remove any references to the deleted nodes
3963
+ var matches = function (ref) {
3964
+ return deletedIds.has(parseReference(ref).split(".")[0]);
3965
+ };
3966
+ // Clone affected nodes as well as their inputs so shallow comparisons work.
3496
3967
  Object.entries(newFn.body).forEach(function (_a) {
3497
3968
  var _b = __read(_a, 2), id = _b[0], node = _b[1];
3498
- Object.entries(node.inputs || {}).forEach(function (_a) {
3499
- var _b;
3500
- var _c = __read(_a, 2), name = _c[0], value = _c[1];
3501
- if (isReference(value)) {
3502
- var path = parseReference(value);
3503
- var parts = path.split(".");
3504
- if (nodeIds.includes(parts[0])) {
3505
- (_b = newFn.body[id].inputs) === null || _b === void 0 ? true : delete _b[name];
3506
- }
3969
+ if (!node.inputs)
3970
+ return;
3971
+ var inputs = removeReferences(node.inputs, matches);
3972
+ if (inputs !== node.inputs) {
3973
+ newFn.body[id] = __assign(__assign({}, node), { inputs: inputs });
3974
+ }
3975
+ });
3976
+ Object.entries(oldFn.outputs || {}).forEach(function (_a) {
3977
+ var _b = __read(_a, 2), name = _b[0], value = _b[1];
3978
+ var next = removeReferences(value, matches);
3979
+ if (next !== value) {
3980
+ if (newFn.outputs === oldFn.outputs) {
3981
+ newFn.outputs = __assign({}, oldFn.outputs);
3507
3982
  }
3508
- });
3983
+ // Keep the public port, matching explicit output-edge deletion.
3984
+ newFn.outputs[name] = next === removedReference ? "" : next;
3985
+ }
3509
3986
  });
3510
3987
  return newFn;
3511
3988
  };
3512
3989
  Engine.prototype.applyEdgeDelete = function (oldFn, fromNode, toNode, fromField, toField) {
3513
3990
  var _a, _b;
3514
- var newFn = __assign(__assign({}, oldFn), { body: __assign({}, oldFn.body), outputs: __assign({}, oldFn.outputs) });
3991
+ var _c, _d, _e;
3992
+ var matches = function (ref) { return ref === "<".concat(fromNode, ".").concat(fromField, ">"); };
3515
3993
  if (toNode === "output") {
3516
- // deleting of output edges
3517
- if ((_a = newFn.outputs) === null || _a === void 0 ? void 0 : _a[toField]) {
3518
- // set to empty to prevent port from being removed, as that would cause the output node to be deleted
3519
- newFn.outputs[toField] = "";
3520
- }
3521
- return newFn;
3522
- }
3523
- var to = newFn.body[toNode];
3994
+ var value_1 = (_c = oldFn.outputs) === null || _c === void 0 ? void 0 : _c[toField];
3995
+ if (value_1 === undefined)
3996
+ return oldFn;
3997
+ var next_1 = removeReferences(value_1, matches);
3998
+ if (next_1 === value_1)
3999
+ return oldFn;
4000
+ return __assign(__assign({}, oldFn), { outputs: __assign(__assign({}, oldFn.outputs), (_a = {}, _a[toField] = next_1 === removedReference ? "" : next_1, _a)) });
4001
+ }
4002
+ var to = oldFn.body[toNode];
3524
4003
  var fn = this.fnIndex[to === null || to === void 0 ? void 0 : to.name];
3525
4004
  if (!to) {
3526
4005
  console.warn("Invalid nodes for edge deletion");
3527
- return newFn;
4006
+ return oldFn;
3528
4007
  }
3529
4008
  if (!fn) {
3530
4009
  console.warn("Function ".concat(to === null || to === void 0 ? void 0 : to.name, " not found in index, which is unexpected as you are deleting an edge to it."));
3531
- return newFn;
4010
+ return oldFn;
4011
+ }
4012
+ var value = (_d = to.inputs) === null || _d === void 0 ? void 0 : _d[toField];
4013
+ if (value === undefined) {
4014
+ console.warn("No matching edge found to delete");
4015
+ return oldFn;
4016
+ }
4017
+ var next = removeReferences(value, matches);
4018
+ if (next === value) {
4019
+ console.warn("No matching edge found to delete");
4020
+ return oldFn;
3532
4021
  }
3533
- if (to.inputs && to.inputs[toField] === "<".concat(fromNode, ".").concat(fromField, ">")) {
3534
- // delete the incoming edge
3535
- delete to.inputs[toField];
4022
+ var inputs = __assign({}, to.inputs);
4023
+ if (next === removedReference) {
4024
+ delete inputs[toField];
3536
4025
  // revert to default value if exists
3537
- if ((_b = fn.inputs) === null || _b === void 0 ? void 0 : _b[toField]) {
4026
+ if ((_e = fn.inputs) === null || _e === void 0 ? void 0 : _e[toField]) {
3538
4027
  var normDef = normalizeVarDef(fn.inputs[toField]);
3539
4028
  if (normDef.default !== undefined) {
3540
- to.inputs[toField] = getDefault(normDef, to.inputs);
4029
+ inputs[toField] = getDefault(normDef, inputs);
3541
4030
  }
3542
4031
  }
3543
4032
  }
3544
4033
  else {
3545
- console.warn("No matching edge found to delete");
4034
+ inputs[toField] = next;
3546
4035
  }
3547
- return newFn;
4036
+ return __assign(__assign({}, oldFn), { body: __assign(__assign({}, oldFn.body), (_b = {}, _b[toNode] = __assign(__assign({}, to), { inputs: inputs }), _b)) });
3548
4037
  };
3549
4038
  Engine.prototype.applyNodeAdd = function (oldFn, nodeName, callback) {
3550
4039
  var newFn = __assign(__assign({}, oldFn), { body: __assign({}, oldFn.body) });
@@ -3591,25 +4080,20 @@ var Engine = /** @class */ (function () {
3591
4080
  return topSort(newFn);
3592
4081
  };
3593
4082
  Engine.prototype.applyNodeConnect = function (oldFn, fromNode, toNode, fromField, toField) {
3594
- var _a;
4083
+ var _a, _b;
3595
4084
  var newFn = __assign(__assign({}, oldFn), { body: __assign({}, oldFn.body) });
3596
4085
  if (toNode === "output") {
3597
4086
  newFn.outputs = __assign(__assign({}, oldFn.outputs), (_a = {}, _a[toField] = "<".concat(fromNode, ".").concat(fromField, ">"), _a));
3598
4087
  // no need to sort, as only output has changed
3599
4088
  return newFn;
3600
4089
  }
3601
- // const from = newFn.body[fromNode];
3602
- var to = __assign({}, newFn.body[toNode]);
4090
+ var to = oldFn.body[toNode];
3603
4091
  if (!to) {
3604
4092
  console.warn("Invalid nodes for connection");
3605
- return newFn;
3606
- }
3607
- if (!to.inputs) {
3608
- to.inputs = {};
4093
+ return oldFn;
3609
4094
  }
3610
- // add the reference
3611
- to.inputs[toField] = "<".concat(fromNode, ".").concat(fromField, ">");
3612
- newFn.body[toNode] = to;
4095
+ // Copy the inputs before assigning: copying only the node still aliases them.
4096
+ newFn.body[toNode] = __assign(__assign({}, to), { inputs: __assign(__assign({}, to.inputs), (_b = {}, _b[toField] = "<".concat(fromNode, ".").concat(fromField, ">"), _b)) });
3613
4097
  return topSort(newFn);
3614
4098
  };
3615
4099
  return Engine;
@@ -6743,9 +7227,10 @@ function GenericNode(_a) {
6743
7227
  var paramEntries = Object.entries(params || data.paramDefs || {});
6744
7228
  var engine = useEngine();
6745
7229
  var _e = __read(React.useState({}), 2), result = _e[0], setResult = _e[1];
6746
- var _f = __read(React.useState(null), 2), duration = _f[0], setDuration = _f[1];
6747
- var _g = __read(useInner(((_c = outputEntries[0]) === null || _c === void 0 ? void 0 : _c[0]) || null, [outputs, data.outputDefs]), 2), focus = _g[0], setFocus = _g[1];
6748
- var _h = __read(React.useState(false), 2), showPreview = _h[0], setShowPreview = _h[1];
7230
+ var _f = __read(React.useState(), 2), cache = _f[0], setCache = _f[1];
7231
+ var _g = __read(React.useState(null), 2), duration = _g[0], setDuration = _g[1];
7232
+ var _h = __read(useInner(((_c = outputEntries[0]) === null || _c === void 0 ? void 0 : _c[0]) || null, [outputs, data.outputDefs]), 2), focus = _h[0], setFocus = _h[1];
7233
+ var _j = __read(React.useState(false), 2), showPreview = _j[0], setShowPreview = _j[1];
6749
7234
  var previewValue = focus ? result[focus] : Object.values(result)[0];
6750
7235
  React.useEffect(function () {
6751
7236
  return engine.subscribe("functionResult", function (event) {
@@ -6753,9 +7238,10 @@ function GenericNode(_a) {
6753
7238
  if (event.name === id) {
6754
7239
  setResult(event.result || {});
6755
7240
  setDuration((_a = event.duration) !== null && _a !== void 0 ? _a : null);
7241
+ setCache(event.cached ? event.cache : undefined);
6756
7242
  }
6757
7243
  });
6758
- }, []);
7244
+ }, [engine, id]);
6759
7245
  return (React.createElement(NodeContainer, { label: data.label, selected: selected, id: id, className: (_d = data.nodeProps) === null || _d === void 0 ? void 0 : _d.className },
6760
7246
  React.createElement("div", { className: styles$1.Description }, data.description),
6761
7247
  children,
@@ -6777,9 +7263,10 @@ function GenericNode(_a) {
6777
7263
  _b)), onClick: function () { return setShowPreview(!showPreview); } }, "Preview")),
6778
7264
  showPreview && (React.createElement("div", { className: styles$1.Preview },
6779
7265
  React.createElement(TreeView, { value: previewValue }),
6780
- duration !== null && (React.createElement("div", { className: styles$1.Duration },
6781
- duration.toFixed(2),
6782
- " ms"))))));
7266
+ duration !== null && (React.createElement("div", { className: styles$1.Duration }, cache ? (React.createElement("span", { title: "Result computed ".concat(new Date(cache.createdAt).toLocaleString()) },
7267
+ cache.kind === "preview" ? "Cached preview" : "Cached",
7268
+ " · ",
7269
+ new Date(cache.createdAt).toLocaleTimeString())) : "".concat(duration.toFixed(2), " ms")))))));
6783
7270
  }
6784
7271
 
6785
7272
  var math = create(all, {});
@@ -7845,44 +8332,58 @@ function useDraggableNode(name, itemProps) {
7845
8332
  return function (ref) { return drag(ref); };
7846
8333
  }
7847
8334
 
7848
- /**
7849
- * using functions
7850
- *
7851
- * in React context
7852
- * - use useFunction. This will build the function, mount it if needed and provides a run function.
7853
- *
7854
- *
7855
- * in Vanilla context
7856
- * - use engine.build(fn) to build the function
7857
- * - use engine.mount(fn) to mount the function (if it has a mount function)
7858
- * - use await engine.run(fn, inputs) to run the function and get the result
8335
+ /** Build a graph, optionally subscribe to triggers, and retain the latest run inputs.
8336
+ * Preview caching requires { mode: "preview" }; refresh clears external preview
8337
+ * results for this graph/session and reruns with the latest inputs.
7859
8338
  */
7860
- function useFunction(engine, fn, mount) {
8339
+ function useFunction(engine, fn, mount, options) {
7861
8340
  var _this = this;
7862
- var _a = __read(React.useState(null), 2), result = _a[0], setResult = _a[1];
7863
- var builtFn = React.useMemo(function () { return engine.build(fn); }, [fn]);
8341
+ if (options === void 0) { options = {}; }
8342
+ var _a = options.mode, mode = _a === void 0 ? "normal" : _a, _b = options.cacheKey, cacheKey = _b === void 0 ? "" : _b;
8343
+ var _c = __read(React.useState(null), 2), result = _c[0], setResult = _c[1];
8344
+ var builtFn = React.useMemo(function () { return engine.build(fn); }, [engine, fn]);
8345
+ var latestInputs = React.useRef({});
8346
+ var sequence = React.useRef(0);
8347
+ var currentDefinition = React.useRef(builtFn);
8348
+ currentDefinition.current = builtFn;
8349
+ var run = React.useCallback(function () {
8350
+ var args_1 = [];
8351
+ for (var _i = 0; _i < arguments.length; _i++) {
8352
+ args_1[_i] = arguments[_i];
8353
+ }
8354
+ return __awaiter(_this, __spreadArray([], __read(args_1), false), void 0, function (inputs) {
8355
+ var request, result;
8356
+ if (inputs === void 0) { inputs = latestInputs.current; }
8357
+ return __generator(this, function (_a) {
8358
+ switch (_a.label) {
8359
+ case 0:
8360
+ latestInputs.current = inputs;
8361
+ request = ++sequence.current;
8362
+ return [4 /*yield*/, engine.run(builtFn, inputs, { mode: mode, cacheKey: cacheKey })];
8363
+ case 1:
8364
+ result = _a.sent();
8365
+ if (request === sequence.current && currentDefinition.current === builtFn) {
8366
+ setResult(result);
8367
+ }
8368
+ return [2 /*return*/, result];
8369
+ }
8370
+ });
8371
+ });
8372
+ }, [engine, builtFn, mode, cacheKey]);
8373
+ var latestRun = React.useRef(run);
8374
+ latestRun.current = run;
7864
8375
  React.useEffect(function () {
8376
+ var _a;
7865
8377
  if (mount) {
7866
- return engine.mount(builtFn);
8378
+ // Keep this hook's inputs/options even when another editor shares the engine.
8379
+ return (_a = builtFn.mount) === null || _a === void 0 ? void 0 : _a.call(builtFn, function () { void latestRun.current(); });
7867
8380
  }
7868
8381
  }, [builtFn, mount]);
7869
- var run = React.useCallback(function (inputs) { return __awaiter(_this, void 0, void 0, function () {
7870
- var result;
7871
- return __generator(this, function (_a) {
7872
- switch (_a.label) {
7873
- case 0: return [4 /*yield*/, engine.run(builtFn, inputs)];
7874
- case 1:
7875
- result = _a.sent();
7876
- setResult(result);
7877
- return [2 /*return*/, result];
7878
- }
7879
- });
7880
- }); }, [builtFn]);
7881
- return React.useMemo(function () { return ({
7882
- fn: builtFn,
7883
- run: run,
7884
- result: result,
7885
- }); }, [builtFn, run, result]);
8382
+ var refresh = React.useCallback(function () {
8383
+ engine.clearCache({ kind: "preview", graph: fn.name, cacheKey: cacheKey });
8384
+ return run();
8385
+ }, [engine, fn.name, cacheKey, run]);
8386
+ return React.useMemo(function () { return ({ fn: builtFn, run: run, refresh: refresh, result: result }); }, [builtFn, run, refresh, result]);
7886
8387
  }
7887
8388
 
7888
8389
  // cheap lodash replacements
@@ -8868,5 +9369,5 @@ function DDContext(_a) {
8868
9369
  return React.createElement(DndProvider, { backend: HTML5Backend }, children);
8869
9370
  }
8870
9371
 
8871
- export { DDContext, DISCARD, Engine, Flow, FlowContext, GenericNode, Input, InputBoolean, InputNumber, InputSelect, InputString, InputText, Lib, Output, Param, binaryOnTree, binaryOnTreeBranch, broadCast, deRefEdgeHandle, ensureBranch, expandTree, forEachBranch, forEachItem, getBranch, getBranches, getItem, getPaths, getPositions, getReferences, getValue$1 as getValue, getVariable, graftTree, hasReference, interpolate, isEmpty, isEmptyTree, isList, isMultiBranch, isReference, isSingleTon, isTree, mapTree, mapTreeBranch, mergeWithOrder, nAryOnTree, nAryOnTreeBranch, normalizePaths, normalizeVarDef, parseReference, primitives, pushItem, safeName, sameShape, simplifyTree$1 as simplifyTree, toArray, topSort, treeSize, treeStats, trimTree, uid$1 as uid, useControls, useDraggableNode, useEngine, useFlow, useFunction, useGraphedFunction, usePositions, usePrompts, useUpdatePositions, useVariable, variableStore };
9372
+ export { DDContext, DISCARD, Engine, Flow, FlowContext, GenericNode, Input, InputBoolean, InputNumber, InputSelect, InputString, InputText, Lib, Output, Param, binaryOnTree, binaryOnTreeBranch, broadCast, deRefEdgeHandle, ensureBranch, expandTree, forEachBranch, forEachItem, getBranch, getBranches, getItem, getPaths, getPositions, getReferencePaths, getReferences, getValue$1 as getValue, getVariable, graftTree, hasReference, interpolate, isEmpty, isEmptyTree, isList, isMultiBranch, isReference, isSingleTon, isTree, mapTree, mapTreeBranch, mergeWithOrder, nAryOnTree, nAryOnTreeBranch, normalizePaths, normalizeVarDef, parseReference, primitives, pushItem, safeName, sameShape, simplifyTree$1 as simplifyTree, toArray, topSort, treeSize, treeStats, trimTree, uid$1 as uid, useControls, useDraggableNode, useEngine, useFlow, useFunction, useGraphedFunction, usePositions, usePrompts, useUpdatePositions, useVariable, variableStore };
8872
9373
  //# sourceMappingURL=index.esm.mjs.map