@field/cascade 0.8.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 +36 -3
- package/CHANGELOG.md +401 -0
- package/DESIGN.md +14 -0
- package/README.md +41 -26
- package/dist/assets/CodeEditor-4O7FQ__R.js +623 -0
- package/dist/assets/CodePanel-BU-autmq.js +2 -0
- package/dist/assets/{CustomNodeDialog-BQbEhIwL.js → CustomNodeDialog-BRpDfOV5.js} +1 -1
- package/dist/assets/OpenGraphDialog-BwbRlC0X.js +1 -0
- package/dist/assets/ProjectSettings-DLOXLxcA.js +2 -0
- package/dist/assets/SettingsDialog-Bz8JxISH.js +1 -0
- package/dist/assets/{VersionHistoryDialog-D6VBzSQ0.js → VersionHistoryDialog-D5Z0nJ5-.js} +2 -2
- package/dist/assets/{core-BInLG4gw.js → core-Yqi0Ni61.js} +8 -8
- package/dist/assets/{cssMode-DfR2qmYG.js → cssMode-CxUzc4vp.js} +1 -1
- package/dist/assets/definitions-GreEMXLV.js +1 -0
- package/dist/assets/{editor.api-Ck8_vgQV.js → editor.api-BNFb25CX.js} +1 -1
- package/dist/assets/{freemarker2-fVn10Wn3.js → freemarker2-0ZDnWxeM.js} +1 -1
- package/dist/assets/geo-u0n_jOsD.js +1 -0
- package/dist/assets/{handlebars-BCSchLT2.js → handlebars-Cy18MoaD.js} +1 -1
- package/dist/assets/{html-CVpSv_k9.js → html-B-2Dx7nq.js} +1 -1
- package/dist/assets/{htmlMode-Da4ESTv0.js → htmlMode-DUAc9B_d.js} +1 -1
- package/dist/assets/{image-BLn--oSF.js → image-cAa-Nu9O.js} +3 -3
- package/dist/assets/index-5ZtDAuy2.js +220 -0
- package/dist/assets/index-7vae6YFC.css +1 -0
- package/dist/assets/index-client-DqYmKstU.js +1 -0
- package/dist/assets/{javascript-BpX0Gvcf.js → javascript-DeQsDqP5.js} +1 -1
- package/dist/assets/{jsonMode-CsLqaou7.js → jsonMode-BiFwJVSk.js} +1 -1
- package/dist/assets/legacy-CTEeXeqF.js +2 -0
- package/dist/assets/{liquid-efUz51Wf.js → liquid-BLoHH68a.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-DCi5-K0n.js → lspLanguageFeatures-CNFgm1Kv.js} +1 -1
- package/dist/assets/math-DioIQOPJ.js +1 -0
- package/dist/assets/{mdx-DP_sjGVy.js → mdx-CFXQVNfs.js} +1 -1
- package/dist/assets/pop-DC_TQG9t.js +1 -0
- package/dist/assets/{python-aAuP3LUi.js → python-Cf7S7p2E.js} +1 -1
- package/dist/assets/{razor-7M6sMmC5.js → razor-CMHBcG-g.js} +1 -1
- package/dist/assets/studioScenes-8sq9FfFg.js +1 -0
- package/dist/assets/{tsMode-Bf4CnoYK.js → tsMode-BEih0GDj.js} +1 -1
- package/dist/assets/{typescript-ddhYBGyT.js → typescript-COEKYD_a.js} +1 -1
- package/dist/assets/{workers-X5la8DDT.js → workers-BNhAhMNn.js} +1 -1
- package/dist/assets/{xml-BzbXa3Ps.js → xml-CPiMpnPl.js} +1 -1
- package/dist/assets/{yaml-CXs-VzxL.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 +106 -14207
- 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 +74 -11
- 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/bundle.d.ts +75 -0
- package/dist/contracts/bundle.js +112 -0
- package/dist/contracts/definition.d.ts +67 -4
- package/dist/contracts/document.d.ts +29 -0
- package/dist/contracts/documentFormat.d.ts +18 -0
- package/dist/contracts/documentFormat.js +195 -0
- package/dist/contracts/editor.d.ts +189 -0
- package/dist/contracts/editor.js +2 -0
- package/dist/contracts/index.d.ts +3 -0
- package/dist/contracts/index.js +3 -0
- package/dist/contracts/validate.js +59 -3
- package/dist/contracts/values.d.ts +3 -3
- package/dist/contracts/values.js +1 -0
- package/dist/image.d.ts +141 -0
- package/dist/image.js +127 -0
- package/dist/index.html +4 -4
- package/dist/player/app.js +17 -5
- package/dist/player/editor.js +1919 -0
- package/dist/player/editorEngine.js +477 -0
- package/dist/runtime/builtins/core/bundle.d.ts +70 -0
- package/dist/runtime/builtins/core/bundle.js +81 -0
- package/dist/runtime/builtins/core/index.d.ts +3 -2
- package/dist/runtime/builtins/core/index.js +6 -2
- package/dist/runtime/builtins/core/routing.d.ts +27 -0
- package/dist/runtime/builtins/core/routing.js +28 -0
- 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/runtime/edit/document.d.ts +94 -0
- package/dist/runtime/edit/document.js +194 -0
- package/dist/runtime/edit/graphEditor.d.ts +223 -0
- package/dist/runtime/edit/graphEditor.js +893 -0
- package/dist/runtime/edit/index.d.ts +12 -0
- package/dist/runtime/edit/index.js +6 -0
- package/dist/runtime/edit/layout.d.ts +50 -0
- package/dist/runtime/edit/layout.js +126 -0
- package/dist/runtime/edit/operations.d.ts +65 -0
- package/dist/runtime/edit/operations.js +57 -0
- package/dist/runtime/edit/parameters.d.ts +75 -0
- package/dist/runtime/edit/parameters.js +284 -0
- package/dist/runtime/edit/read.d.ts +35 -0
- package/dist/runtime/edit/read.js +65 -0
- package/dist/runtime/editor/index.d.ts +104 -0
- package/dist/runtime/editor/index.js +439 -0
- package/dist/runtime/expressions/bundlePaths.d.ts +37 -0
- package/dist/runtime/expressions/bundlePaths.js +55 -0
- package/dist/runtime/expressions/index.d.ts +2 -0
- package/dist/runtime/expressions/index.js +1 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/params/animator.d.ts +5 -0
- package/dist/runtime/params/animator.js +16 -0
- package/dist/runtime/runtime.js +238 -12
- package/dist/runtime/seed.d.ts +29 -0
- package/dist/runtime/seed.js +52 -0
- package/dist/runtime/types.d.ts +12 -0
- package/dist/stage.js +43 -9
- package/dist/studio/panel.d.ts +2 -0
- package/doc/CLI_REFERENCE.md +171 -0
- package/doc/EDITOR_EXPORT.md +235 -0
- package/doc/HEADLESS_GPU.md +27 -0
- package/doc/MCP.md +69 -0
- package/doc/NODE_REFERENCE.md +208 -0
- package/doc/PROJECT_AUTHORING.md +258 -1
- package/package.json +29 -8
- package/dist/assets/CodeEditor-BEk-kIBd.js +0 -623
- package/dist/assets/CodePanel-CeSdjXkg.js +0 -2
- package/dist/assets/ExportDialog-CjTbbTZJ.js +0 -358
- package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
- package/dist/assets/OpenGraphDialog-DCwH9itM.js +0 -1
- package/dist/assets/ProjectSettings-Dg7M5xt9.js +0 -2
- package/dist/assets/SettingsDialog-e7GgsjB9.js +0 -1
- package/dist/assets/definitions-DZpOjWKR.js +0 -1
- package/dist/assets/geo-D2VPjAdt.js +0 -1
- package/dist/assets/index-CMiBdEuT.js +0 -212
- package/dist/assets/index-DMIuml9a.css +0 -1
- package/dist/assets/index-client-BI2H82FJ.js +0 -1
- package/dist/assets/legacy-ZA7textG.js +0 -2
- package/dist/assets/math-B6hpxPJO.js +0 -1
- package/dist/assets/pop-CYUIjT6x.js +0 -1
- package/dist/assets/projectSettingsApi-C1WvIwfP.js +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRuntime,
|
|
3
|
+
moduleId,
|
|
4
|
+
projectModuleFile,
|
|
5
|
+
validateNodeModuleArchitecture
|
|
6
|
+
} from "./chunk-EUIAC3U3.js";
|
|
7
|
+
import {
|
|
8
|
+
builtinNodeRegistration
|
|
9
|
+
} from "./chunk-36P5K3SD.js";
|
|
10
|
+
import {
|
|
11
|
+
ProjectRoot,
|
|
12
|
+
extractNodeDefinition
|
|
13
|
+
} from "./chunk-RQZ4SX5R.js";
|
|
14
|
+
|
|
15
|
+
// src/cli/webBuild.ts
|
|
16
|
+
import fs from "node:fs/promises";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { builtinModules } from "node:module";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { build } from "esbuild";
|
|
21
|
+
import ts from "typescript";
|
|
22
|
+
|
|
23
|
+
// packages/runtime/src/browser.ts
|
|
24
|
+
function createBrowserRuntimeHost(options) {
|
|
25
|
+
const {
|
|
26
|
+
modules,
|
|
27
|
+
legacyShell,
|
|
28
|
+
now = () => Date.now(),
|
|
29
|
+
report = () => {
|
|
30
|
+
},
|
|
31
|
+
...capabilities
|
|
32
|
+
} = options;
|
|
33
|
+
return {
|
|
34
|
+
environment: "browser",
|
|
35
|
+
modules,
|
|
36
|
+
legacyShell,
|
|
37
|
+
capabilities,
|
|
38
|
+
now,
|
|
39
|
+
report
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/cli/webBuild.ts
|
|
44
|
+
var installRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
45
|
+
var forbiddenModules = new Set(builtinModules.map((name) => name.replace(/^node:/, "")));
|
|
46
|
+
async function buildPlayer(file, options) {
|
|
47
|
+
if (!options.out) throw new Error("cascade build requires --out <new-directory>");
|
|
48
|
+
const project = new ProjectRoot(path.dirname(path.resolve(file)));
|
|
49
|
+
const out = path.resolve(project.root, options.out);
|
|
50
|
+
if (await exists(out)) throw new Error(`Player output already exists: ${out}. Choose a new directory.`);
|
|
51
|
+
const canonicalOut = await canonicalFuturePath(out);
|
|
52
|
+
for (const sourceDirectory of ["assets", "nodes"]) {
|
|
53
|
+
const sourceRoot = await canonicalFuturePath(path.join(project.root, sourceDirectory));
|
|
54
|
+
if (inside(sourceRoot, canonicalOut)) {
|
|
55
|
+
throw new Error(`Player output cannot be inside the project's ${sourceDirectory} directory`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const document = JSON.parse(await fs.readFile(file, "utf8"));
|
|
59
|
+
if (!Array.isArray(document.nodes)) throw new Error("Graph document must contain a nodes array");
|
|
60
|
+
const modules = [];
|
|
61
|
+
const seen = /* @__PURE__ */ new Set();
|
|
62
|
+
for (const node of document.nodes) {
|
|
63
|
+
const id = moduleId(node);
|
|
64
|
+
if (seen.has(id)) continue;
|
|
65
|
+
seen.add(id);
|
|
66
|
+
if (builtinNodeRegistration(id)) continue;
|
|
67
|
+
const resolved = await projectModuleFile(project.root, node, id);
|
|
68
|
+
if (!resolved.file) throw new Error(`${id}: no supported static project module (${resolved.tried.join(", ")})`);
|
|
69
|
+
const sourceFile = project.resolve(path.relative(project.root, resolved.file));
|
|
70
|
+
const source = await fs.readFile(sourceFile, "utf8");
|
|
71
|
+
const extracted = extractNodeDefinition(source, sourceFile, ts);
|
|
72
|
+
if (!extracted.ok) throw new Error(`${id}: static browser builds require definition-v1
|
|
73
|
+
${extracted.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n")}`);
|
|
74
|
+
const diagnostics = validateNodeModuleArchitecture(source, sourceFile, ts);
|
|
75
|
+
if (diagnostics.length) throw new Error(diagnostics.map((d) => `${id}: ${d.code}: ${d.message}`).join("\n"));
|
|
76
|
+
modules.push({ id, file: sourceFile, definition: extracted.definition });
|
|
77
|
+
}
|
|
78
|
+
const registrations = modules.map((item) => ({
|
|
79
|
+
kind: "definition-v1",
|
|
80
|
+
moduleId: item.id,
|
|
81
|
+
definition: item.definition,
|
|
82
|
+
loadExecute: async () => {
|
|
83
|
+
throw new Error("Static builds never execute project code");
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
const runtime = createRuntime({ host: createBrowserRuntimeHost({
|
|
87
|
+
modules: { resolve: async (id) => registrations.find((item) => item.moduleId === id) ?? null },
|
|
88
|
+
// Presence describes the exported player contract, not this Node process.
|
|
89
|
+
assets: {},
|
|
90
|
+
gpu: {}
|
|
91
|
+
}), nodes: registrations });
|
|
92
|
+
const assets = new Set(options.assets ?? []);
|
|
93
|
+
try {
|
|
94
|
+
const graph = await runtime.load(document);
|
|
95
|
+
const diagnostics = graph.preflight();
|
|
96
|
+
if (diagnostics.length) throw new Error(`This graph cannot be exported to the browser player:
|
|
97
|
+
${diagnostics.map((d) => `${d.path ?? ""} ${d.code}: ${d.message}`).join("\n")}`);
|
|
98
|
+
for (const node of graph.inspect().nodes) {
|
|
99
|
+
for (const port of [...Object.values(node.inputs), ...Object.values(node.props)]) {
|
|
100
|
+
if (port.type !== "image" && port.type !== "asset") continue;
|
|
101
|
+
const value = port.value;
|
|
102
|
+
const asset = typeof value === "string" ? value : value && typeof value === "object" && "path" in value ? value.path : void 0;
|
|
103
|
+
if (typeof asset === "string" && asset && !/^(https?:|data:|blob:)/.test(asset)) assets.add(asset);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
await runtime.dispose();
|
|
108
|
+
}
|
|
109
|
+
const sources = [];
|
|
110
|
+
for (const asset of assets) {
|
|
111
|
+
const name = asset.replace(/^\.\//, "");
|
|
112
|
+
if (!name || name.includes("\\") || name.split("/").some((part) => !part || part.startsWith(".")) || path.isAbsolute(name)) {
|
|
113
|
+
throw new Error(`Asset must be a visible project-relative file: ${asset}`);
|
|
114
|
+
}
|
|
115
|
+
const source = project.resolve(name);
|
|
116
|
+
if (!(await fs.stat(source)).isFile()) throw new Error(`Asset is not a file: ${asset}`);
|
|
117
|
+
sources.push({ name, file: source });
|
|
118
|
+
}
|
|
119
|
+
const playerApp = await playerFile("app");
|
|
120
|
+
const playerEmbed = await playerFile("embed");
|
|
121
|
+
const plugins = [await staticRuntimePlugin(project.root)];
|
|
122
|
+
const imports = modules.map((item, index) => `import { execute as execute${index} } from ${JSON.stringify(item.file)};`).join("\n");
|
|
123
|
+
const registrationSource = modules.map((item, index) => `{ kind:'definition-v1', moduleId:${JSON.stringify(item.id)}, definition:${JSON.stringify(item.definition)}, loadExecute:async()=>execute${index} }`).join(",");
|
|
124
|
+
const assetSource = sources.map((item) => `${JSON.stringify(item.name)}:new URL(${JSON.stringify(`./assets/${item.name.split("/").map(encodeURIComponent).join("/")}`)},import.meta.url).href`).join(",");
|
|
125
|
+
const appSource = `import { startPlayer } from ${JSON.stringify(playerApp)};
|
|
126
|
+
${imports}
|
|
127
|
+
window.cascadePlayerReady = startPlayer({document:${JSON.stringify(document)},registrations:[${registrationSource}],assets:{${assetSource}}});
|
|
128
|
+
window.cascadePlayerReady.catch(error=>{const message=document.createElement('pre');message.textContent=String(error?.message??error);document.body.append(message);});`;
|
|
129
|
+
const embedSource = `import { mountPlayer } from ${JSON.stringify(playerEmbed)};
|
|
130
|
+
export function mount(container,options={}) { return mountPlayer(container,{...options,url:new URL('./player.html',import.meta.url)}); }`;
|
|
131
|
+
const common = {
|
|
132
|
+
bundle: true,
|
|
133
|
+
platform: "browser",
|
|
134
|
+
format: "esm",
|
|
135
|
+
target: "es2022",
|
|
136
|
+
write: false,
|
|
137
|
+
minify: true,
|
|
138
|
+
logLevel: "silent",
|
|
139
|
+
plugins
|
|
140
|
+
};
|
|
141
|
+
const app = await build({ ...common, stdin: { contents: appSource, resolveDir: project.root, sourcefile: "player-entry.js" } });
|
|
142
|
+
const embed = await build({ ...common, stdin: { contents: embedSource, resolveDir: project.root, sourcefile: "embed-entry.js" } });
|
|
143
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
144
|
+
const staging = await fs.mkdtemp(path.join(path.dirname(out), ".cascade-player-"));
|
|
145
|
+
try {
|
|
146
|
+
const html = '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Cascade</title><style>html,body{margin:0;width:100%;height:100%;overflow:hidden}body{font:13px system-ui}pre{white-space:pre-wrap;padding:16px}</style></head><body><script type="module" src="./app.js"></script></body></html>';
|
|
147
|
+
await fs.writeFile(path.join(staging, "index.html"), html);
|
|
148
|
+
await fs.writeFile(path.join(staging, "player.html"), html);
|
|
149
|
+
await fs.writeFile(path.join(staging, "app.js"), app.outputFiles[0].contents);
|
|
150
|
+
await fs.writeFile(path.join(staging, "embed.js"), embed.outputFiles[0].contents);
|
|
151
|
+
for (const source of sources) {
|
|
152
|
+
const target = path.join(staging, "assets", source.name);
|
|
153
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
154
|
+
await fs.copyFile(source.file, target);
|
|
155
|
+
}
|
|
156
|
+
await fs.mkdir(out);
|
|
157
|
+
try {
|
|
158
|
+
for (const entry of await fs.readdir(staging)) await fs.rename(path.join(staging, entry), path.join(out, entry));
|
|
159
|
+
} catch (error) {
|
|
160
|
+
await fs.rm(out, { recursive: true, force: true });
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
} finally {
|
|
164
|
+
await fs.rm(staging, { recursive: true, force: true });
|
|
165
|
+
}
|
|
166
|
+
return { directory: out };
|
|
167
|
+
}
|
|
168
|
+
async function playerFile(name) {
|
|
169
|
+
const source = path.join(installRoot, "src", "player", `${name}.ts`);
|
|
170
|
+
return await exists(source) ? source : path.join(installRoot, "dist", "player", `${name}.js`);
|
|
171
|
+
}
|
|
172
|
+
async function staticRuntimePlugin(projectRoot) {
|
|
173
|
+
const manifest = JSON.parse(await fs.readFile(path.join(installRoot, "package.json"), "utf8"));
|
|
174
|
+
const canonicalProjectRoot = await fs.realpath(projectRoot);
|
|
175
|
+
const confinementMarker = /* @__PURE__ */ Symbol("cascade-player-confinement");
|
|
176
|
+
return { name: "cascade-static-player", setup(builder) {
|
|
177
|
+
builder.onResolve({ filter: /.*/ }, async (args) => {
|
|
178
|
+
const marked = typeof args.pluginData === "object" && args.pluginData !== null && args.pluginData[confinementMarker] === true;
|
|
179
|
+
if (!marked && isFilesystemImport(args.path) && await importerIsInProject(args.importer, canonicalProjectRoot)) {
|
|
180
|
+
const resolved = await builder.resolve(args.path, {
|
|
181
|
+
importer: args.importer,
|
|
182
|
+
resolveDir: args.resolveDir,
|
|
183
|
+
kind: args.kind,
|
|
184
|
+
pluginData: { [confinementMarker]: true }
|
|
185
|
+
});
|
|
186
|
+
if (resolved.errors.length || !resolved.path || resolved.external) return resolved;
|
|
187
|
+
const canonicalTarget = await fs.realpath(resolved.path);
|
|
188
|
+
if (!inside(canonicalProjectRoot, canonicalTarget)) {
|
|
189
|
+
return { errors: [{
|
|
190
|
+
text: `Project import escapes the project: ${args.path} resolves to ${canonicalTarget}`
|
|
191
|
+
}] };
|
|
192
|
+
}
|
|
193
|
+
return resolved;
|
|
194
|
+
}
|
|
195
|
+
if (args.path.startsWith("node:") || forbiddenModules.has(args.path)) {
|
|
196
|
+
return { errors: [{ text: `Browser player cannot import Node module ${args.path}` }] };
|
|
197
|
+
}
|
|
198
|
+
const canonical = args.path.replace(/^@field\/cascade\//, "cascade/").replace(/^@cascade\//, "cascade/");
|
|
199
|
+
if (!canonical.startsWith("cascade/")) return;
|
|
200
|
+
const subpath = canonical.slice("cascade/".length);
|
|
201
|
+
if (!(subpath === "io" || subpath === "image" || subpath === "gpu" || subpath === "contracts" || subpath.startsWith("contracts/") || subpath === "runtime" || subpath.startsWith("runtime/"))) {
|
|
202
|
+
return { errors: [{ text: `Browser player cannot import ${args.path}; server transports and configuration are not exported` }] };
|
|
203
|
+
}
|
|
204
|
+
if (subpath === "runtime/node" || subpath === "runtime/definition/extract") {
|
|
205
|
+
return { errors: [{ text: `Browser player cannot import ${args.path}` }] };
|
|
206
|
+
}
|
|
207
|
+
const entry = manifest.exports[`./${subpath}`]?.default;
|
|
208
|
+
if (!entry) return { errors: [{ text: `Unknown Cascade browser export: ${args.path}` }] };
|
|
209
|
+
const sourceShim = path.join(installRoot, "server", "src", "runtime", `${subpath}.ts`);
|
|
210
|
+
return { path: (subpath === "io" || subpath === "image" || subpath === "gpu") && await exists(sourceShim) ? sourceShim : path.resolve(installRoot, entry) };
|
|
211
|
+
});
|
|
212
|
+
} };
|
|
213
|
+
}
|
|
214
|
+
function isFilesystemImport(specifier) {
|
|
215
|
+
return specifier.startsWith(".") || path.isAbsolute(specifier);
|
|
216
|
+
}
|
|
217
|
+
async function importerIsInProject(importer, canonicalProjectRoot) {
|
|
218
|
+
if (!importer || !path.isAbsolute(importer)) return false;
|
|
219
|
+
try {
|
|
220
|
+
return inside(canonicalProjectRoot, await fs.realpath(importer));
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error.code === "ENOENT") return false;
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function canonicalFuturePath(candidate) {
|
|
227
|
+
let existing = path.resolve(candidate);
|
|
228
|
+
const missing = [];
|
|
229
|
+
for (; ; ) {
|
|
230
|
+
try {
|
|
231
|
+
await fs.lstat(existing);
|
|
232
|
+
break;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (error.code !== "ENOENT") throw error;
|
|
235
|
+
const parent = path.dirname(existing);
|
|
236
|
+
if (parent === existing) throw new Error(`Output path has no accessible parent: ${candidate}`);
|
|
237
|
+
missing.unshift(path.basename(existing));
|
|
238
|
+
existing = parent;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return path.resolve(await fs.realpath(existing), ...missing);
|
|
242
|
+
}
|
|
243
|
+
function inside(root, candidate) {
|
|
244
|
+
const relative = path.relative(root, candidate);
|
|
245
|
+
return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
|
|
246
|
+
}
|
|
247
|
+
async function exists(file) {
|
|
248
|
+
try {
|
|
249
|
+
await fs.lstat(file);
|
|
250
|
+
return true;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
if (error.code === "ENOENT") return false;
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export {
|
|
258
|
+
createBrowserRuntimeHost,
|
|
259
|
+
installRoot,
|
|
260
|
+
buildPlayer,
|
|
261
|
+
playerFile,
|
|
262
|
+
staticRuntimePlugin,
|
|
263
|
+
canonicalFuturePath,
|
|
264
|
+
inside,
|
|
265
|
+
exists
|
|
266
|
+
};
|
|
267
|
+
//# sourceMappingURL=chunk-3JTJHF7V.js.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/cli/runTrace.ts
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
var MAX_NODE_LOGS = 500;
|
|
5
|
+
var MAX_LOG_LINE = 2e3;
|
|
6
|
+
var RunTrace = class {
|
|
7
|
+
constructor(graphFile, cascade, seed) {
|
|
8
|
+
this.graphFile = graphFile;
|
|
9
|
+
this.cascade = cascade;
|
|
10
|
+
this.seed = seed;
|
|
11
|
+
this.runs = [];
|
|
12
|
+
this.current = null;
|
|
13
|
+
/** Stage calls made outside any node (a run with no graph events). */
|
|
14
|
+
this.loose = [];
|
|
15
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
16
|
+
this.envNames = [];
|
|
17
|
+
}
|
|
18
|
+
/** Follow one loaded graph's events. Returns the unsubscribe. */
|
|
19
|
+
attach(graph) {
|
|
20
|
+
const modules = new Map(graph.inspect().nodes.map((node) => [node.id, node.moduleId]));
|
|
21
|
+
return graph.subscribe((event) => {
|
|
22
|
+
if (event.type === "run:start") {
|
|
23
|
+
this.runs.push({ runId: event.runId, frame: graph.getFrame(), nodes: [] });
|
|
24
|
+
} else if (event.type === "node:start") {
|
|
25
|
+
const run = this.runs[this.runs.length - 1];
|
|
26
|
+
if (!run) return;
|
|
27
|
+
const node = { id: event.nodeId, module: modules.get(event.nodeId) ?? "", ms: 0, stages: [] };
|
|
28
|
+
run.nodes.push(node);
|
|
29
|
+
this.current = { node, started: performance.now() };
|
|
30
|
+
} else if (event.type === "node:complete" && this.current?.node.id === event.nodeId) {
|
|
31
|
+
this.current.node.ms = Math.round(performance.now() - this.current.started);
|
|
32
|
+
this.current = null;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/** Record a stage call against the node cooking now. */
|
|
37
|
+
recordStage(trace) {
|
|
38
|
+
if (this.current) this.current.node.stages.push(trace);
|
|
39
|
+
else this.loose.push(trace);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Record a line printed while a node cooks, against that node. Returns
|
|
43
|
+
* false when no node is cooking, so the caller leaves the line alone (the
|
|
44
|
+
* CLI's own messages are not node output).
|
|
45
|
+
*/
|
|
46
|
+
recordLog(level, text) {
|
|
47
|
+
const node = this.current?.node;
|
|
48
|
+
if (!node) return false;
|
|
49
|
+
for (const line of text.split("\n")) {
|
|
50
|
+
if (!line.trim()) continue;
|
|
51
|
+
node.logs ??= [];
|
|
52
|
+
if (node.logs.length >= MAX_NODE_LOGS) {
|
|
53
|
+
node.logsDropped = (node.logsDropped ?? 0) + 1;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
node.logs.push({ level, text: line.length > MAX_LOG_LINE ? `${line.slice(0, MAX_LOG_LINE)}\u2026` : line });
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
/** Every logged line in order, with its node. */
|
|
61
|
+
logs() {
|
|
62
|
+
return this.runs.flatMap((run) => run.nodes.flatMap((node) => (node.logs ?? []).map((line) => ({ node: node.id, ...line }))));
|
|
63
|
+
}
|
|
64
|
+
/** The names (never the values) of variables `--env` set, for the trace. */
|
|
65
|
+
setEnvNames(names) {
|
|
66
|
+
this.envNames = [...names];
|
|
67
|
+
}
|
|
68
|
+
/** Every stage call in order, with its node. */
|
|
69
|
+
stages() {
|
|
70
|
+
const list = [];
|
|
71
|
+
for (const run of this.runs) {
|
|
72
|
+
for (const node of run.nodes) for (const stage of node.stages) list.push({ node: node.id, ...stage });
|
|
73
|
+
}
|
|
74
|
+
for (const stage of this.loose) list.push({ node: null, ...stage });
|
|
75
|
+
return list;
|
|
76
|
+
}
|
|
77
|
+
toJSON() {
|
|
78
|
+
return {
|
|
79
|
+
format: 1,
|
|
80
|
+
graph: this.graphFile,
|
|
81
|
+
startedAt: this.startedAt.toISOString(),
|
|
82
|
+
cascade: this.cascade,
|
|
83
|
+
seed: this.seed,
|
|
84
|
+
...this.envNames.length ? { env: this.envNames } : {},
|
|
85
|
+
runs: this.runs
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Write the trace into the project and return its project-relative path. */
|
|
89
|
+
write(projectRoot) {
|
|
90
|
+
const stamp = this.startedAt.toISOString().replace(/[:.]/g, "-");
|
|
91
|
+
const name = `${stamp}-${path.basename(this.graphFile, ".cascade")}.json`;
|
|
92
|
+
const relative = path.posix.join(".cascade", "runs", name);
|
|
93
|
+
const full = path.join(projectRoot, relative);
|
|
94
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
95
|
+
fs.writeFileSync(full, JSON.stringify(this.toJSON(), null, 2) + "\n");
|
|
96
|
+
pruneTraces(path.dirname(full));
|
|
97
|
+
return relative;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var CONSOLE_LEVELS = ["log", "info", "warn", "error", "debug"];
|
|
101
|
+
function captureNodeConsole(trace) {
|
|
102
|
+
const original = Object.fromEntries(CONSOLE_LEVELS.map((level) => [level, console[level]]));
|
|
103
|
+
for (const level of CONSOLE_LEVELS) {
|
|
104
|
+
console[level] = (...args) => {
|
|
105
|
+
trace.recordLog(level, args.map((arg) => typeof arg === "string" ? arg : arg instanceof Error ? arg.stack ?? arg.message : safeJson(arg)).join(" "));
|
|
106
|
+
original[level](...args);
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return () => {
|
|
110
|
+
for (const level of CONSOLE_LEVELS) console[level] = original[level];
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function safeJson(value) {
|
|
114
|
+
try {
|
|
115
|
+
return JSON.stringify(value) ?? String(value);
|
|
116
|
+
} catch {
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
var KEPT_TRACES = 200;
|
|
121
|
+
function pruneTraces(directory) {
|
|
122
|
+
const traces = fs.readdirSync(directory).filter((name) => name.endsWith(".json")).sort();
|
|
123
|
+
for (const name of traces.slice(0, Math.max(0, traces.length - KEPT_TRACES))) {
|
|
124
|
+
fs.rmSync(path.join(directory, name), { force: true });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
RunTrace,
|
|
130
|
+
captureNodeConsole
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=chunk-4UDFYF5G.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/nodes/definition/browserCapabilities.ts
|
|
2
|
+
function assetUrl(path) {
|
|
3
|
+
const clean = path.replace(/^\.\//, "").replace(/^assets\//, "");
|
|
4
|
+
return `/api/assets/${clean.split("/").map(encodeURIComponent).join("/")}`;
|
|
5
|
+
}
|
|
6
|
+
var browserAssetCapability = {
|
|
7
|
+
async read(ref, { signal }) {
|
|
8
|
+
const response = await fetch(assetUrl(ref.path), { signal });
|
|
9
|
+
if (!response.ok) throw new Error(`Could not read asset ${ref.path} (${response.status})`);
|
|
10
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
11
|
+
},
|
|
12
|
+
async write(data, metadata, { signal }) {
|
|
13
|
+
const name = metadata.suggestedName?.trim() || "asset.bin";
|
|
14
|
+
const form = new FormData();
|
|
15
|
+
form.append("file", new Blob([new Uint8Array(data)], { type: metadata.mediaType }), name);
|
|
16
|
+
const response = await fetch("/api/assets", {
|
|
17
|
+
method: "POST",
|
|
18
|
+
body: form,
|
|
19
|
+
signal
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) throw new Error(`Could not write asset ${name} (${response.status})`);
|
|
22
|
+
const result = await response.json();
|
|
23
|
+
if (!result.path) throw new Error(`Asset upload returned no path for ${name}`);
|
|
24
|
+
return { path: result.path, ...metadata.mediaType ? { mediaType: metadata.mediaType } : {} };
|
|
25
|
+
},
|
|
26
|
+
async resolveUrl(ref) {
|
|
27
|
+
return { value: assetUrl(ref.path), release: () => {
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
browserAssetCapability
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-53NMBJAO.js.map
|