@field/cascade 0.10.0 → 0.11.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/AGENTS.md +20 -3
- package/CHANGELOG.md +187 -0
- package/README.md +1 -2
- package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-4O7FQ__R.js} +4 -4
- package/dist/assets/{CodePanel-sY6sZa7B.js → CodePanel-BU-autmq.js} +2 -2
- package/dist/assets/{CustomNodeDialog-BRKnqZhp.js → CustomNodeDialog-BRpDfOV5.js} +1 -1
- package/dist/assets/{OpenGraphDialog-ZsgL-iwr.js → OpenGraphDialog-BwbRlC0X.js} +1 -1
- package/dist/assets/{ProjectSettings-Cw17nNe3.js → ProjectSettings-DLOXLxcA.js} +1 -1
- package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-Bz8JxISH.js} +1 -1
- package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-D5Z0nJ5-.js} +1 -1
- package/dist/assets/{core-BICrEt0y.js → core-Yqi0Ni61.js} +1 -1
- package/dist/assets/{cssMode-D7TXw57I.js → cssMode-CxUzc4vp.js} +1 -1
- package/dist/assets/{definitions-DUww2Rt-.js → definitions-GreEMXLV.js} +1 -1
- package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-BNFb25CX.js} +1 -1
- package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-0ZDnWxeM.js} +1 -1
- package/dist/assets/geo-u0n_jOsD.js +1 -0
- package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-Cy18MoaD.js} +1 -1
- package/dist/assets/{html-C7O4TyhX.js → html-B-2Dx7nq.js} +1 -1
- package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUAc9B_d.js} +1 -1
- package/dist/assets/{image-DR4oeyJB.js → image-cAa-Nu9O.js} +1 -1
- package/dist/assets/{index-DyvDC6pS.js → index-5ZtDAuy2.js} +42 -41
- package/dist/assets/{javascript-CpIEK7tz.js → javascript-DeQsDqP5.js} +1 -1
- package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-BiFwJVSk.js} +1 -1
- package/dist/assets/{liquid-CN6FFHZE.js → liquid-BLoHH68a.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-CNFgm1Kv.js} +1 -1
- package/dist/assets/math-DioIQOPJ.js +1 -0
- package/dist/assets/{mdx-DE89pafV.js → mdx-CFXQVNfs.js} +1 -1
- package/dist/assets/pop-DC_TQG9t.js +1 -0
- package/dist/assets/{python-ByTllTB9.js → python-Cf7S7p2E.js} +1 -1
- package/dist/assets/{razor-USYlRRCE.js → razor-CMHBcG-g.js} +1 -1
- package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BEih0GDj.js} +1 -1
- package/dist/assets/{typescript-DInBjGQQ.js → typescript-COEKYD_a.js} +1 -1
- package/dist/assets/{workers-C7Ye5B7D.js → workers-BNhAhMNn.js} +1 -1
- package/dist/assets/{xml-U0Uocqyo.js → xml-CPiMpnPl.js} +1 -1
- package/dist/assets/{yaml-CBw_PVYf.js → yaml-Bgp1ITLR.js} +1 -1
- package/dist/cli/Graph-ZJJTRQTM.js +2975 -0
- package/dist/cli/chunk-2KXB47FP.js +115 -0
- package/dist/cli/chunk-35D343DC.js +1447 -0
- package/dist/cli/chunk-36P5K3SD.js +631 -0
- package/dist/cli/chunk-3JTJHF7V.js +267 -0
- package/dist/cli/chunk-4UDFYF5G.js +132 -0
- package/dist/cli/chunk-53NMBJAO.js +35 -0
- package/dist/cli/chunk-A4OAMOZH.js +310 -0
- package/dist/cli/chunk-BIJO5OFS.js +71 -0
- package/dist/cli/chunk-BP7UKF36.js +376 -0
- package/dist/cli/chunk-CKS2FQWR.js +89 -0
- package/dist/cli/chunk-CO22XHNG.js +593 -0
- package/dist/cli/chunk-D6BUOHUG.js +634 -0
- package/dist/cli/chunk-DEQSEFFH.js +153 -0
- package/dist/cli/chunk-DUIUFVU2.js +241 -0
- package/dist/cli/chunk-EUIAC3U3.js +3006 -0
- package/dist/cli/chunk-FCDXJ6EK.js +2208 -0
- package/dist/cli/chunk-GFS6W3C5.js +308 -0
- package/dist/cli/chunk-HFHWYL55.js +139 -0
- package/dist/cli/chunk-HSNOBIVT.js +263 -0
- package/dist/cli/chunk-ICH7N5PN.js +26 -0
- package/dist/cli/chunk-J7VK7MNF.js +418 -0
- package/dist/cli/chunk-KH4MUBKF.js +1551 -0
- package/dist/cli/chunk-MIIEFUGG.js +623 -0
- package/dist/cli/chunk-OWWBYLHC.js +136 -0
- package/dist/cli/chunk-POMMNKZZ.js +672 -0
- package/dist/cli/chunk-QBBKRNGY.js +90 -0
- package/dist/cli/chunk-QQPA2ZHH.js +273 -0
- package/dist/cli/chunk-RLCAJBVR.js +224 -0
- package/dist/cli/chunk-RQZ4SX5R.js +1125 -0
- package/dist/cli/chunk-RUU3YWXU.js +99 -0
- package/dist/cli/chunk-RXDIMXN5.js +98 -0
- package/dist/cli/chunk-SGJAET2L.js +197 -0
- package/dist/cli/chunk-VVGSLVSF.js +151 -0
- package/dist/cli/chunk-WD62JIP6.js +118 -0
- package/dist/cli/chunk-WIH4TEIS.js +104 -0
- package/dist/cli/chunk-WKURDVYP.js +1119 -0
- package/dist/cli/chunk-WPUDGEFY.js +20 -0
- package/dist/cli/chunk-XWDSKG7N.js +1301 -0
- package/dist/cli/chunk-YAT5Y25U.js +121 -0
- package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
- package/dist/cli/cliCommands-UQSFGEHF.js +3464 -0
- package/dist/cli/commands-Q23M26QM.js +1292 -0
- package/dist/cli/definitions-HF2FTHYU.js +32 -0
- package/dist/cli/doctorCommand-B7BPQMN6.js +76 -0
- package/dist/cli/edit-JHCPTQFT.js +28 -0
- package/dist/cli/editorCommands-UIF5YLUZ.js +33 -0
- package/dist/cli/encode-HAUZTYPT.js +17 -0
- package/dist/cli/frames-LSAUBZ7P.js +16 -0
- package/dist/cli/geo-4RNK3QVN.js +43 -0
- package/dist/cli/graphCommands-6BMAKGQE.js +184 -0
- package/dist/cli/graphFile-VPSQ6OF7.js +286 -0
- package/dist/cli/headlessCanvas-5XLDZT7M.js +15 -0
- package/dist/cli/helpCommand-3VRE3KVO.js +155 -0
- package/dist/cli/index.js +100 -30756
- package/dist/cli/math-ZRMLKU2D.js +16 -0
- package/dist/cli/mcp-ZLEEMYGF.js +164 -0
- package/dist/cli/parity-KINARCP2.js +328 -0
- package/dist/cli/pop-USDFXB7H.js +39 -0
- package/dist/cli/project-K3QIPGBB.js +12 -0
- package/dist/cli/projectDefinition-DMTZN2NE.js +27 -0
- package/dist/cli/projectRuntime-Z5PJOH47.js +48 -0
- package/dist/cli/registerStandardNodes-S2VLMF6J.js +3081 -0
- package/dist/cli/render-YURAPPUI.js +436 -0
- package/dist/cli/runCommand-PI764LEG.js +198 -0
- package/dist/cli/runner-RA6UKEPD.js +37 -0
- package/dist/cli/runtime/image.ts +277 -0
- package/dist/cli/runtime/stage.ts +4 -1
- package/dist/cli/stageRunner-KUNIMQKJ.js +10 -0
- package/dist/cli/verbs-RCE3K4VQ.js +19 -0
- package/dist/cli/webBuild-GT4PZT5D.js +37 -0
- package/dist/contracts/documentFormat.d.ts +18 -0
- package/dist/contracts/documentFormat.js +195 -0
- package/dist/contracts/editor.d.ts +17 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/image.d.ts +141 -0
- package/dist/image.js +127 -0
- package/dist/index.html +1 -1
- package/dist/player/editor.js +32 -7
- package/dist/runtime/builtins/headless.d.ts +19 -0
- package/dist/runtime/builtins/headless.js +25 -0
- package/dist/runtime/builtins/image/contactSheet.d.ts +226 -0
- package/dist/runtime/builtins/image/contactSheet.js +199 -0
- package/dist/runtime/builtins/image/index.d.ts +9 -0
- package/dist/runtime/builtins/image/index.js +9 -0
- package/dist/stage.js +4 -1
- package/doc/CLI_REFERENCE.md +73 -2
- package/doc/EDITOR_EXPORT.md +11 -2
- package/doc/MCP.md +17 -1
- package/doc/NODE_REFERENCE.md +142 -0
- package/doc/PROJECT_AUTHORING.md +127 -7
- package/package.json +16 -4
- package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
- package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
- package/dist/assets/geo-C2RHOVHC.js +0 -1
- package/dist/assets/math-DaDEgWKt.js +0 -1
- package/dist/assets/pop-pLH7L0PW.js +0 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatCompactJSON
|
|
3
|
+
} from "./chunk-OWWBYLHC.js";
|
|
4
|
+
import {
|
|
5
|
+
writeFileAtomic
|
|
6
|
+
} from "./chunk-WPUDGEFY.js";
|
|
7
|
+
import {
|
|
8
|
+
builtinModuleIds,
|
|
9
|
+
diagnoseGraph,
|
|
10
|
+
moduleDefinitions
|
|
11
|
+
} from "./chunk-EUIAC3U3.js";
|
|
12
|
+
import "./chunk-36P5K3SD.js";
|
|
13
|
+
import "./chunk-D6BUOHUG.js";
|
|
14
|
+
import "./chunk-RXDIMXN5.js";
|
|
15
|
+
import "./chunk-QQPA2ZHH.js";
|
|
16
|
+
import "./chunk-KH4MUBKF.js";
|
|
17
|
+
import "./chunk-35D343DC.js";
|
|
18
|
+
import "./chunk-DEQSEFFH.js";
|
|
19
|
+
import "./chunk-HSNOBIVT.js";
|
|
20
|
+
import "./chunk-ICH7N5PN.js";
|
|
21
|
+
import "./chunk-GFS6W3C5.js";
|
|
22
|
+
import "./chunk-HFHWYL55.js";
|
|
23
|
+
import "./chunk-CO22XHNG.js";
|
|
24
|
+
import "./chunk-RQZ4SX5R.js";
|
|
25
|
+
import "./chunk-A4OAMOZH.js";
|
|
26
|
+
import "./chunk-J7VK7MNF.js";
|
|
27
|
+
import {
|
|
28
|
+
GraphEditError,
|
|
29
|
+
GraphEditor,
|
|
30
|
+
applyOperation
|
|
31
|
+
} from "./chunk-XWDSKG7N.js";
|
|
32
|
+
import "./chunk-BIJO5OFS.js";
|
|
33
|
+
|
|
34
|
+
// src/cli/graphFile.ts
|
|
35
|
+
import fs from "node:fs/promises";
|
|
36
|
+
import path from "node:path";
|
|
37
|
+
|
|
38
|
+
// src/cli/lineMerge.ts
|
|
39
|
+
function matchLines(before, after, limit = 2e3) {
|
|
40
|
+
const n = before.length;
|
|
41
|
+
const m = after.length;
|
|
42
|
+
const max = Math.min(n + m, limit);
|
|
43
|
+
const offset = max + 1;
|
|
44
|
+
const frontier = new Int32Array(2 * max + 3);
|
|
45
|
+
const trace = [];
|
|
46
|
+
let found = false;
|
|
47
|
+
for (let d = 0; d <= max && !found; d += 1) {
|
|
48
|
+
trace.push(frontier.slice());
|
|
49
|
+
for (let k = -d; k <= d; k += 2) {
|
|
50
|
+
let x2 = k === -d || k !== d && frontier[offset + k - 1] < frontier[offset + k + 1] ? frontier[offset + k + 1] : frontier[offset + k - 1] + 1;
|
|
51
|
+
let y2 = x2 - k;
|
|
52
|
+
while (x2 < n && y2 < m && before[x2] === after[y2]) {
|
|
53
|
+
x2 += 1;
|
|
54
|
+
y2 += 1;
|
|
55
|
+
}
|
|
56
|
+
frontier[offset + k] = x2;
|
|
57
|
+
if (x2 >= n && y2 >= m) {
|
|
58
|
+
found = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!found) return null;
|
|
64
|
+
const match = new Array(m).fill(-1);
|
|
65
|
+
let x = n;
|
|
66
|
+
let y = m;
|
|
67
|
+
for (let d = trace.length - 1; d >= 0 && (x > 0 || y > 0); d -= 1) {
|
|
68
|
+
const v = trace[d];
|
|
69
|
+
const k = x - y;
|
|
70
|
+
const down = k === -d || k !== d && v[offset + k - 1] < v[offset + k + 1];
|
|
71
|
+
const previousK = down ? k + 1 : k - 1;
|
|
72
|
+
const previousX = v[offset + previousK];
|
|
73
|
+
const previousY = previousX - previousK;
|
|
74
|
+
while (x > previousX && y > previousY) {
|
|
75
|
+
x -= 1;
|
|
76
|
+
y -= 1;
|
|
77
|
+
match[y] = x;
|
|
78
|
+
}
|
|
79
|
+
if (d > 0) {
|
|
80
|
+
x = previousX;
|
|
81
|
+
y = previousY;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return match;
|
|
85
|
+
}
|
|
86
|
+
function mergeLines(original, serializedBefore, after) {
|
|
87
|
+
let prefix = 0;
|
|
88
|
+
while (prefix < serializedBefore.length && prefix < after.length && serializedBefore[prefix] === after[prefix]) prefix += 1;
|
|
89
|
+
let suffix = 0;
|
|
90
|
+
while (suffix < serializedBefore.length - prefix && suffix < after.length - prefix && serializedBefore[serializedBefore.length - 1 - suffix] === after[after.length - 1 - suffix]) suffix += 1;
|
|
91
|
+
const middle = matchLines(
|
|
92
|
+
serializedBefore.slice(prefix, serializedBefore.length - suffix),
|
|
93
|
+
after.slice(prefix, after.length - suffix)
|
|
94
|
+
);
|
|
95
|
+
if (!middle) return null;
|
|
96
|
+
return after.map((line, index) => {
|
|
97
|
+
if (index < prefix) return original[index];
|
|
98
|
+
if (index >= after.length - suffix) return original[serializedBefore.length - (after.length - index)];
|
|
99
|
+
const matched = middle[index - prefix];
|
|
100
|
+
return matched >= 0 ? original[matched + prefix] : line;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/cli/graphFile.ts
|
|
105
|
+
var STYLES = [
|
|
106
|
+
{ name: "studio", asciiOnly: false, trailingNewline: false },
|
|
107
|
+
{ name: "studio", asciiOnly: false, trailingNewline: true },
|
|
108
|
+
{ name: "plain", asciiOnly: false, trailingNewline: false },
|
|
109
|
+
{ name: "plain", asciiOnly: false, trailingNewline: true },
|
|
110
|
+
{ name: "plain", asciiOnly: true, trailingNewline: false },
|
|
111
|
+
{ name: "plain", asciiOnly: true, trailingNewline: true },
|
|
112
|
+
{ name: "studio", asciiOnly: true, trailingNewline: false },
|
|
113
|
+
{ name: "studio", asciiOnly: true, trailingNewline: true }
|
|
114
|
+
];
|
|
115
|
+
function serializeGraph(document, style) {
|
|
116
|
+
let text = JSON.stringify(document, null, 2);
|
|
117
|
+
if (style.name === "studio") text = formatCompactJSON(text, 2);
|
|
118
|
+
if (style.asciiOnly) text = text.replace(/[\u007f-]/g, (char) => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
119
|
+
return style.trailingNewline ? `${text}
|
|
120
|
+
` : text;
|
|
121
|
+
}
|
|
122
|
+
function detectStyle(text, document) {
|
|
123
|
+
return STYLES.find((style) => serializeGraph(document, style) === text) ?? null;
|
|
124
|
+
}
|
|
125
|
+
function writeModeFor(text, document) {
|
|
126
|
+
const exact = detectStyle(text, document);
|
|
127
|
+
if (exact) return { kind: "exact", style: exact };
|
|
128
|
+
const lines = text.split("\n");
|
|
129
|
+
let best = null;
|
|
130
|
+
for (const style of STYLES) {
|
|
131
|
+
const candidate = serializeGraph(document, style).split("\n");
|
|
132
|
+
if (candidate.length !== lines.length) continue;
|
|
133
|
+
const differing = candidate.filter((line, index) => line !== lines[index]).length;
|
|
134
|
+
if (!best || differing < best.differing) best = { style, differing };
|
|
135
|
+
}
|
|
136
|
+
return best ? { kind: "aligned", style: best.style } : { kind: "rewrite", style: STYLES[1] };
|
|
137
|
+
}
|
|
138
|
+
var GraphFile = class _GraphFile {
|
|
139
|
+
constructor(file, original, document, mode) {
|
|
140
|
+
this.file = file;
|
|
141
|
+
this.original = original;
|
|
142
|
+
this.document = document;
|
|
143
|
+
this.mode = mode;
|
|
144
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
145
|
+
this.results = [];
|
|
146
|
+
this.warnings = [];
|
|
147
|
+
this.editor = new GraphEditor(document, { definitionOf: (id) => this.definitions.get(id) });
|
|
148
|
+
}
|
|
149
|
+
static async open(file) {
|
|
150
|
+
const resolved = path.resolve(file);
|
|
151
|
+
let text;
|
|
152
|
+
try {
|
|
153
|
+
text = await fs.readFile(resolved, "utf8");
|
|
154
|
+
} catch {
|
|
155
|
+
throw new GraphEditError(`No graph at ${resolved}; create one with "cascade graph create ${file}"`);
|
|
156
|
+
}
|
|
157
|
+
let document;
|
|
158
|
+
try {
|
|
159
|
+
document = JSON.parse(text);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new GraphEditError(`${resolved} is not valid JSON: ${error instanceof Error ? error.message : error}`);
|
|
162
|
+
}
|
|
163
|
+
const mode = writeModeFor(text, document);
|
|
164
|
+
const graph = new _GraphFile(resolved, text, document, mode);
|
|
165
|
+
if (mode.kind === "rewrite") {
|
|
166
|
+
graph.warnings.push(`${path.basename(resolved)} is not in a formatting the editor reproduces exactly; it will be rewritten in Studio's format on save`);
|
|
167
|
+
}
|
|
168
|
+
await graph.loadDefinitions(document.nodes.map((node) => String(node.module ?? node.type ?? "")));
|
|
169
|
+
return graph;
|
|
170
|
+
}
|
|
171
|
+
/** A new, empty graph file. Refuses to overwrite one that exists. */
|
|
172
|
+
static async create(file, options = {}) {
|
|
173
|
+
const resolved = path.resolve(file);
|
|
174
|
+
if (!resolved.endsWith(".cascade")) throw new GraphEditError(`A graph file ends in .cascade: ${file}`);
|
|
175
|
+
const document = {
|
|
176
|
+
version: "0.2",
|
|
177
|
+
metadata: { name: options.name ?? path.basename(resolved, ".cascade") },
|
|
178
|
+
nodes: []
|
|
179
|
+
};
|
|
180
|
+
await fs.mkdir(path.dirname(resolved), { recursive: true });
|
|
181
|
+
try {
|
|
182
|
+
await fs.writeFile(resolved, serializeGraph(document, STYLES[1]), { flag: "wx" });
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error.code === "EEXIST") throw new GraphEditError(`${resolved} already exists`);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
return resolved;
|
|
188
|
+
}
|
|
189
|
+
/** Apply one operation. Nothing is written until `save`. */
|
|
190
|
+
async apply(operation) {
|
|
191
|
+
if (operation.op === "node.add") {
|
|
192
|
+
const module = await this.resolveModule(operation.module);
|
|
193
|
+
operation = { ...operation, module };
|
|
194
|
+
}
|
|
195
|
+
const before = this.editor.warnings.length;
|
|
196
|
+
const result = { op: operation.op, ...applyOperation(this.editor, operation) };
|
|
197
|
+
this.warnings.push(...this.editor.warnings.slice(before));
|
|
198
|
+
this.results.push(result);
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
/** Apply a batch. All or nothing: a failing operation leaves the file untouched. */
|
|
202
|
+
async applyAll(operations, options = {}) {
|
|
203
|
+
for (const [index, operation] of operations.entries()) {
|
|
204
|
+
try {
|
|
205
|
+
await this.apply(operation);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
208
|
+
throw new GraphEditError(operations.length > 1 ? `Operation ${index + 1} (${operation.op}): ${message}` : message);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return this.save(options);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Validate and write. The edited graph is checked with the same code as
|
|
215
|
+
* `cascade check`, and refused if that finds a problem the original did not
|
|
216
|
+
* have — a graph that was already failing can still be repaired one edit at
|
|
217
|
+
* a time. The write is atomic, and refused if the file changed on disk since
|
|
218
|
+
* it was read (Studio saving, or another agent). `dryRun` validates and
|
|
219
|
+
* writes nothing.
|
|
220
|
+
*/
|
|
221
|
+
async save(options = {}) {
|
|
222
|
+
const text = this.serialize();
|
|
223
|
+
const changed = text !== this.original;
|
|
224
|
+
if (changed) {
|
|
225
|
+
const [before, after] = await Promise.all([
|
|
226
|
+
diagnoseGraph(this.file, JSON.parse(this.original)),
|
|
227
|
+
diagnoseGraph(this.file, this.document)
|
|
228
|
+
]);
|
|
229
|
+
const introduced = after.errors.filter((error) => !before.errors.includes(error));
|
|
230
|
+
if (introduced.length) {
|
|
231
|
+
throw new GraphEditError(`The edit was not saved; the graph would fail "cascade check":
|
|
232
|
+
${introduced.map((error) => ` ${error}`).join("\n")}`);
|
|
233
|
+
}
|
|
234
|
+
this.warnings.push(...after.warnings.filter((warning) => !before.warnings.includes(warning)));
|
|
235
|
+
if (!options.dryRun) await this.writeAtomically(text);
|
|
236
|
+
}
|
|
237
|
+
return { results: this.results, warnings: [...new Set(this.warnings)], changed };
|
|
238
|
+
}
|
|
239
|
+
serialize() {
|
|
240
|
+
const text = serializeGraph(this.document, this.mode.style);
|
|
241
|
+
if (this.mode.kind !== "aligned") return text;
|
|
242
|
+
const before = serializeGraph(JSON.parse(this.original), this.mode.style).split("\n");
|
|
243
|
+
const merged = mergeLines(this.original.split("\n"), before, text.split("\n"));
|
|
244
|
+
return merged ? merged.join("\n") : text;
|
|
245
|
+
}
|
|
246
|
+
async writeAtomically(text) {
|
|
247
|
+
const current = await fs.readFile(this.file, "utf8");
|
|
248
|
+
if (current !== this.original) {
|
|
249
|
+
throw new GraphEditError(`${this.file} changed on disk while it was being edited; nothing was written. Run the command again.`);
|
|
250
|
+
}
|
|
251
|
+
const { mode } = await fs.stat(this.file);
|
|
252
|
+
await writeFileAtomic(this.file, text, { mode });
|
|
253
|
+
}
|
|
254
|
+
async loadDefinitions(ids) {
|
|
255
|
+
const missing = ids.filter((id) => id && !this.definitions.has(id));
|
|
256
|
+
if (!missing.length) return;
|
|
257
|
+
for (const [id, definition] of await moduleDefinitions(this.file, missing)) this.definitions.set(id, definition);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The module a `node add` names. A full id is used as given; a short name is
|
|
261
|
+
* looked up as a project node (`nodes/<Name>/`) first, then as a built-in
|
|
262
|
+
* (`Circle` is `cascade.geo.Circle`). A module with no static definition is
|
|
263
|
+
* refused, because the editor could not check a single port or parameter on it.
|
|
264
|
+
*/
|
|
265
|
+
async resolveModule(name) {
|
|
266
|
+
const candidates = name.includes(".") ? [name] : [`project.${name}`, ...builtinModuleIds().filter((id) => id.slice(id.lastIndexOf(".") + 1).toLowerCase() === name.toLowerCase())];
|
|
267
|
+
await this.loadDefinitions(candidates);
|
|
268
|
+
const found = candidates.filter((id) => this.definitions.get(id));
|
|
269
|
+
if (found.length > 1 && !name.includes(".")) {
|
|
270
|
+
throw new GraphEditError(`"${name}" is ambiguous: ${found.join(", ")}`);
|
|
271
|
+
}
|
|
272
|
+
if (found[0]) return found[0];
|
|
273
|
+
const near = builtinModuleIds().filter((id) => id.toLowerCase().includes(name.toLowerCase().replace(/^.*\./, ""))).slice(0, 6);
|
|
274
|
+
throw new GraphEditError(
|
|
275
|
+
`No node type "${name}" with a static definition: not a built-in, and no nodes/${name.replace(/^project\./, "")}/index.ts exporting \`definition\` in ${path.dirname(this.file)}` + (near.length ? `. Similar: ${near.join(", ")}` : '. "cascade inspect" lists what a graph uses; doc/NODE_REFERENCE.md lists the built-ins')
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
var openGraph = (file) => GraphFile.open(file);
|
|
280
|
+
export {
|
|
281
|
+
GraphFile,
|
|
282
|
+
detectStyle,
|
|
283
|
+
openGraph,
|
|
284
|
+
serializeGraph
|
|
285
|
+
};
|
|
286
|
+
//# sourceMappingURL=graphFile-VPSQ6OF7.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CANVAS_PACKAGE,
|
|
3
|
+
MISSING_CANVAS_MESSAGE,
|
|
4
|
+
MissingCanvasError,
|
|
5
|
+
canvasAvailable,
|
|
6
|
+
installHeadlessCanvas
|
|
7
|
+
} from "./chunk-WIH4TEIS.js";
|
|
8
|
+
export {
|
|
9
|
+
CANVAS_PACKAGE,
|
|
10
|
+
MISSING_CANVAS_MESSAGE,
|
|
11
|
+
MissingCanvasError,
|
|
12
|
+
canvasAvailable,
|
|
13
|
+
installHeadlessCanvas
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=headlessCanvas-5XLDZT7M.js.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
headlessBuiltin,
|
|
3
|
+
headlessBuiltinRegistrations
|
|
4
|
+
} from "./chunk-36P5K3SD.js";
|
|
5
|
+
import "./chunk-D6BUOHUG.js";
|
|
6
|
+
import "./chunk-RXDIMXN5.js";
|
|
7
|
+
import "./chunk-QQPA2ZHH.js";
|
|
8
|
+
import "./chunk-KH4MUBKF.js";
|
|
9
|
+
import "./chunk-35D343DC.js";
|
|
10
|
+
import "./chunk-DEQSEFFH.js";
|
|
11
|
+
import "./chunk-HSNOBIVT.js";
|
|
12
|
+
import "./chunk-J7VK7MNF.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/helpCommand.ts
|
|
15
|
+
import fs from "node:fs/promises";
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
var HELP_NODE_USAGE = "cascade help node [<type>] [--project <dir>] [--json]";
|
|
20
|
+
async function runHelpCommand(args) {
|
|
21
|
+
const json = args.includes("--json");
|
|
22
|
+
const projectIndex = args.indexOf("--project");
|
|
23
|
+
const project = path.resolve(projectIndex === -1 ? "." : args[projectIndex + 1] ?? ".");
|
|
24
|
+
const operands = [];
|
|
25
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
26
|
+
if (args[index] === "--project") index += 1;
|
|
27
|
+
else if (!args[index].startsWith("-")) operands.push(args[index]);
|
|
28
|
+
}
|
|
29
|
+
const [, topic, type] = operands;
|
|
30
|
+
if (topic !== "node") {
|
|
31
|
+
const { mainHelp } = await import("./verbs-RCE3K4VQ.js");
|
|
32
|
+
console.log(mainHelp());
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!type) {
|
|
36
|
+
const nodes = await listNodes(project);
|
|
37
|
+
if (json) print({ nodes });
|
|
38
|
+
else console.log(nodes.map((node) => ` ${node.module.padEnd(28)}${node.summary}`).join("\n") + `
|
|
39
|
+
|
|
40
|
+
${HELP_NODE_USAGE}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const found = await findNode(type, project);
|
|
44
|
+
if (found) {
|
|
45
|
+
if (json) print(found);
|
|
46
|
+
else console.log(formatNode(found));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const studio = studioNode(type);
|
|
50
|
+
if (studio) {
|
|
51
|
+
const message = `${type} is a class-based Studio node (${studio}): it has no static definition to print. Its ports are in Studio's Definition panel; doc/NODE_REFERENCE.md in this package lists it.`;
|
|
52
|
+
if (json) print({ module: type, source: "studio", message });
|
|
53
|
+
else console.log(message);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const near = (await listNodes(project)).map((node) => node.module).filter((id) => id.toLowerCase().includes(type.toLowerCase().replace(/^.*\./, ""))).slice(0, 6);
|
|
57
|
+
throw new Error(`No node type "${type}".${near.length ? ` Did you mean ${near.join(", ")}?` : ""} cascade help node lists them all.`);
|
|
58
|
+
}
|
|
59
|
+
function print(data) {
|
|
60
|
+
process.stdout.write(`${JSON.stringify(data)}
|
|
61
|
+
`);
|
|
62
|
+
}
|
|
63
|
+
async function findNode(type, project) {
|
|
64
|
+
const exact = headlessBuiltin(type);
|
|
65
|
+
if (exact) return { module: exact.moduleId, source: "builtin", definition: exact.definition };
|
|
66
|
+
const name = type.slice(type.lastIndexOf(".") + 1);
|
|
67
|
+
if (!type.includes(".") || type.startsWith("project.")) {
|
|
68
|
+
const local = await projectDefinition(project, name);
|
|
69
|
+
if (local) return { module: `project.${name}`, source: "project", definition: local };
|
|
70
|
+
}
|
|
71
|
+
if (type.includes(".")) return void 0;
|
|
72
|
+
const short = headlessBuiltinRegistrations.filter((registration) => registration.moduleId.slice(registration.moduleId.lastIndexOf(".") + 1).toLowerCase() === name.toLowerCase());
|
|
73
|
+
return short.length === 1 ? { module: short[0].moduleId, source: "builtin", definition: short[0].definition } : void 0;
|
|
74
|
+
}
|
|
75
|
+
async function projectDefinition(project, name) {
|
|
76
|
+
if (!existsSync(path.join(project, "nodes", name, "index.ts"))) return void 0;
|
|
77
|
+
const { moduleDefinitions } = await import("./projectRuntime-Z5PJOH47.js");
|
|
78
|
+
const definitions = await moduleDefinitions(path.join(project, "index.cascade"), [`project.${name}`]);
|
|
79
|
+
return definitions.get(`project.${name}`);
|
|
80
|
+
}
|
|
81
|
+
async function listNodes(project) {
|
|
82
|
+
const summary = (definition) => [definition.label, definition.description].filter(Boolean).join(" \u2014 ");
|
|
83
|
+
const builtins = headlessBuiltinRegistrations.map((registration) => ({ module: registration.moduleId, summary: summary(registration.definition) }));
|
|
84
|
+
let names = [];
|
|
85
|
+
try {
|
|
86
|
+
names = (await fs.readdir(path.join(project, "nodes"), { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
const local = [];
|
|
90
|
+
for (const name of names) {
|
|
91
|
+
const definition = await projectDefinition(project, name).catch(() => void 0);
|
|
92
|
+
local.push({ module: `project.${name}`, summary: definition ? summary(definition) : "(dynamic or unreadable definition)" });
|
|
93
|
+
}
|
|
94
|
+
return [...builtins.sort((a, b) => a.module.localeCompare(b.module)), ...local];
|
|
95
|
+
}
|
|
96
|
+
function formatNode({ module, source, definition }) {
|
|
97
|
+
const lines = [`${module}${definition.label ? ` \u2014 ${definition.label}` : ""}${source === "project" ? " (project node)" : ""}`];
|
|
98
|
+
if (definition.description) lines.push(definition.description);
|
|
99
|
+
lines.push("", `Runs on ${definition.runsOn}.${definition.capabilities?.length ? ` Capabilities: ${definition.capabilities.join(", ")}.` : ""}`);
|
|
100
|
+
for (const [heading, group] of [["Inputs", definition.inputs], ["Props", definition.props], ["Outputs", definition.outputs]]) {
|
|
101
|
+
const entries = Object.entries(group ?? {});
|
|
102
|
+
if (!entries.length) continue;
|
|
103
|
+
lines.push("", `${heading}:`);
|
|
104
|
+
for (const [name, port] of entries) lines.push(` ${describePort(name, port)}`);
|
|
105
|
+
}
|
|
106
|
+
const firstInput = Object.keys(definition.inputs ?? {})[0];
|
|
107
|
+
const firstOutput = Object.keys(definition.outputs ?? {})[0];
|
|
108
|
+
const firstProp = Object.keys(definition.props ?? {})[0];
|
|
109
|
+
lines.push("", "Use:", ` cascade node add <graph> ${module}`);
|
|
110
|
+
if (firstOutput) lines.push(` cascade connect <graph> <node>.${firstOutput} <other>.<input>`);
|
|
111
|
+
if (firstInput) lines.push(` cascade connect <graph> <other>.<output> <node>.${firstInput}`);
|
|
112
|
+
if (firstProp) lines.push(` cascade set <graph> <node>.${firstProp} <value>`);
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
115
|
+
function describePort(name, port) {
|
|
116
|
+
if (port.kind === "trigger") return `${name} trigger`;
|
|
117
|
+
const parts = [`${name} ${String(port.type)}`];
|
|
118
|
+
if ("default" in port) parts.push(`default ${JSON.stringify(port.default)}`);
|
|
119
|
+
if (port.min !== void 0 || port.max !== void 0) parts.push(`range ${port.min ?? ""}\u2026${port.max ?? ""}`);
|
|
120
|
+
if (Array.isArray(port.options)) parts.push(`options ${port.options.map((option) => JSON.stringify(option?.value ?? option)).join(" | ")}`);
|
|
121
|
+
if (port.expression) parts.push(`expression ${String(port.expression)}`);
|
|
122
|
+
const line = parts.join(", ");
|
|
123
|
+
return port.description ? `${line} \u2014 ${String(port.description)}` : line;
|
|
124
|
+
}
|
|
125
|
+
function studioNode(type) {
|
|
126
|
+
const reference = referenceFile();
|
|
127
|
+
if (!reference) return void 0;
|
|
128
|
+
const text = readFileSync(reference, "utf8");
|
|
129
|
+
const studio = text.slice(text.indexOf("## Studio node libraries"));
|
|
130
|
+
const name = type.slice(type.lastIndexOf(".") + 1);
|
|
131
|
+
const library = /^### (.+?) — `(cascade\.[a-z]+)\.\*`/gm;
|
|
132
|
+
let match;
|
|
133
|
+
const sections = [];
|
|
134
|
+
while (match = library.exec(studio)) sections.push({ label: match[1], namespace: match[2], at: match.index });
|
|
135
|
+
for (const [index, section] of sections.entries()) {
|
|
136
|
+
const body = studio.slice(section.at, sections[index + 1]?.at ?? studio.length);
|
|
137
|
+
if (type.includes(".") && !type.startsWith(`${section.namespace}.`)) continue;
|
|
138
|
+
if (body.includes(`\`${name}\``)) return `${section.namespace}.${name}, ${section.label} library`;
|
|
139
|
+
}
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
function referenceFile() {
|
|
143
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
144
|
+
for (const candidate of [path.resolve(here, "..", "..", "doc", "NODE_REFERENCE.md"), path.resolve(here, "..", "doc", "NODE_REFERENCE.md")]) {
|
|
145
|
+
if (existsSync(candidate)) return candidate;
|
|
146
|
+
}
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
HELP_NODE_USAGE,
|
|
151
|
+
findNode,
|
|
152
|
+
formatNode,
|
|
153
|
+
runHelpCommand
|
|
154
|
+
};
|
|
155
|
+
//# sourceMappingURL=helpCommand-3VRE3KVO.js.map
|