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