@field/cascade 0.7.0 → 0.8.0
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/CHANGELOG.md +67 -0
- package/dist/assets/{CodeEditor-DkpxJFRs.js → CodeEditor-BEk-kIBd.js} +29 -29
- package/dist/assets/CodePanel-CeSdjXkg.js +2 -0
- package/dist/assets/CustomNodeDialog-BQbEhIwL.js +1 -0
- package/dist/assets/{ExportDialog-BJu2Bpvv.js → ExportDialog-CjTbbTZJ.js} +2 -2
- package/dist/assets/OpenGraphDialog-CsMNY3C6.css +1 -0
- package/dist/assets/OpenGraphDialog-DCwH9itM.js +1 -0
- package/dist/assets/ProjectSettings-Dg7M5xt9.js +2 -0
- package/dist/assets/SettingsDialog-e7GgsjB9.js +1 -0
- package/dist/assets/VersionHistoryDialog-D6VBzSQ0.js +3 -0
- package/dist/assets/{core-DwPLRUzm.js → core-BInLG4gw.js} +1 -1
- package/dist/assets/{cssMode-C1bjAPYj.js → cssMode-DfR2qmYG.js} +1 -1
- package/dist/assets/definitions-DZpOjWKR.js +1 -0
- package/dist/assets/{editor.api-wr5lElMs.js → editor.api-Ck8_vgQV.js} +1 -1
- package/dist/assets/{freemarker2-DQHTaSDb.js → freemarker2-fVn10Wn3.js} +1 -1
- package/dist/assets/geo-D2VPjAdt.js +1 -0
- package/dist/assets/{handlebars-DOiZYGbp.js → handlebars-BCSchLT2.js} +1 -1
- package/dist/assets/{html-B1u4CWMY.js → html-CVpSv_k9.js} +1 -1
- package/dist/assets/{htmlMode-YNKJ8Hq1.js → htmlMode-Da4ESTv0.js} +1 -1
- package/dist/assets/{image-DmeeS7Tl.js → image-BLn--oSF.js} +1 -1
- package/dist/assets/index-CMiBdEuT.js +212 -0
- package/dist/assets/index-DMIuml9a.css +1 -0
- package/dist/assets/index-client-BI2H82FJ.js +1 -0
- package/dist/assets/{javascript-DVujl3OI.js → javascript-BpX0Gvcf.js} +1 -1
- package/dist/assets/{jsonMode-Bp_Ujoae.js → jsonMode-CsLqaou7.js} +1 -1
- package/dist/assets/{legacy-CAj2_7QE.js → legacy-ZA7textG.js} +1 -1
- package/dist/assets/{liquid-C0wcDX3X.js → liquid-efUz51Wf.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-C2XtKL_c.js → lspLanguageFeatures-DCi5-K0n.js} +1 -1
- package/dist/assets/math-B6hpxPJO.js +1 -0
- package/dist/assets/{mdx-Dz4Hb1ZN.js → mdx-DP_sjGVy.js} +1 -1
- package/dist/assets/pop-CYUIjT6x.js +1 -0
- package/dist/assets/{python-Y4eTKlAp.js → python-aAuP3LUi.js} +1 -1
- package/dist/assets/{razor-BmpH_QN_.js → razor-7M6sMmC5.js} +1 -1
- package/dist/assets/{tsMode-mOQwydYU.js → tsMode-Bf4CnoYK.js} +1 -1
- package/dist/assets/{typescript-8ItD7azm.js → typescript-ddhYBGyT.js} +1 -1
- package/dist/assets/{workers-xhda_-L2.js → workers-X5la8DDT.js} +1 -1
- package/dist/assets/{xml-ydyZSquM.js → xml-BzbXa3Ps.js} +1 -1
- package/dist/assets/{yaml-BQjpTmW5.js → yaml-CXs-VzxL.js} +1 -1
- package/dist/cli/index.js +276 -214
- package/dist/index.html +6 -4
- package/package.json +1 -1
- package/dist/assets/CodePanel-O31InnQg.js +0 -2
- package/dist/assets/CustomNodeDialog-Cn3sT-M2.js +0 -1
- package/dist/assets/ProjectSettings-BzEmtsYc.js +0 -2
- package/dist/assets/SettingsDialog-zWct7T8j.js +0 -1
- package/dist/assets/VersionHistoryDialog-DCe6XQcO.js +0 -3
- package/dist/assets/definitions-Dh-oZAA5.js +0 -1
- package/dist/assets/geo-DLRNQg57.js +0 -1
- package/dist/assets/index-C35beBgj.js +0 -212
- package/dist/assets/index-client-BoJgsN9M.js +0 -1
- package/dist/assets/index-ilgbOu6G.css +0 -1
- package/dist/assets/math-D0hLbnR2.js +0 -1
- package/dist/assets/pop-OaOoEUwk.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -212,9 +212,9 @@ function levelCount(geometry, level) {
|
|
|
212
212
|
if (level === "primitive") return geometry.primitiveCount;
|
|
213
213
|
return 1;
|
|
214
214
|
}
|
|
215
|
-
function checkName(name,
|
|
215
|
+
function checkName(name, path25) {
|
|
216
216
|
if (!NAME.test(name) || RESERVED_NAMES.has(name))
|
|
217
|
-
fail("invalid-name", `${
|
|
217
|
+
fail("invalid-name", `${path25}.${name} is not a valid attribute name`);
|
|
218
218
|
}
|
|
219
219
|
function checkAttributeSet(set, count, level) {
|
|
220
220
|
for (const [name, attribute] of Object.entries(set)) {
|
|
@@ -826,8 +826,8 @@ var init_values = __esm({
|
|
|
826
826
|
});
|
|
827
827
|
|
|
828
828
|
// packages/contracts/src/validate.ts
|
|
829
|
-
function diagnostic(code, message2,
|
|
830
|
-
return { phase: "definition", code: `definition/${code}`, message: message2, path:
|
|
829
|
+
function diagnostic(code, message2, path25) {
|
|
830
|
+
return { phase: "definition", code: `definition/${code}`, message: message2, path: path25 };
|
|
831
831
|
}
|
|
832
832
|
function isRecord2(value) {
|
|
833
833
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -842,12 +842,12 @@ function finiteJson(value) {
|
|
|
842
842
|
if (Array.isArray(value)) return value.every(finiteJson);
|
|
843
843
|
return isRecord2(value) && Object.values(value).every(finiteJson);
|
|
844
844
|
}
|
|
845
|
-
function unknownFields(value, allowed,
|
|
845
|
+
function unknownFields(value, allowed, path25) {
|
|
846
846
|
return Object.keys(value).filter((key2) => !allowed.has(key2)).map(
|
|
847
847
|
(key2) => diagnostic(
|
|
848
848
|
"unknown-field",
|
|
849
|
-
`${
|
|
850
|
-
`${
|
|
849
|
+
`${path25}.${key2} is not allowed`,
|
|
850
|
+
`${path25}.${key2}`
|
|
851
851
|
)
|
|
852
852
|
);
|
|
853
853
|
}
|
|
@@ -879,7 +879,7 @@ function validateNodeDefinition(value) {
|
|
|
879
879
|
if (!isRecord2(value))
|
|
880
880
|
return [diagnostic("not-object", "Definition must be an object")];
|
|
881
881
|
const result = unknownFields(value, TOP_FIELDS, "definition");
|
|
882
|
-
const add = (code, message2,
|
|
882
|
+
const add = (code, message2, path25) => result.push(diagnostic(code, message2, path25));
|
|
883
883
|
const environment = value.runsOn;
|
|
884
884
|
if (value.apiVersion !== 1)
|
|
885
885
|
add("api-version", "apiVersion must be 1", "apiVersion");
|
|
@@ -896,15 +896,15 @@ function validateNodeDefinition(value) {
|
|
|
896
896
|
);
|
|
897
897
|
const seen = /* @__PURE__ */ new Set();
|
|
898
898
|
for (const [index, capability] of (Array.isArray(value.capabilities) ? value.capabilities : []).entries()) {
|
|
899
|
-
const
|
|
899
|
+
const path25 = `capabilities.${index}`;
|
|
900
900
|
if (!seen.has(capability) && !allowed.includes(capability))
|
|
901
901
|
add(
|
|
902
902
|
"capability-not-allowed",
|
|
903
903
|
`${String(capability)} is not available on ${String(environment)}`,
|
|
904
|
-
|
|
904
|
+
path25
|
|
905
905
|
);
|
|
906
906
|
if (seen.has(capability))
|
|
907
|
-
add("duplicate-capability", `${String(capability)} is duplicated`,
|
|
907
|
+
add("duplicate-capability", `${String(capability)} is duplicated`, path25);
|
|
908
908
|
seen.add(capability);
|
|
909
909
|
}
|
|
910
910
|
const inward = /* @__PURE__ */ new Map();
|
|
@@ -917,31 +917,31 @@ function validateNodeDefinition(value) {
|
|
|
917
917
|
continue;
|
|
918
918
|
}
|
|
919
919
|
for (const [name, raw] of Object.entries(entries2)) {
|
|
920
|
-
const
|
|
920
|
+
const path25 = `${section}.${name}`;
|
|
921
921
|
if (!IDENTIFIER.test(name))
|
|
922
|
-
add("invalid-name", `${name} is not a valid identifier`,
|
|
923
|
-
if (RESERVED.has(name)) add("reserved-name", `${name} is reserved`,
|
|
922
|
+
add("invalid-name", `${name} is not a valid identifier`, path25);
|
|
923
|
+
if (RESERVED.has(name)) add("reserved-name", `${name} is reserved`, path25);
|
|
924
924
|
const names = section === "outputs" ? outward : inward;
|
|
925
925
|
const previous = names.get(name);
|
|
926
926
|
if (previous)
|
|
927
927
|
add(
|
|
928
928
|
"name-collision",
|
|
929
929
|
`${name} is declared in ${previous} and ${section}`,
|
|
930
|
-
|
|
930
|
+
path25
|
|
931
931
|
);
|
|
932
932
|
else names.set(name, section);
|
|
933
933
|
if (!isRecord2(raw)) {
|
|
934
|
-
add("invalid-entry", `${
|
|
934
|
+
add("invalid-entry", `${path25} must be an object`, path25);
|
|
935
935
|
continue;
|
|
936
936
|
}
|
|
937
937
|
const fields2 = section === "props" ? PROP_FIELDS : section === "outputs" ? OUTPUT_FIELDS : DATA_FIELDS;
|
|
938
938
|
if (section !== "props" && raw.kind !== "data" && raw.kind !== "trigger") {
|
|
939
939
|
add(
|
|
940
940
|
"invalid-kind",
|
|
941
|
-
`${
|
|
942
|
-
`${
|
|
941
|
+
`${path25}.kind must be data or trigger`,
|
|
942
|
+
`${path25}.kind`
|
|
943
943
|
);
|
|
944
|
-
result.push(...unknownFields(raw, fields2,
|
|
944
|
+
result.push(...unknownFields(raw, fields2, path25));
|
|
945
945
|
continue;
|
|
946
946
|
}
|
|
947
947
|
if (section !== "props" && raw.kind === "trigger") {
|
|
@@ -951,22 +951,22 @@ function validateNodeDefinition(value) {
|
|
|
951
951
|
add(
|
|
952
952
|
"trigger-extra",
|
|
953
953
|
"Trigger ports allow only kind and description",
|
|
954
|
-
|
|
954
|
+
path25
|
|
955
955
|
);
|
|
956
956
|
continue;
|
|
957
957
|
}
|
|
958
|
-
result.push(...unknownFields(raw, fields2,
|
|
958
|
+
result.push(...unknownFields(raw, fields2, path25));
|
|
959
959
|
if ("control" in raw && (typeof raw.control !== "string" || !CONTROLS.has(raw.control)))
|
|
960
960
|
add(
|
|
961
961
|
"invalid-control",
|
|
962
962
|
`${String(raw.control)} is not a Cascade control`,
|
|
963
|
-
`${
|
|
963
|
+
`${path25}.control`
|
|
964
964
|
);
|
|
965
965
|
if ("options" in raw && raw.control !== "select")
|
|
966
966
|
add(
|
|
967
967
|
"options-without-select",
|
|
968
968
|
"options require the select control",
|
|
969
|
-
`${
|
|
969
|
+
`${path25}.options`
|
|
970
970
|
);
|
|
971
971
|
if (Array.isArray(raw.options))
|
|
972
972
|
for (const [index, option] of raw.options.entries()) {
|
|
@@ -976,19 +976,19 @@ function validateNodeDefinition(value) {
|
|
|
976
976
|
add(
|
|
977
977
|
"invalid-option",
|
|
978
978
|
"a labelled option needs a value",
|
|
979
|
-
`${
|
|
979
|
+
`${path25}.options[${index}]`
|
|
980
980
|
);
|
|
981
981
|
if (typeof entry.label !== "string" || !entry.label)
|
|
982
982
|
add(
|
|
983
983
|
"invalid-option",
|
|
984
984
|
"a labelled option needs a non-empty label",
|
|
985
|
-
`${
|
|
985
|
+
`${path25}.options[${index}].label`
|
|
986
986
|
);
|
|
987
987
|
if ("disabled" in entry && typeof entry.disabled !== "boolean")
|
|
988
988
|
add(
|
|
989
989
|
"invalid-option",
|
|
990
990
|
"disabled must be a boolean",
|
|
991
|
-
`${
|
|
991
|
+
`${path25}.options[${index}].disabled`
|
|
992
992
|
);
|
|
993
993
|
}
|
|
994
994
|
const type = raw.type;
|
|
@@ -996,56 +996,56 @@ function validateNodeDefinition(value) {
|
|
|
996
996
|
add(
|
|
997
997
|
"invalid-type",
|
|
998
998
|
`${String(type)} is not a Cascade type`,
|
|
999
|
-
`${
|
|
999
|
+
`${path25}.type`
|
|
1000
1000
|
);
|
|
1001
1001
|
continue;
|
|
1002
1002
|
}
|
|
1003
1003
|
if (type === "texture" && environment !== "browser")
|
|
1004
|
-
add("texture-not-portable", "texture is browser-only", `${
|
|
1004
|
+
add("texture-not-portable", "texture is browser-only", `${path25}.type`);
|
|
1005
1005
|
if (type === "texture" && "default" in raw)
|
|
1006
1006
|
add(
|
|
1007
1007
|
"texture-default",
|
|
1008
1008
|
"texture cannot have a default",
|
|
1009
|
-
`${
|
|
1009
|
+
`${path25}.default`
|
|
1010
1010
|
);
|
|
1011
1011
|
if (section === "props" && !("default" in raw))
|
|
1012
|
-
add("missing-default", "Props require defaults", `${
|
|
1012
|
+
add("missing-default", "Props require defaults", `${path25}.default`);
|
|
1013
1013
|
if ("default" in raw && !finiteJson(raw.default))
|
|
1014
1014
|
add(
|
|
1015
1015
|
"invalid-default",
|
|
1016
1016
|
"Default must be finite JSON",
|
|
1017
|
-
`${
|
|
1017
|
+
`${path25}.default`
|
|
1018
1018
|
);
|
|
1019
1019
|
else if ("default" in raw && !defaultMatches(type, raw.default))
|
|
1020
1020
|
add(
|
|
1021
1021
|
"default-type",
|
|
1022
1022
|
`Default does not match ${type}`,
|
|
1023
|
-
`${
|
|
1023
|
+
`${path25}.default`
|
|
1024
1024
|
);
|
|
1025
1025
|
if (type === "int" && "default" in raw && (!Number.isFinite(raw.default) || !Number.isInteger(raw.default)))
|
|
1026
1026
|
add(
|
|
1027
1027
|
"non-integer",
|
|
1028
1028
|
"Integer defaults must be finite integers",
|
|
1029
|
-
`${
|
|
1029
|
+
`${path25}.default`
|
|
1030
1030
|
);
|
|
1031
1031
|
if ("step" in raw && (typeof raw.step !== "number" || !Number.isFinite(raw.step) || raw.step <= 0))
|
|
1032
|
-
add("invalid-step", "step must be greater than zero", `${
|
|
1032
|
+
add("invalid-step", "step must be greater than zero", `${path25}.step`);
|
|
1033
1033
|
if (section === "props" && "expression" in raw && (typeof raw.expression !== "string" || raw.expression.length === 0))
|
|
1034
1034
|
add(
|
|
1035
1035
|
"invalid-expression",
|
|
1036
1036
|
"expression must be a non-empty string",
|
|
1037
|
-
`${
|
|
1037
|
+
`${path25}.expression`
|
|
1038
1038
|
);
|
|
1039
1039
|
for (const field of ["min", "max"]) {
|
|
1040
1040
|
if (field in raw && (typeof raw[field] !== "number" || !Number.isFinite(raw[field])))
|
|
1041
1041
|
add(
|
|
1042
1042
|
`invalid-${field}`,
|
|
1043
1043
|
`${field} must be a finite number`,
|
|
1044
|
-
`${
|
|
1044
|
+
`${path25}.${field}`
|
|
1045
1045
|
);
|
|
1046
1046
|
}
|
|
1047
1047
|
if (typeof raw.min === "number" && typeof raw.max === "number" && raw.min > raw.max)
|
|
1048
|
-
add("invalid-range", "min must not exceed max",
|
|
1048
|
+
add("invalid-range", "min must not exceed max", path25);
|
|
1049
1049
|
}
|
|
1050
1050
|
}
|
|
1051
1051
|
return result;
|
|
@@ -1741,10 +1741,10 @@ var init_evaluator = __esm({
|
|
|
1741
1741
|
fframe: this.timeState.frameFraction,
|
|
1742
1742
|
fps: this.timeState.fps,
|
|
1743
1743
|
self: node,
|
|
1744
|
-
ch: (
|
|
1745
|
-
chs: (
|
|
1746
|
-
chv: (
|
|
1747
|
-
opexist: (
|
|
1744
|
+
ch: (path25) => this.ch(node, path25),
|
|
1745
|
+
chs: (path25) => this.chs(node, path25),
|
|
1746
|
+
chv: (path25) => this.chv(node, path25),
|
|
1747
|
+
opexist: (path25) => this.scope?.nodeExists(node, path25) ? 1 : 0,
|
|
1748
1748
|
opinput: (index) => this.scope?.inputNode(node, index) ?? null,
|
|
1749
1749
|
fit,
|
|
1750
1750
|
fit01,
|
|
@@ -1761,8 +1761,8 @@ var init_evaluator = __esm({
|
|
|
1761
1761
|
};
|
|
1762
1762
|
}
|
|
1763
1763
|
// ============ Channel functions ============
|
|
1764
|
-
ch(node,
|
|
1765
|
-
const resolved = this.resolveParam(node,
|
|
1764
|
+
ch(node, path25) {
|
|
1765
|
+
const resolved = this.resolveParam(node, path25);
|
|
1766
1766
|
if (!resolved) return 0;
|
|
1767
1767
|
const value = resolved.value;
|
|
1768
1768
|
if (typeof value === "number") return value;
|
|
@@ -1770,13 +1770,13 @@ var init_evaluator = __esm({
|
|
|
1770
1770
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
1771
1771
|
return 0;
|
|
1772
1772
|
}
|
|
1773
|
-
chs(node,
|
|
1774
|
-
const resolved = this.resolveParam(node,
|
|
1773
|
+
chs(node, path25) {
|
|
1774
|
+
const resolved = this.resolveParam(node, path25);
|
|
1775
1775
|
if (!resolved) return "";
|
|
1776
1776
|
return String(resolved.value ?? "");
|
|
1777
1777
|
}
|
|
1778
|
-
chv(node,
|
|
1779
|
-
const resolved = this.resolveParam(node,
|
|
1778
|
+
chv(node, path25) {
|
|
1779
|
+
const resolved = this.resolveParam(node, path25);
|
|
1780
1780
|
if (!resolved) return [];
|
|
1781
1781
|
const value = resolved.value;
|
|
1782
1782
|
if (Array.isArray(value)) {
|
|
@@ -1785,12 +1785,12 @@ var init_evaluator = __esm({
|
|
|
1785
1785
|
if (typeof value === "number") return [value];
|
|
1786
1786
|
return [];
|
|
1787
1787
|
}
|
|
1788
|
-
resolveParam(node,
|
|
1789
|
-
return this.scope?.resolveParam(node,
|
|
1788
|
+
resolveParam(node, path25) {
|
|
1789
|
+
return this.scope?.resolveParam(node, path25) ?? null;
|
|
1790
1790
|
}
|
|
1791
1791
|
/** True if a referenced path resolves from this node. */
|
|
1792
|
-
validatePath(fromNode,
|
|
1793
|
-
return this.resolveParam(fromNode,
|
|
1792
|
+
validatePath(fromNode, path25) {
|
|
1793
|
+
return this.resolveParam(fromNode, path25) !== null;
|
|
1794
1794
|
}
|
|
1795
1795
|
/** Parameter paths referenced through ch/chs/chv. */
|
|
1796
1796
|
extractDependencies(expression) {
|
|
@@ -1829,14 +1829,14 @@ function geometryError(code, message2) {
|
|
|
1829
1829
|
function attributeSet(geometry, level) {
|
|
1830
1830
|
return geometry[level];
|
|
1831
1831
|
}
|
|
1832
|
-
function numericAttribute(attribute,
|
|
1832
|
+
function numericAttribute(attribute, path25, sizes) {
|
|
1833
1833
|
if (attribute === void 0) return void 0;
|
|
1834
1834
|
if (isStringAttribute(attribute))
|
|
1835
|
-
geometryError("attribute-storage", `${
|
|
1835
|
+
geometryError("attribute-storage", `${path25} must be numeric`);
|
|
1836
1836
|
if (sizes !== void 0 && !sizes.includes(attribute.size))
|
|
1837
1837
|
geometryError(
|
|
1838
1838
|
"attribute-size",
|
|
1839
|
-
`${
|
|
1839
|
+
`${path25} has size ${attribute.size}; expected ${sizes.join(" or ")}`
|
|
1840
1840
|
);
|
|
1841
1841
|
return attribute;
|
|
1842
1842
|
}
|
|
@@ -2647,7 +2647,7 @@ function geometryToSvg(geometry, options2 = {}) {
|
|
|
2647
2647
|
if (mask2[primitive] === 1 && assigned[primitive] === -1)
|
|
2648
2648
|
assigned[primitive] = index;
|
|
2649
2649
|
}
|
|
2650
|
-
const
|
|
2650
|
+
const path25 = (primitive) => {
|
|
2651
2651
|
const attributes = [`d="${pathData(geometry, primitive, number)}"`];
|
|
2652
2652
|
const paint = paintOf(colour, primitive, "primitive");
|
|
2653
2653
|
if (paint !== void 0) {
|
|
@@ -2666,13 +2666,13 @@ function geometryToSvg(geometry, options2 = {}) {
|
|
|
2666
2666
|
const body = [];
|
|
2667
2667
|
if (names.length === 0) {
|
|
2668
2668
|
for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
|
|
2669
|
-
body.push(indent(
|
|
2669
|
+
body.push(indent(path25(primitive), 2));
|
|
2670
2670
|
} else {
|
|
2671
2671
|
for (let index = 0; index < names.length; index += 1) {
|
|
2672
2672
|
const members = [];
|
|
2673
2673
|
for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
|
|
2674
2674
|
if (assigned[primitive] === index)
|
|
2675
|
-
members.push(indent(
|
|
2675
|
+
members.push(indent(path25(primitive), 3));
|
|
2676
2676
|
body.push(
|
|
2677
2677
|
indent(`<g id="${escapeXml(names[index])}">`, 2),
|
|
2678
2678
|
...members,
|
|
@@ -2681,7 +2681,7 @@ function geometryToSvg(geometry, options2 = {}) {
|
|
|
2681
2681
|
}
|
|
2682
2682
|
const rest = [];
|
|
2683
2683
|
for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
|
|
2684
|
-
if (assigned[primitive] === -1) rest.push(indent(
|
|
2684
|
+
if (assigned[primitive] === -1) rest.push(indent(path25(primitive), 3));
|
|
2685
2685
|
if (rest.length > 0)
|
|
2686
2686
|
body.push(
|
|
2687
2687
|
indent(`<g id="${UNGROUPED}">`, 2),
|
|
@@ -2788,11 +2788,11 @@ function validateOptions(options2) {
|
|
|
2788
2788
|
if (!Number.isSafeInteger(precision) || precision < 0 || precision > 15)
|
|
2789
2789
|
geometryError("svg-precision", "precision must be an integer from 0 to 15");
|
|
2790
2790
|
}
|
|
2791
|
-
function validateRange(attribute,
|
|
2791
|
+
function validateRange(attribute, path25, accepts, expectation) {
|
|
2792
2792
|
if (attribute === void 0) return;
|
|
2793
2793
|
for (const value of attribute.data)
|
|
2794
2794
|
if (!accepts(value))
|
|
2795
|
-
geometryError("attribute-value", `${
|
|
2795
|
+
geometryError("attribute-value", `${path25} values must be ${expectation}`);
|
|
2796
2796
|
}
|
|
2797
2797
|
function indent(line2, depth) {
|
|
2798
2798
|
return `${" ".repeat(depth)}${line2}`;
|
|
@@ -3081,8 +3081,8 @@ var init_resolve = __esm({
|
|
|
3081
3081
|
});
|
|
3082
3082
|
|
|
3083
3083
|
// packages/runtime/src/params/animator.ts
|
|
3084
|
-
function split(
|
|
3085
|
-
return String(
|
|
3084
|
+
function split(path25) {
|
|
3085
|
+
return String(path25 ?? "").split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
3086
3086
|
}
|
|
3087
3087
|
var PropAnimator;
|
|
3088
3088
|
var init_animator = __esm({
|
|
@@ -3150,8 +3150,8 @@ var init_animator = __esm({
|
|
|
3150
3150
|
*/
|
|
3151
3151
|
scope() {
|
|
3152
3152
|
return {
|
|
3153
|
-
resolveParam: (from,
|
|
3154
|
-
const segments = split(
|
|
3153
|
+
resolveParam: (from, path25) => {
|
|
3154
|
+
const segments = split(path25);
|
|
3155
3155
|
if (!segments.length) return null;
|
|
3156
3156
|
const name = segments[segments.length - 1];
|
|
3157
3157
|
const owner = this.owner(from, segments.slice(0, -1));
|
|
@@ -3161,8 +3161,8 @@ var init_animator = __esm({
|
|
|
3161
3161
|
if (name in owner.inputs) return { value: owner.inputs[name] };
|
|
3162
3162
|
return null;
|
|
3163
3163
|
},
|
|
3164
|
-
nodeExists: (from,
|
|
3165
|
-
const segments = split(
|
|
3164
|
+
nodeExists: (from, path25) => {
|
|
3165
|
+
const segments = split(path25);
|
|
3166
3166
|
if (!segments.length) return false;
|
|
3167
3167
|
const last = segments[segments.length - 1];
|
|
3168
3168
|
if (last === "..") return true;
|
|
@@ -5479,12 +5479,12 @@ function buildTrails(history, current, options2 = {}) {
|
|
|
5479
5479
|
for (const frame of frames) {
|
|
5480
5480
|
for (let index = 0; index < frame.id.length; index += 1) {
|
|
5481
5481
|
const id = frame.id[index];
|
|
5482
|
-
const
|
|
5483
|
-
|
|
5482
|
+
const path25 = paths.get(id) ?? [];
|
|
5483
|
+
path25.push(
|
|
5484
5484
|
frame.position[index * frame.size],
|
|
5485
5485
|
frame.position[index * frame.size + 1]
|
|
5486
5486
|
);
|
|
5487
|
-
paths.set(id,
|
|
5487
|
+
paths.set(id, path25);
|
|
5488
5488
|
}
|
|
5489
5489
|
}
|
|
5490
5490
|
if (result === "velocity") {
|
|
@@ -5492,11 +5492,11 @@ function buildTrails(history, current, options2 = {}) {
|
|
|
5492
5492
|
}
|
|
5493
5493
|
const builder = new GeometryBuilder({ positionSize: 2 });
|
|
5494
5494
|
const trailIds = [];
|
|
5495
|
-
for (const [id,
|
|
5496
|
-
if (
|
|
5495
|
+
for (const [id, path25] of paths) {
|
|
5496
|
+
if (path25.length < 4) continue;
|
|
5497
5497
|
const points = [];
|
|
5498
|
-
for (let at = 0; at <
|
|
5499
|
-
points.push(builder.addPoint(
|
|
5498
|
+
for (let at = 0; at < path25.length; at += 2) {
|
|
5499
|
+
points.push(builder.addPoint(path25[at], path25[at + 1]));
|
|
5500
5500
|
}
|
|
5501
5501
|
builder.addPrimitive(points, { closed: false, kind: "poly" });
|
|
5502
5502
|
trailIds.push(id);
|
|
@@ -5541,11 +5541,11 @@ function withDerivedVelocity(current, paths, velocityscale) {
|
|
|
5541
5541
|
const ids = current.point.id?.data;
|
|
5542
5542
|
const velocity = new Float32Array(current.pointCount * size);
|
|
5543
5543
|
for (let index = 0; index < current.pointCount; index += 1) {
|
|
5544
|
-
const
|
|
5545
|
-
if (!
|
|
5546
|
-
const last =
|
|
5547
|
-
velocity[index * size] = (
|
|
5548
|
-
velocity[index * size + 1] = (
|
|
5544
|
+
const path25 = ids ? paths.get(Number(ids[index])) : void 0;
|
|
5545
|
+
if (!path25 || path25.length < 4) continue;
|
|
5546
|
+
const last = path25.length;
|
|
5547
|
+
velocity[index * size] = (path25[last - 2] - path25[last - 4]) * velocityscale;
|
|
5548
|
+
velocity[index * size + 1] = (path25[last - 1] - path25[last - 3]) * velocityscale;
|
|
5549
5549
|
}
|
|
5550
5550
|
return {
|
|
5551
5551
|
...current,
|
|
@@ -7063,16 +7063,16 @@ function cloneValue(value, ancestors) {
|
|
|
7063
7063
|
ancestors.delete(value);
|
|
7064
7064
|
return Object.freeze(clone);
|
|
7065
7065
|
}
|
|
7066
|
-
function runtimeValue(value, type, allowUndefined,
|
|
7066
|
+
function runtimeValue(value, type, allowUndefined, path25) {
|
|
7067
7067
|
if (value === void 0) {
|
|
7068
7068
|
if (allowUndefined) return void 0;
|
|
7069
|
-
misuse("runtime/value-type", `${
|
|
7069
|
+
misuse("runtime/value-type", `${path25} requires a ${type} value`);
|
|
7070
7070
|
}
|
|
7071
7071
|
const tupleLength = TUPLE_LENGTHS[type];
|
|
7072
7072
|
const valid = type === "float" && typeof value === "number" && Number.isFinite(value) || type === "int" && typeof value === "number" && Number.isInteger(value) || type === "bool" && typeof value === "boolean" || type === "string" && typeof value === "string" || tupleLength !== void 0 && Array.isArray(value) && value.length === tupleLength && value.every(
|
|
7073
7073
|
(item) => type.endsWith("i") ? typeof item === "number" && Number.isInteger(item) : typeof item === "number" && Number.isFinite(item)
|
|
7074
7074
|
) || !tupleLength && type !== "float" && type !== "int" && type !== "bool" && type !== "string";
|
|
7075
|
-
if (!valid) misuse("runtime/value-type", `${
|
|
7075
|
+
if (!valid) misuse("runtime/value-type", `${path25} is not a valid ${type}`);
|
|
7076
7076
|
return snapshot(value);
|
|
7077
7077
|
}
|
|
7078
7078
|
var PREFLIGHT_WARNING_CODES, RUN_TOLERATED_CODES, MutableAbortSignal, Runtime, Graph, TUPLE_LENGTHS;
|
|
@@ -8352,32 +8352,32 @@ function extractNodeDefinition(source, fileName, ts4) {
|
|
|
8352
8352
|
diagnostics: [error.diagnostic]
|
|
8353
8353
|
};
|
|
8354
8354
|
}
|
|
8355
|
-
function read(node,
|
|
8355
|
+
function read(node, path25, depth) {
|
|
8356
8356
|
if (++visited > MAX_NODES)
|
|
8357
8357
|
throw extraction(
|
|
8358
8358
|
node,
|
|
8359
8359
|
"definition/size-limit",
|
|
8360
8360
|
"Definition exceeds 10,000 syntax nodes",
|
|
8361
|
-
|
|
8361
|
+
path25
|
|
8362
8362
|
);
|
|
8363
8363
|
if (depth > MAX_DEPTH)
|
|
8364
8364
|
throw extraction(
|
|
8365
8365
|
node,
|
|
8366
8366
|
"definition/depth-limit",
|
|
8367
8367
|
"Definition exceeds 32 levels",
|
|
8368
|
-
|
|
8368
|
+
path25
|
|
8369
8369
|
);
|
|
8370
8370
|
if (ts4.isParenthesizedExpression(node) || ts4.isSatisfiesExpression(node))
|
|
8371
|
-
return read(node.expression,
|
|
8371
|
+
return read(node.expression, path25, depth + 1);
|
|
8372
8372
|
if (ts4.isAsExpression(node)) {
|
|
8373
8373
|
if (node.type.getText(file) !== "const")
|
|
8374
8374
|
throw extraction(
|
|
8375
8375
|
node.type,
|
|
8376
8376
|
"definition/non-literal",
|
|
8377
8377
|
"Only 'as const' assertions are allowed",
|
|
8378
|
-
|
|
8378
|
+
path25
|
|
8379
8379
|
);
|
|
8380
|
-
return read(node.expression,
|
|
8380
|
+
return read(node.expression, path25, depth + 1);
|
|
8381
8381
|
}
|
|
8382
8382
|
if (ts4.isStringLiteral(node) || ts4.isNoSubstitutionTemplateLiteral(node))
|
|
8383
8383
|
return node.text;
|
|
@@ -8394,9 +8394,9 @@ function extractNodeDefinition(source, fileName, ts4) {
|
|
|
8394
8394
|
element,
|
|
8395
8395
|
"definition/spread",
|
|
8396
8396
|
"Spreads are not allowed",
|
|
8397
|
-
`${
|
|
8397
|
+
`${path25}.${index}`
|
|
8398
8398
|
);
|
|
8399
|
-
return read(element, `${
|
|
8399
|
+
return read(element, `${path25}.${index}`, depth + 1);
|
|
8400
8400
|
});
|
|
8401
8401
|
if (ts4.isObjectLiteralExpression(node)) {
|
|
8402
8402
|
const object = /* @__PURE__ */ Object.create(null);
|
|
@@ -8407,14 +8407,14 @@ function extractNodeDefinition(source, fileName, ts4) {
|
|
|
8407
8407
|
property,
|
|
8408
8408
|
"definition/spread",
|
|
8409
8409
|
"Spreads are not allowed",
|
|
8410
|
-
|
|
8410
|
+
path25
|
|
8411
8411
|
);
|
|
8412
8412
|
if (!ts4.isPropertyAssignment(property) || property.name && ts4.isComputedPropertyName(property.name))
|
|
8413
8413
|
throw extraction(
|
|
8414
8414
|
property,
|
|
8415
8415
|
"definition/non-literal",
|
|
8416
8416
|
"Only explicit properties are allowed",
|
|
8417
|
-
|
|
8417
|
+
path25
|
|
8418
8418
|
);
|
|
8419
8419
|
const key2 = ts4.isIdentifier(property.name) || ts4.isStringLiteral(property.name) || ts4.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
8420
8420
|
if (key2 === void 0)
|
|
@@ -8422,17 +8422,17 @@ function extractNodeDefinition(source, fileName, ts4) {
|
|
|
8422
8422
|
property,
|
|
8423
8423
|
"definition/non-literal",
|
|
8424
8424
|
"Property name must be literal",
|
|
8425
|
-
|
|
8425
|
+
path25
|
|
8426
8426
|
);
|
|
8427
8427
|
if (keys.has(key2))
|
|
8428
8428
|
throw extraction(
|
|
8429
8429
|
property.name,
|
|
8430
8430
|
"definition/duplicate-key",
|
|
8431
8431
|
`Duplicate key ${key2}`,
|
|
8432
|
-
`${
|
|
8432
|
+
`${path25}.${key2}`
|
|
8433
8433
|
);
|
|
8434
8434
|
keys.add(key2);
|
|
8435
|
-
object[key2] = read(property.initializer, `${
|
|
8435
|
+
object[key2] = read(property.initializer, `${path25}.${key2}`, depth + 1);
|
|
8436
8436
|
}
|
|
8437
8437
|
return object;
|
|
8438
8438
|
}
|
|
@@ -8440,19 +8440,19 @@ function extractNodeDefinition(source, fileName, ts4) {
|
|
|
8440
8440
|
node,
|
|
8441
8441
|
"definition/non-literal",
|
|
8442
8442
|
`Unsupported ${ts4.SyntaxKind[node.kind]}`,
|
|
8443
|
-
|
|
8443
|
+
path25
|
|
8444
8444
|
);
|
|
8445
8445
|
}
|
|
8446
8446
|
function location(node) {
|
|
8447
8447
|
const point = file.getLineAndCharacterOfPosition(node.getStart(file));
|
|
8448
8448
|
return { line: point.line + 1, column: point.character + 1 };
|
|
8449
8449
|
}
|
|
8450
|
-
function extraction(node, code, message2,
|
|
8450
|
+
function extraction(node, code, message2, path25) {
|
|
8451
8451
|
return new ExtractionFailure({
|
|
8452
8452
|
phase: "definition",
|
|
8453
8453
|
code,
|
|
8454
8454
|
message: message2,
|
|
8455
|
-
path:
|
|
8455
|
+
path: path25,
|
|
8456
8456
|
file: fileName,
|
|
8457
8457
|
...location(node)
|
|
8458
8458
|
});
|
|
@@ -9568,13 +9568,14 @@ export function execute(node, graph) {
|
|
|
9568
9568
|
import fs6 from "node:fs/promises";
|
|
9569
9569
|
import path6 from "node:path";
|
|
9570
9570
|
function createNodeFileCapability(project) {
|
|
9571
|
-
const root = path6.resolve(project.resolve("."));
|
|
9572
9571
|
const within3 = (relative, what) => {
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
throw new Error(
|
|
9577
|
-
|
|
9572
|
+
try {
|
|
9573
|
+
return project.resolve(relative.replace(/^\.?\//, ""));
|
|
9574
|
+
} catch (error) {
|
|
9575
|
+
throw new Error(
|
|
9576
|
+
`files: ${what} outside the project is refused \u2014 ${relative} (${error instanceof Error ? error.message : String(error)})`
|
|
9577
|
+
);
|
|
9578
|
+
}
|
|
9578
9579
|
};
|
|
9579
9580
|
const cacheOnly = (relative) => {
|
|
9580
9581
|
const clean = relative.replace(/^\.?\//, "");
|
|
@@ -9974,6 +9975,7 @@ async function renderDeterministicProjectFrames(file, document2, options2) {
|
|
|
9974
9975
|
try {
|
|
9975
9976
|
graph = await runtime.load(document2);
|
|
9976
9977
|
reportPreflightWarnings(host, classifyPreflight(graph.preflight()).warnings);
|
|
9978
|
+
const targets = options2.nodes?.length ? options2.nodes : options2.entryNode ? [options2.entryNode] : [];
|
|
9977
9979
|
const outDirectory = project.resolve(options2.out);
|
|
9978
9980
|
await fs8.mkdir(outDirectory, { recursive: true });
|
|
9979
9981
|
for (const frame of frameRange(options2.start, options2.end, options2.step ?? 1)) {
|
|
@@ -9984,17 +9986,24 @@ async function renderDeterministicProjectFrames(file, document2, options2) {
|
|
|
9984
9986
|
const result = await graph.run({
|
|
9985
9987
|
frame,
|
|
9986
9988
|
...options2.fps === void 0 ? {} : { fps: options2.fps },
|
|
9987
|
-
|
|
9989
|
+
/**
|
|
9990
|
+
* One named node narrows the COOK as well as the output, because the
|
|
9991
|
+
* runtime can walk back from a single target. Several cannot be
|
|
9992
|
+
* expressed as one target, so the graph cooks whole and only the saving
|
|
9993
|
+
* narrows — which is still the bulk of the win when the outputs are
|
|
9994
|
+
* 18 MB PNGs.
|
|
9995
|
+
*/
|
|
9996
|
+
...targets.length === 1 ? { target: { kind: "node", nodeId: targets[0] } } : {}
|
|
9988
9997
|
});
|
|
9989
9998
|
if (result.status !== "completed") {
|
|
9990
9999
|
throw new Error(
|
|
9991
10000
|
`frame ${frame} ${result.status} \u2014 ${result.diagnostics.map((item) => item.message).join("; ") || "no diagnostics"}`
|
|
9992
10001
|
);
|
|
9993
10002
|
}
|
|
9994
|
-
const outputs = imageOutputs(graph,
|
|
10003
|
+
const outputs = imageOutputs(graph, targets);
|
|
9995
10004
|
if (!outputs.length) {
|
|
9996
10005
|
throw new Error(
|
|
9997
|
-
"No image output to save: the graph has no unconsumed image port. Name the node to render with --
|
|
10006
|
+
targets.length ? `No image output on ${targets.join(", ")} \u2014 check the node id, and that it has an image output.` : "No image output to save: the graph has no unconsumed image port. Name the node to render with --node."
|
|
9998
10007
|
);
|
|
9999
10008
|
}
|
|
10000
10009
|
for (const { nodeId, value } of outputs) {
|
|
@@ -10023,13 +10032,14 @@ async function renderDeterministicProjectFrames(file, document2, options2) {
|
|
|
10023
10032
|
async function disposeProjectGpu(host) {
|
|
10024
10033
|
await host.capabilities.gpu?.dispose();
|
|
10025
10034
|
}
|
|
10026
|
-
function imageOutputs(graph,
|
|
10035
|
+
function imageOutputs(graph, nodes = []) {
|
|
10036
|
+
const wanted = new Set(nodes);
|
|
10027
10037
|
const found = [];
|
|
10028
10038
|
for (const node of graph.inspect().nodes) {
|
|
10029
|
-
if (
|
|
10039
|
+
if (wanted.size && !wanted.has(node.id)) continue;
|
|
10030
10040
|
for (const port of Object.values(node.outputs)) {
|
|
10031
10041
|
if (port.type !== "image") continue;
|
|
10032
|
-
if (!
|
|
10042
|
+
if (!wanted.size && port.connected) continue;
|
|
10033
10043
|
if (port.value === void 0) continue;
|
|
10034
10044
|
found.push({ nodeId: node.id, value: port.value });
|
|
10035
10045
|
}
|
|
@@ -11011,6 +11021,8 @@ var init_git = __esm({
|
|
|
11011
11021
|
|
|
11012
11022
|
// server/src/routes/graph.ts
|
|
11013
11023
|
import { Router } from "express";
|
|
11024
|
+
import fs14 from "node:fs/promises";
|
|
11025
|
+
import path17 from "node:path";
|
|
11014
11026
|
function handleError(res, err) {
|
|
11015
11027
|
if (err instanceof PathSafetyError) {
|
|
11016
11028
|
res.status(400).json({ error: err.message });
|
|
@@ -11063,6 +11075,42 @@ function createGraphRouter(project) {
|
|
|
11063
11075
|
handleError(res, err);
|
|
11064
11076
|
}
|
|
11065
11077
|
});
|
|
11078
|
+
router.get("/:filename/workspace", async (req, res) => {
|
|
11079
|
+
try {
|
|
11080
|
+
const target = workspacePath(project, req.params.filename);
|
|
11081
|
+
await fs14.access(project.resolve(req.params.filename));
|
|
11082
|
+
let content;
|
|
11083
|
+
try {
|
|
11084
|
+
content = await fs14.readFile(target, "utf-8");
|
|
11085
|
+
} catch (err) {
|
|
11086
|
+
if (err?.code === "ENOENT") {
|
|
11087
|
+
res.json(null);
|
|
11088
|
+
return;
|
|
11089
|
+
}
|
|
11090
|
+
throw err;
|
|
11091
|
+
}
|
|
11092
|
+
res.type("application/json").send(content);
|
|
11093
|
+
} catch (err) {
|
|
11094
|
+
handleError(res, err);
|
|
11095
|
+
}
|
|
11096
|
+
});
|
|
11097
|
+
router.put("/:filename/workspace", async (req, res) => {
|
|
11098
|
+
try {
|
|
11099
|
+
const body = req.body;
|
|
11100
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || !body.layout || typeof body.layout !== "object") {
|
|
11101
|
+
res.status(400).json({ error: "a workspace is an object with a layout" });
|
|
11102
|
+
return;
|
|
11103
|
+
}
|
|
11104
|
+
const slot = req.query.slot === "failed" ? "failed" : null;
|
|
11105
|
+
const target = workspacePath(project, req.params.filename, slot);
|
|
11106
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
11107
|
+
await fs14.writeFile(temporary, JSON.stringify(body, null, 1), "utf-8");
|
|
11108
|
+
await fs14.rename(temporary, target);
|
|
11109
|
+
res.json({ ok: true, file: path17.basename(target) });
|
|
11110
|
+
} catch (err) {
|
|
11111
|
+
handleError(res, err);
|
|
11112
|
+
}
|
|
11113
|
+
});
|
|
11066
11114
|
router.get("/:filename/versions", async (req, res) => {
|
|
11067
11115
|
try {
|
|
11068
11116
|
const filename = req.params.filename;
|
|
@@ -11100,6 +11148,13 @@ function createGraphRouter(project) {
|
|
|
11100
11148
|
});
|
|
11101
11149
|
return router;
|
|
11102
11150
|
}
|
|
11151
|
+
function workspacePath(project, filename, slot = null) {
|
|
11152
|
+
if (!filename.endsWith(".cascade") || filename !== path17.basename(filename)) {
|
|
11153
|
+
throw new PathSafetyError(`not a .cascade file: ${filename}`);
|
|
11154
|
+
}
|
|
11155
|
+
const stem = filename.slice(0, -".cascade".length);
|
|
11156
|
+
return project.resolve(`${stem}.workspace${slot ? `.${slot}` : ""}.json`);
|
|
11157
|
+
}
|
|
11103
11158
|
function saveCommitMessage(filename, at = /* @__PURE__ */ new Date()) {
|
|
11104
11159
|
const time = at.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
11105
11160
|
return `Saved ${filename} ${time}`;
|
|
@@ -11116,8 +11171,8 @@ var init_graph = __esm({
|
|
|
11116
11171
|
// server/src/routes/assets.ts
|
|
11117
11172
|
import { Router as Router2 } from "express";
|
|
11118
11173
|
import multer from "multer";
|
|
11119
|
-
import
|
|
11120
|
-
import
|
|
11174
|
+
import path18 from "path";
|
|
11175
|
+
import fs15 from "fs/promises";
|
|
11121
11176
|
function handleError2(res, err) {
|
|
11122
11177
|
if (err instanceof PathSafetyError) {
|
|
11123
11178
|
res.status(400).json({ error: err.message });
|
|
@@ -11133,8 +11188,8 @@ function createAssetsRouter(project) {
|
|
|
11133
11188
|
let assetType = "data";
|
|
11134
11189
|
if (file.mimetype.startsWith("image/")) assetType = "images";
|
|
11135
11190
|
else if (file.mimetype.startsWith("audio/")) assetType = "audio";
|
|
11136
|
-
const dir =
|
|
11137
|
-
await
|
|
11191
|
+
const dir = path18.join(assetsRoot, assetType);
|
|
11192
|
+
await fs15.mkdir(dir, { recursive: true });
|
|
11138
11193
|
cb(null, dir);
|
|
11139
11194
|
},
|
|
11140
11195
|
filename: (_req, file, cb) => cb(null, file.originalname)
|
|
@@ -11145,7 +11200,7 @@ function createAssetsRouter(project) {
|
|
|
11145
11200
|
res.status(400).json({ error: "no file uploaded" });
|
|
11146
11201
|
return;
|
|
11147
11202
|
}
|
|
11148
|
-
const relativePath =
|
|
11203
|
+
const relativePath = path18.relative(assetsRoot, req.file.path);
|
|
11149
11204
|
res.json({
|
|
11150
11205
|
id: req.file.filename,
|
|
11151
11206
|
path: `./assets/${relativePath}`,
|
|
@@ -11171,7 +11226,7 @@ function createAssetsRouter(project) {
|
|
|
11171
11226
|
try {
|
|
11172
11227
|
const rel = req.params.assetPath?.join("/") ?? "";
|
|
11173
11228
|
const full = resolveWithinRoot(assetsRoot, rel);
|
|
11174
|
-
await
|
|
11229
|
+
await fs15.unlink(full);
|
|
11175
11230
|
res.json({ ok: true });
|
|
11176
11231
|
} catch (err) {
|
|
11177
11232
|
handleError2(res, err);
|
|
@@ -11187,10 +11242,10 @@ var init_assets = __esm({
|
|
|
11187
11242
|
});
|
|
11188
11243
|
|
|
11189
11244
|
// server/src/nodeWatch.ts
|
|
11190
|
-
import
|
|
11191
|
-
import
|
|
11245
|
+
import fs16 from "node:fs";
|
|
11246
|
+
import path19 from "node:path";
|
|
11192
11247
|
function moduleNameFor(relative) {
|
|
11193
|
-
const segments = relative.split(
|
|
11248
|
+
const segments = relative.split(path19.sep).filter(Boolean);
|
|
11194
11249
|
if (segments.length < 2) return null;
|
|
11195
11250
|
const name = segments[0];
|
|
11196
11251
|
if (!name || name.startsWith(".")) return null;
|
|
@@ -11212,9 +11267,9 @@ function createNodeWatcher(project) {
|
|
|
11212
11267
|
);
|
|
11213
11268
|
}
|
|
11214
11269
|
function watch(directory, scope) {
|
|
11215
|
-
if (!
|
|
11270
|
+
if (!fs16.existsSync(directory)) return;
|
|
11216
11271
|
try {
|
|
11217
|
-
const watcher =
|
|
11272
|
+
const watcher = fs16.watch(directory, { recursive: true }, (_event, filename) => {
|
|
11218
11273
|
if (!filename) return;
|
|
11219
11274
|
if (scope === "shared") {
|
|
11220
11275
|
emit(ALL_MODULES);
|
|
@@ -11231,8 +11286,8 @@ function createNodeWatcher(project) {
|
|
|
11231
11286
|
}
|
|
11232
11287
|
watch(project.resolve("nodes"), "project");
|
|
11233
11288
|
try {
|
|
11234
|
-
const sharedNodes =
|
|
11235
|
-
watch(
|
|
11289
|
+
const sharedNodes = fs16.realpathSync(path19.join(project.root, "shared"));
|
|
11290
|
+
watch(path19.join(sharedNodes, "nodes"), "shared");
|
|
11236
11291
|
} catch {
|
|
11237
11292
|
}
|
|
11238
11293
|
return {
|
|
@@ -11902,15 +11957,15 @@ var init_eventBuffer = __esm({
|
|
|
11902
11957
|
});
|
|
11903
11958
|
|
|
11904
11959
|
// server/src/agent/allowlist.ts
|
|
11905
|
-
import
|
|
11960
|
+
import fs17 from "node:fs";
|
|
11906
11961
|
import os4 from "node:os";
|
|
11907
|
-
import
|
|
11962
|
+
import path20 from "node:path";
|
|
11908
11963
|
function allowlistHint(root, alias) {
|
|
11909
11964
|
return `Add this under "commands" in ${root}/cascade.json: "${alias}": "<path>"`;
|
|
11910
11965
|
}
|
|
11911
11966
|
function readManifest(root) {
|
|
11912
11967
|
try {
|
|
11913
|
-
return JSON.parse(
|
|
11968
|
+
return JSON.parse(fs17.readFileSync(path20.join(root, "cascade.json"), "utf8"));
|
|
11914
11969
|
} catch {
|
|
11915
11970
|
return {};
|
|
11916
11971
|
}
|
|
@@ -11930,8 +11985,8 @@ function agentExtraArgs(root, alias) {
|
|
|
11930
11985
|
}
|
|
11931
11986
|
function executableFile2(candidate) {
|
|
11932
11987
|
try {
|
|
11933
|
-
|
|
11934
|
-
return
|
|
11988
|
+
fs17.accessSync(candidate, fs17.constants.X_OK);
|
|
11989
|
+
return fs17.statSync(candidate).isFile() ? fs17.realpathSync(candidate) : null;
|
|
11935
11990
|
} catch {
|
|
11936
11991
|
return null;
|
|
11937
11992
|
}
|
|
@@ -11939,10 +11994,10 @@ function executableFile2(candidate) {
|
|
|
11939
11994
|
function resolveExecutable2(target) {
|
|
11940
11995
|
if (typeof target !== "string" || !target || target.includes("\0")) return null;
|
|
11941
11996
|
const expanded = target.replace(/^~(?=\/|$)/, os4.homedir());
|
|
11942
|
-
if (
|
|
11997
|
+
if (path20.isAbsolute(expanded)) return executableFile2(expanded);
|
|
11943
11998
|
if (!/[\\/]/.test(expanded)) {
|
|
11944
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
11945
|
-
const found = executableFile2(
|
|
11999
|
+
for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
|
|
12000
|
+
const found = executableFile2(path20.join(dir, expanded));
|
|
11946
12001
|
if (found) return found;
|
|
11947
12002
|
}
|
|
11948
12003
|
return null;
|
|
@@ -12393,7 +12448,7 @@ var init_agent = __esm({
|
|
|
12393
12448
|
});
|
|
12394
12449
|
|
|
12395
12450
|
// server/src/graphWatch.ts
|
|
12396
|
-
import
|
|
12451
|
+
import fs18 from "node:fs";
|
|
12397
12452
|
function graphFileFor(relative) {
|
|
12398
12453
|
const segments = String(relative).split(/[\\/]/).filter(Boolean);
|
|
12399
12454
|
if (segments.length !== 1) return null;
|
|
@@ -12418,7 +12473,7 @@ function createGraphWatcher(project) {
|
|
|
12418
12473
|
);
|
|
12419
12474
|
}
|
|
12420
12475
|
try {
|
|
12421
|
-
watcher =
|
|
12476
|
+
watcher = fs18.watch(project.root, { recursive: false }, (_event, filename) => {
|
|
12422
12477
|
if (!filename) return;
|
|
12423
12478
|
const graph = graphFileFor(String(filename));
|
|
12424
12479
|
if (graph) emit(graph);
|
|
@@ -12604,8 +12659,8 @@ var init_net = __esm({
|
|
|
12604
12659
|
// server/src/routes/media.ts
|
|
12605
12660
|
import express, { Router as Router11 } from "express";
|
|
12606
12661
|
import crypto2 from "crypto";
|
|
12607
|
-
import
|
|
12608
|
-
import
|
|
12662
|
+
import fs19 from "fs";
|
|
12663
|
+
import path21 from "path";
|
|
12609
12664
|
async function loadSharp() {
|
|
12610
12665
|
if (sharpModule) return sharpModule;
|
|
12611
12666
|
if (sharpError) throw new Error(sharpError);
|
|
@@ -12692,11 +12747,11 @@ function fieldToGrey(array) {
|
|
|
12692
12747
|
}
|
|
12693
12748
|
function contentAddressed2(rel, body) {
|
|
12694
12749
|
const hash2 = crypto2.createHash("sha1").update(body).digest("hex").slice(0, 12);
|
|
12695
|
-
const dir =
|
|
12696
|
-
const name =
|
|
12750
|
+
const dir = path21.posix.dirname(rel);
|
|
12751
|
+
const name = path21.posix.basename(rel);
|
|
12697
12752
|
const [stem, ...rest] = name.split(".");
|
|
12698
12753
|
const tail = rest.length ? `.${rest.join(".")}` : "";
|
|
12699
|
-
return
|
|
12754
|
+
return path21.posix.join(dir, `${stem}.${hash2}${tail}`);
|
|
12700
12755
|
}
|
|
12701
12756
|
function clampInt(value, min, max) {
|
|
12702
12757
|
const n = Number(value);
|
|
@@ -12718,8 +12773,8 @@ function createMediaRouter(project) {
|
|
|
12718
12773
|
try {
|
|
12719
12774
|
const target = req.query.exact === "1" ? rel : contentAddressed2(rel, req.body);
|
|
12720
12775
|
const full = project.resolve(target);
|
|
12721
|
-
await
|
|
12722
|
-
await
|
|
12776
|
+
await fs19.promises.mkdir(path21.dirname(full), { recursive: true });
|
|
12777
|
+
await fs19.promises.writeFile(full, req.body);
|
|
12723
12778
|
res.json({ ok: true, path: target, bytes: req.body.length });
|
|
12724
12779
|
} catch (err) {
|
|
12725
12780
|
res.status(err instanceof PathSafetyError ? 400 : 500).json({ error: String(err) });
|
|
@@ -12736,13 +12791,13 @@ function createMediaRouter(project) {
|
|
|
12736
12791
|
}
|
|
12737
12792
|
let stat;
|
|
12738
12793
|
try {
|
|
12739
|
-
stat = await
|
|
12794
|
+
stat = await fs19.promises.stat(full);
|
|
12740
12795
|
if (!stat.isFile()) throw new Error("not a file");
|
|
12741
12796
|
} catch {
|
|
12742
12797
|
res.status(404).json({ error: `no such file: ${rel}` });
|
|
12743
12798
|
return;
|
|
12744
12799
|
}
|
|
12745
|
-
const ext =
|
|
12800
|
+
const ext = path21.extname(full).toLowerCase();
|
|
12746
12801
|
const width = clampInt(req.query.w, 1, 2e4);
|
|
12747
12802
|
const quality = clampInt(req.query.q, 1, 100) ?? 82;
|
|
12748
12803
|
const requested = String(req.query.fmt ?? "").toLowerCase();
|
|
@@ -12756,7 +12811,7 @@ function createMediaRouter(project) {
|
|
|
12756
12811
|
}
|
|
12757
12812
|
if (!raw && ext === ".npy") {
|
|
12758
12813
|
try {
|
|
12759
|
-
const array = readNpy(await
|
|
12814
|
+
const array = readNpy(await fs19.promises.readFile(full));
|
|
12760
12815
|
const { pixels, min, max } = fieldToGrey(array);
|
|
12761
12816
|
res.setHeader("X-Field-Shape", `${array.height}x${array.width}`);
|
|
12762
12817
|
res.setHeader("X-Field-Range", `${min}:${max}`);
|
|
@@ -12787,7 +12842,7 @@ function createMediaRouter(project) {
|
|
|
12787
12842
|
if (raw || !RASTER.has(ext)) {
|
|
12788
12843
|
res.type(MIME[ext] ?? "application/octet-stream");
|
|
12789
12844
|
res.setHeader("Content-Length", String(stat.size));
|
|
12790
|
-
|
|
12845
|
+
fs19.createReadStream(full).pipe(res);
|
|
12791
12846
|
return;
|
|
12792
12847
|
}
|
|
12793
12848
|
let format2 = requested;
|
|
@@ -12795,7 +12850,7 @@ function createMediaRouter(project) {
|
|
|
12795
12850
|
if (format2 === "gif") {
|
|
12796
12851
|
res.type("image/gif");
|
|
12797
12852
|
res.setHeader("Content-Length", String(stat.size));
|
|
12798
|
-
|
|
12853
|
+
fs19.createReadStream(full).pipe(res);
|
|
12799
12854
|
return;
|
|
12800
12855
|
}
|
|
12801
12856
|
try {
|
|
@@ -12862,11 +12917,11 @@ var init_media = __esm({
|
|
|
12862
12917
|
});
|
|
12863
12918
|
|
|
12864
12919
|
// server/src/build.ts
|
|
12865
|
-
import
|
|
12920
|
+
import fs20 from "node:fs";
|
|
12866
12921
|
import { createHash } from "node:crypto";
|
|
12867
12922
|
function buildId(distDir) {
|
|
12868
12923
|
try {
|
|
12869
|
-
const contents =
|
|
12924
|
+
const contents = fs20.readFileSync(`${distDir}/index.html`);
|
|
12870
12925
|
return createHash("sha256").update(contents).digest("hex").slice(0, 16);
|
|
12871
12926
|
} catch {
|
|
12872
12927
|
return null;
|
|
@@ -12881,8 +12936,8 @@ var init_build = __esm({
|
|
|
12881
12936
|
// server/src/index.ts
|
|
12882
12937
|
import express2 from "express";
|
|
12883
12938
|
import { createServer } from "http";
|
|
12884
|
-
import
|
|
12885
|
-
import
|
|
12939
|
+
import path22 from "path";
|
|
12940
|
+
import fs21 from "fs";
|
|
12886
12941
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
12887
12942
|
function startServer(project, opts = {}) {
|
|
12888
12943
|
const app = express2();
|
|
@@ -12949,7 +13004,7 @@ function startServer(project, opts = {}) {
|
|
|
12949
13004
|
app.get("/api/build", (_req, res) => {
|
|
12950
13005
|
res.json({ id: buildId(CASCADE_DIST) });
|
|
12951
13006
|
});
|
|
12952
|
-
if (
|
|
13007
|
+
if (fs21.existsSync(CASCADE_DIST)) {
|
|
12953
13008
|
app.use(express2.static(CASCADE_DIST, {
|
|
12954
13009
|
setHeaders(res, filePath) {
|
|
12955
13010
|
res.setHeader(
|
|
@@ -12961,7 +13016,7 @@ function startServer(project, opts = {}) {
|
|
|
12961
13016
|
app.get("/{*splat}", (req, res, next) => {
|
|
12962
13017
|
if (req.path.startsWith("/api") || req.path === "/health") return next();
|
|
12963
13018
|
res.setHeader("Cache-Control", "no-store");
|
|
12964
|
-
res.sendFile(
|
|
13019
|
+
res.sendFile(path22.join(CASCADE_DIST, "index.html"));
|
|
12965
13020
|
});
|
|
12966
13021
|
} else {
|
|
12967
13022
|
console.warn(`[cascade-server] no build found at ${CASCADE_DIST} \u2014 run "npm run build" in the Cascade repo first. API-only for now.`);
|
|
@@ -12994,18 +13049,18 @@ var init_src3 = __esm({
|
|
|
12994
13049
|
init_build();
|
|
12995
13050
|
init_service();
|
|
12996
13051
|
__filename = fileURLToPath3(import.meta.url);
|
|
12997
|
-
__dirname =
|
|
12998
|
-
CASCADE_DIST =
|
|
13052
|
+
__dirname = path22.dirname(__filename);
|
|
13053
|
+
CASCADE_DIST = path22.resolve(__dirname, "..", "..", "dist");
|
|
12999
13054
|
}
|
|
13000
13055
|
});
|
|
13001
13056
|
|
|
13002
13057
|
// server/src/projectTemplate.ts
|
|
13003
|
-
import
|
|
13004
|
-
import
|
|
13058
|
+
import fs22 from "node:fs";
|
|
13059
|
+
import path23 from "node:path";
|
|
13005
13060
|
function cascadeVersionRange() {
|
|
13006
13061
|
try {
|
|
13007
13062
|
const pkg = JSON.parse(
|
|
13008
|
-
|
|
13063
|
+
fs22.readFileSync(new URL("../../package.json", import.meta.url), "utf8")
|
|
13009
13064
|
);
|
|
13010
13065
|
const match = /^(\d+)\.(\d+)\./.exec(pkg.version ?? "");
|
|
13011
13066
|
return match ? `^${match[1]}.${match[2]}.0` : "*";
|
|
@@ -13017,14 +13072,14 @@ function createProject(projectsRoot, name) {
|
|
|
13017
13072
|
if (!PROJECT_NAME.test(name)) {
|
|
13018
13073
|
throw new Error("Project names use lowercase letters, numbers, and hyphens (maximum 64 characters).");
|
|
13019
13074
|
}
|
|
13020
|
-
const root =
|
|
13021
|
-
const directory =
|
|
13022
|
-
if (
|
|
13023
|
-
if (
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
writeJson(
|
|
13027
|
-
writeJson(
|
|
13075
|
+
const root = path23.resolve(projectsRoot);
|
|
13076
|
+
const directory = path23.resolve(root, name);
|
|
13077
|
+
if (path23.dirname(directory) !== root) throw new Error("Project path escapes the configured projects directory.");
|
|
13078
|
+
if (fs22.existsSync(directory)) throw new Error(`Already exists: ${directory}`);
|
|
13079
|
+
fs22.mkdirSync(path23.join(directory, "nodes"), { recursive: true });
|
|
13080
|
+
fs22.mkdirSync(path23.join(directory, "assets"));
|
|
13081
|
+
writeJson(path23.join(directory, "cascade.json"), { name });
|
|
13082
|
+
writeJson(path23.join(directory, "index.cascade"), {
|
|
13028
13083
|
name,
|
|
13029
13084
|
version: "0.2",
|
|
13030
13085
|
viewport: { x: 0, y: 0, zoom: 1 },
|
|
@@ -13032,7 +13087,7 @@ function createProject(projectsRoot, name) {
|
|
|
13032
13087
|
connections: [],
|
|
13033
13088
|
annotations: []
|
|
13034
13089
|
});
|
|
13035
|
-
writeJson(
|
|
13090
|
+
writeJson(path23.join(directory, "package.json"), {
|
|
13036
13091
|
name,
|
|
13037
13092
|
private: true,
|
|
13038
13093
|
type: "module",
|
|
@@ -13052,7 +13107,7 @@ function createProject(projectsRoot, name) {
|
|
|
13052
13107
|
typescript: "^6.0.0"
|
|
13053
13108
|
}
|
|
13054
13109
|
});
|
|
13055
|
-
writeJson(
|
|
13110
|
+
writeJson(path23.join(directory, "tsconfig.json"), {
|
|
13056
13111
|
compilerOptions: {
|
|
13057
13112
|
target: "ES2022",
|
|
13058
13113
|
module: "ESNext",
|
|
@@ -13062,8 +13117,8 @@ function createProject(projectsRoot, name) {
|
|
|
13062
13117
|
},
|
|
13063
13118
|
include: ["nodes/**/*.ts"]
|
|
13064
13119
|
});
|
|
13065
|
-
|
|
13066
|
-
|
|
13120
|
+
fs22.writeFileSync(path23.join(directory, ".gitignore"), ".cascade-cache/\nrenders/\nnode_modules/\n");
|
|
13121
|
+
fs22.writeFileSync(path23.join(directory, "AGENTS.md"), `# Cascade project
|
|
13067
13122
|
|
|
13068
13123
|
Read \`node_modules/cascade/AGENTS.md\` for the framework itself. What follows is what an agent working in *this* project needs.
|
|
13069
13124
|
|
|
@@ -13218,14 +13273,14 @@ function createNode(projectRoot, name) {
|
|
|
13218
13273
|
if (!NODE_NAME.test(name)) {
|
|
13219
13274
|
throw new Error("Node names start with an uppercase letter and contain only letters or numbers.");
|
|
13220
13275
|
}
|
|
13221
|
-
const directory =
|
|
13222
|
-
if (
|
|
13223
|
-
|
|
13224
|
-
|
|
13276
|
+
const directory = path23.resolve(projectRoot, "nodes", name);
|
|
13277
|
+
if (fs22.existsSync(directory)) throw new Error(`Already exists: ${directory}`);
|
|
13278
|
+
fs22.mkdirSync(directory, { recursive: true });
|
|
13279
|
+
fs22.writeFileSync(path23.join(directory, "index.ts"), nodeSource(name));
|
|
13225
13280
|
return directory;
|
|
13226
13281
|
}
|
|
13227
13282
|
function writeJson(file, value) {
|
|
13228
|
-
|
|
13283
|
+
fs22.writeFileSync(file, `${JSON.stringify(value, null, 2)}
|
|
13229
13284
|
`);
|
|
13230
13285
|
}
|
|
13231
13286
|
function nodeSource(name) {
|
|
@@ -13263,8 +13318,8 @@ __export(cliCommands_exports, {
|
|
|
13263
13318
|
parseStudioArgs: () => parseStudioArgs,
|
|
13264
13319
|
runStudioCli: () => runStudioCli
|
|
13265
13320
|
});
|
|
13266
|
-
import
|
|
13267
|
-
import
|
|
13321
|
+
import fs23 from "node:fs";
|
|
13322
|
+
import path24 from "node:path";
|
|
13268
13323
|
import { execFile as execFile5 } from "node:child_process";
|
|
13269
13324
|
function parseStudioArgs(args) {
|
|
13270
13325
|
const positional = [];
|
|
@@ -13319,7 +13374,7 @@ async function runStudioCli(args) {
|
|
|
13319
13374
|
const config = loadConfig2();
|
|
13320
13375
|
if (positional[0] === "projects") {
|
|
13321
13376
|
if (positional[1]) {
|
|
13322
|
-
saveConfig({ projectsRoot:
|
|
13377
|
+
saveConfig({ projectsRoot: path24.resolve(positional[1].replace(/^~(?=\/|$)/, process.env.HOME ?? "~")) });
|
|
13323
13378
|
console.log(`[cascade] projects root set to ${loadConfig2().projectsRoot} (${configPath()})`);
|
|
13324
13379
|
} else console.log(loadConfig2().projectsRoot);
|
|
13325
13380
|
return;
|
|
@@ -13338,9 +13393,9 @@ async function runStudioCli(args) {
|
|
|
13338
13393
|
return;
|
|
13339
13394
|
}
|
|
13340
13395
|
let target = positional[0] ?? ".";
|
|
13341
|
-
if (!
|
|
13342
|
-
const projectTarget =
|
|
13343
|
-
if (
|
|
13396
|
+
if (!fs23.existsSync(path24.resolve(target))) {
|
|
13397
|
+
const projectTarget = path24.resolve(config.projectsRoot, target);
|
|
13398
|
+
if (fs23.existsSync(projectTarget)) target = projectTarget;
|
|
13344
13399
|
}
|
|
13345
13400
|
const project = ProjectRoot.fromArg(target);
|
|
13346
13401
|
const server = startServer(project, { host, port, trustedHosts });
|
|
@@ -13486,8 +13541,8 @@ var ExpressionEngine = class extends ExpressionEvaluator {
|
|
|
13486
13541
|
super();
|
|
13487
13542
|
this.graph = null;
|
|
13488
13543
|
const scope = {
|
|
13489
|
-
resolveParam: (from,
|
|
13490
|
-
nodeExists: (from,
|
|
13544
|
+
resolveParam: (from, path25) => this.resolveNodePath(from, path25),
|
|
13545
|
+
nodeExists: (from, path25) => from.node(path25) !== null,
|
|
13491
13546
|
inputNode: (from, index) => this.resolveInput(from, index)
|
|
13492
13547
|
};
|
|
13493
13548
|
this.setScope(scope);
|
|
@@ -13501,15 +13556,15 @@ var ExpressionEngine = class extends ExpressionEvaluator {
|
|
|
13501
13556
|
/**
|
|
13502
13557
|
* Resolve a path to get the parameter value
|
|
13503
13558
|
*/
|
|
13504
|
-
resolveNodePath(fromNode,
|
|
13505
|
-
const lastSlash =
|
|
13506
|
-
if (lastSlash === -1 ||
|
|
13507
|
-
const paramName2 =
|
|
13559
|
+
resolveNodePath(fromNode, path25) {
|
|
13560
|
+
const lastSlash = path25.lastIndexOf("/");
|
|
13561
|
+
if (lastSlash === -1 || path25.startsWith("./")) {
|
|
13562
|
+
const paramName2 = path25.replace("./", "");
|
|
13508
13563
|
const prop2 = fromNode.props[paramName2];
|
|
13509
13564
|
return prop2 ? { value: prop2.value } : null;
|
|
13510
13565
|
}
|
|
13511
|
-
const nodePath =
|
|
13512
|
-
const paramName =
|
|
13566
|
+
const nodePath = path25.substring(0, lastSlash) || ".";
|
|
13567
|
+
const paramName = path25.substring(lastSlash + 1);
|
|
13513
13568
|
const targetNode = fromNode.node(nodePath);
|
|
13514
13569
|
if (!targetNode) return null;
|
|
13515
13570
|
const prop = targetNode.props[paramName];
|
|
@@ -13536,14 +13591,14 @@ var ExpressionEngine = class extends ExpressionEvaluator {
|
|
|
13536
13591
|
for (const [propKey, prop] of Object.entries(node.props)) {
|
|
13537
13592
|
if (!prop.expression) continue;
|
|
13538
13593
|
const paths = this.extractDependencies(prop.expression);
|
|
13539
|
-
for (const
|
|
13540
|
-
if (!this.validatePath(node,
|
|
13594
|
+
for (const path25 of paths) {
|
|
13595
|
+
if (!this.validatePath(node, path25)) {
|
|
13541
13596
|
warnings.push({
|
|
13542
13597
|
nodeId: node.id,
|
|
13543
13598
|
nodePath: node.path(),
|
|
13544
13599
|
propName: propKey,
|
|
13545
13600
|
expression: prop.expression,
|
|
13546
|
-
brokenPath:
|
|
13601
|
+
brokenPath: path25
|
|
13547
13602
|
});
|
|
13548
13603
|
}
|
|
13549
13604
|
}
|
|
@@ -13584,24 +13639,24 @@ var CascadeContext = class {
|
|
|
13584
13639
|
/**
|
|
13585
13640
|
* Find a node by path (absolute or relative to pwd)
|
|
13586
13641
|
*/
|
|
13587
|
-
node(
|
|
13642
|
+
node(path25) {
|
|
13588
13643
|
if (!this._graph) return null;
|
|
13589
|
-
if (
|
|
13590
|
-
return this.resolveAbsolutePath(
|
|
13644
|
+
if (path25.startsWith("/")) {
|
|
13645
|
+
return this.resolveAbsolutePath(path25);
|
|
13591
13646
|
}
|
|
13592
13647
|
if (this._pwd) {
|
|
13593
|
-
return this._pwd.node(
|
|
13648
|
+
return this._pwd.node(path25);
|
|
13594
13649
|
}
|
|
13595
|
-
return this.resolveAbsolutePath("/" +
|
|
13650
|
+
return this.resolveAbsolutePath("/" + path25);
|
|
13596
13651
|
}
|
|
13597
13652
|
/**
|
|
13598
13653
|
* Find a parameter by path (e.g., "/node1/radius")
|
|
13599
13654
|
*/
|
|
13600
|
-
parm(
|
|
13601
|
-
const lastSlash =
|
|
13655
|
+
parm(path25) {
|
|
13656
|
+
const lastSlash = path25.lastIndexOf("/");
|
|
13602
13657
|
if (lastSlash === -1) return null;
|
|
13603
|
-
const nodePath =
|
|
13604
|
-
const parmName =
|
|
13658
|
+
const nodePath = path25.substring(0, lastSlash) || "/";
|
|
13659
|
+
const parmName = path25.substring(lastSlash + 1);
|
|
13605
13660
|
const node = this.node(nodePath);
|
|
13606
13661
|
return node?.parm(parmName) ?? null;
|
|
13607
13662
|
}
|
|
@@ -13627,12 +13682,12 @@ var CascadeContext = class {
|
|
|
13627
13682
|
/**
|
|
13628
13683
|
* Navigate to a path (like cd command)
|
|
13629
13684
|
*/
|
|
13630
|
-
cd(
|
|
13631
|
-
if (
|
|
13685
|
+
cd(path25) {
|
|
13686
|
+
if (path25 === "/") {
|
|
13632
13687
|
this._pwd = null;
|
|
13633
13688
|
return;
|
|
13634
13689
|
}
|
|
13635
|
-
const target = this.node(
|
|
13690
|
+
const target = this.node(path25);
|
|
13636
13691
|
if (target && target.isNetwork()) {
|
|
13637
13692
|
this._pwd = target;
|
|
13638
13693
|
}
|
|
@@ -13730,22 +13785,22 @@ var CascadeContext = class {
|
|
|
13730
13785
|
/**
|
|
13731
13786
|
* Get parameter value as number (shorthand)
|
|
13732
13787
|
*/
|
|
13733
|
-
ch(
|
|
13734
|
-
const p = this.parm(
|
|
13788
|
+
ch(path25) {
|
|
13789
|
+
const p = this.parm(path25);
|
|
13735
13790
|
return p?.evalAsFloat() ?? 0;
|
|
13736
13791
|
}
|
|
13737
13792
|
/**
|
|
13738
13793
|
* Get parameter value as string (shorthand)
|
|
13739
13794
|
*/
|
|
13740
|
-
chs(
|
|
13741
|
-
const p = this.parm(
|
|
13795
|
+
chs(path25) {
|
|
13796
|
+
const p = this.parm(path25);
|
|
13742
13797
|
return p?.evalAsString() ?? "";
|
|
13743
13798
|
}
|
|
13744
13799
|
/**
|
|
13745
13800
|
* Get parameter value as vector (shorthand)
|
|
13746
13801
|
*/
|
|
13747
|
-
chv(
|
|
13748
|
-
const p = this.parm(
|
|
13802
|
+
chv(path25) {
|
|
13803
|
+
const p = this.parm(path25);
|
|
13749
13804
|
const val = p?.eval();
|
|
13750
13805
|
if (Array.isArray(val)) return val;
|
|
13751
13806
|
if (typeof val === "number") return [val];
|
|
@@ -13782,9 +13837,9 @@ var CascadeContext = class {
|
|
|
13782
13837
|
return this.fframe();
|
|
13783
13838
|
}
|
|
13784
13839
|
// ============ Internal Helpers ============
|
|
13785
|
-
resolveAbsolutePath(
|
|
13840
|
+
resolveAbsolutePath(path25) {
|
|
13786
13841
|
if (!this._graph) return null;
|
|
13787
|
-
const segments =
|
|
13842
|
+
const segments = path25.replace(/^\//, "").split("/").filter((s) => s.length > 0);
|
|
13788
13843
|
if (segments.length === 0) {
|
|
13789
13844
|
return null;
|
|
13790
13845
|
}
|
|
@@ -13820,7 +13875,7 @@ function parseFrameSpec(spec) {
|
|
|
13820
13875
|
|
|
13821
13876
|
// src/cli/runner.ts
|
|
13822
13877
|
async function runGraph(options2) {
|
|
13823
|
-
const { file, entryNode, validateOnly, checkOnly, inspectOnly, strict, verbose, frames, fps, out } = options2;
|
|
13878
|
+
const { file, entryNode, nodes, validateOnly, checkOnly, inspectOnly, strict, verbose, frames, fps, out } = options2;
|
|
13824
13879
|
if (verbose) {
|
|
13825
13880
|
console.log(`Loading graph from: ${file}`);
|
|
13826
13881
|
}
|
|
@@ -13914,6 +13969,7 @@ async function runGraph(options2) {
|
|
|
13914
13969
|
...fps === void 0 ? {} : { fps },
|
|
13915
13970
|
out: out ?? "renders",
|
|
13916
13971
|
...entryNode ? { entryNode } : {},
|
|
13972
|
+
...nodes?.length ? { nodes } : {},
|
|
13917
13973
|
...verbose ? { verbose } : {}
|
|
13918
13974
|
});
|
|
13919
13975
|
if (rendered) {
|
|
@@ -13923,7 +13979,7 @@ async function runGraph(options2) {
|
|
|
13923
13979
|
return;
|
|
13924
13980
|
}
|
|
13925
13981
|
if (options2.json) throw new Error("--json rendering requires a fully definition-v1 graph");
|
|
13926
|
-
} else if (await runDeterministicProjectGraph(file, graphData, entryNode)) {
|
|
13982
|
+
} else if (await runDeterministicProjectGraph(file, graphData, nodes?.[0] ?? entryNode)) {
|
|
13927
13983
|
if (verbose) console.log("Graph execution completed");
|
|
13928
13984
|
releaseHost();
|
|
13929
13985
|
return;
|
|
@@ -13939,7 +13995,7 @@ async function runGraph(options2) {
|
|
|
13939
13995
|
}
|
|
13940
13996
|
|
|
13941
13997
|
// src/cli/index.ts
|
|
13942
|
-
import * as
|
|
13998
|
+
import * as fs24 from "fs/promises";
|
|
13943
13999
|
import { readFileSync } from "node:fs";
|
|
13944
14000
|
|
|
13945
14001
|
// src/cli/supervise.ts
|
|
@@ -14056,7 +14112,8 @@ Options:
|
|
|
14056
14112
|
--port <number> Bind Studio HTTP to an explicit port
|
|
14057
14113
|
--trusted-host <name> Allow an exact remote browser hostname (repeatable)
|
|
14058
14114
|
--no-open Do not open Studio in a browser
|
|
14059
|
-
--
|
|
14115
|
+
--node <id> Render only this node's output. Repeatable.
|
|
14116
|
+
--entry-node <id> Older spelling of --node, single valued
|
|
14060
14117
|
--frames <range> Render a frame sequence: 1-100, 1-100x2 (step), or 42
|
|
14061
14118
|
--fps <number> Frame rate to evaluate the range at
|
|
14062
14119
|
--out <dir> Sequence directory, project-relative (default: renders)
|
|
@@ -14098,7 +14155,7 @@ image output nothing downstream consumes, or per output of --entry-node.
|
|
|
14098
14155
|
process.exit(1);
|
|
14099
14156
|
}
|
|
14100
14157
|
try {
|
|
14101
|
-
await
|
|
14158
|
+
await fs24.access(fileArg);
|
|
14102
14159
|
} catch {
|
|
14103
14160
|
console.error(`Error: File not found: ${fileArg}`);
|
|
14104
14161
|
process.exit(1);
|
|
@@ -14114,6 +14171,11 @@ image output nothing downstream consumes, or per output of --entry-node.
|
|
|
14114
14171
|
if (verboseIndex !== -1) {
|
|
14115
14172
|
options2.verbose = true;
|
|
14116
14173
|
}
|
|
14174
|
+
const named = [];
|
|
14175
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
14176
|
+
if (args[index] === "--node" && args[index + 1]) named.push(args[index + 1]);
|
|
14177
|
+
}
|
|
14178
|
+
if (named.length) options2.nodes = named;
|
|
14117
14179
|
const entryNodeIndex = args.indexOf("--entry-node");
|
|
14118
14180
|
if (entryNodeIndex !== -1 && args[entryNodeIndex + 1]) {
|
|
14119
14181
|
options2.entryNode = args[entryNodeIndex + 1];
|