@field/cascade 0.3.0 → 0.3.1
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 +18 -0
- package/dist/assets/{CodeEditor-BADFbTJS.js → CodeEditor-D22fVi-4.js} +4 -4
- package/dist/assets/{CodePanel-faK6PRfz.js → CodePanel-DPNEYfcR.js} +2 -2
- package/dist/assets/{CustomNodeDialog-DhlKuviy.js → CustomNodeDialog-DeHCAvgb.js} +1 -1
- package/dist/assets/{ProjectSettings-D6a6kjqR.js → ProjectSettings-DWpVtWEx.js} +1 -1
- package/dist/assets/{SettingsDialog-DJZdIvLC.js → SettingsDialog-za00IS3L.js} +1 -1
- package/dist/assets/{VersionHistoryDialog-C1X3HOhc.js → VersionHistoryDialog-DTMGhI0_.js} +1 -1
- package/dist/assets/{core-CiuiuWZi.js → core-JsyKMO1k.js} +1 -1
- package/dist/assets/{cssMode-D-NwZSMJ.js → cssMode-emdRheFK.js} +1 -1
- package/dist/assets/{editor.api-DT3WEvBd.js → editor.api-xHX-fMSo.js} +1 -1
- package/dist/assets/{freemarker2-eKNYQaiz.js → freemarker2-C3M8_Gs3.js} +1 -1
- package/dist/assets/geo-BAR6lQp5.js +1 -0
- package/dist/assets/{handlebars-C2w1ggK2.js → handlebars-CBMurtFk.js} +1 -1
- package/dist/assets/{html-DQb41jyo.js → html-xbLQwknw.js} +1 -1
- package/dist/assets/{htmlMode-IhNlh6Lv.js → htmlMode-2wKdDTon.js} +1 -1
- package/dist/assets/{image-DMCXBY-_.js → image-i1j6nWKe.js} +1 -1
- package/dist/assets/{index-HTCN44kj.js → index-CMcH8AgP.js} +35 -35
- package/dist/assets/{javascript-BIT6jMLC.js → javascript-DJaOo_2I.js} +1 -1
- package/dist/assets/{jsonMode-CNfyB1ro.js → jsonMode-Bheas8_0.js} +1 -1
- package/dist/assets/{liquid-CmSmJmOG.js → liquid-BCj_BGbM.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-BqMm3uMP.js → lspLanguageFeatures-BfIsx_xz.js} +1 -1
- package/dist/assets/{mdx-6r6YgOFw.js → mdx-DwiQDiVO.js} +1 -1
- package/dist/assets/{python-x1H0TQuI.js → python-BjrB5nqj.js} +1 -1
- package/dist/assets/{razor-Dzl1vZFm.js → razor-CfoBmGDs.js} +1 -1
- package/dist/assets/{tsMode-n6DMMe3a.js → tsMode-BcEoMSxp.js} +1 -1
- package/dist/assets/{typescript-ziEz65xN.js → typescript-DIg7TEmQ.js} +1 -1
- package/dist/assets/{workers-D-qUdp-r.js → workers-vXYuwtPw.js} +1 -1
- package/dist/assets/{xml-DMZjt5lE.js → xml-DexLQIQy.js} +1 -1
- package/dist/assets/{yaml-DFe5qZSO.js → yaml-DUnD_1dE.js} +1 -1
- package/dist/cli/index.js +317 -49
- package/dist/cli/runtime/gpu.ts +76 -0
- package/dist/contracts/capabilities.d.ts +69 -6
- package/dist/contracts/definition.d.ts +21 -1
- package/dist/contracts/schema.js +8 -5
- package/dist/contracts/validate.js +11 -2
- package/dist/gpu.d.ts +71 -0
- package/dist/gpu.js +53 -0
- package/dist/index.html +1 -1
- package/dist/runtime/runtime.d.ts +23 -3
- package/dist/runtime/runtime.js +118 -24
- package/dist/runtime/types.d.ts +3 -3
- package/package.json +5 -1
- package/dist/assets/geo-DUvVbLnx.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -782,6 +782,20 @@ function validateNodeDefinition(value) {
|
|
|
782
782
|
);
|
|
783
783
|
if ("step" in raw && (typeof raw.step !== "number" || !Number.isFinite(raw.step) || raw.step <= 0))
|
|
784
784
|
add("invalid-step", "step must be greater than zero", `${path23}.step`);
|
|
785
|
+
if (section === "props" && "expression" in raw && (typeof raw.expression !== "string" || raw.expression.length === 0))
|
|
786
|
+
add(
|
|
787
|
+
"invalid-expression",
|
|
788
|
+
"expression must be a non-empty string",
|
|
789
|
+
`${path23}.expression`
|
|
790
|
+
);
|
|
791
|
+
for (const field of ["min", "max"]) {
|
|
792
|
+
if (field in raw && (typeof raw[field] !== "number" || !Number.isFinite(raw[field])))
|
|
793
|
+
add(
|
|
794
|
+
`invalid-${field}`,
|
|
795
|
+
`${field} must be a finite number`,
|
|
796
|
+
`${path23}.${field}`
|
|
797
|
+
);
|
|
798
|
+
}
|
|
785
799
|
if (typeof raw.min === "number" && typeof raw.max === "number" && raw.min > raw.max)
|
|
786
800
|
add("invalid-range", "min must not exceed max", path23);
|
|
787
801
|
}
|
|
@@ -801,7 +815,7 @@ var init_validate = __esm({
|
|
|
801
815
|
CONTROLS = /* @__PURE__ */ new Set(["number", "slider", "range", "int", "boolean", "text", "textarea", "select", "vector", "matrix", "color", "image", "asset"]);
|
|
802
816
|
CAPABILITIES_BY_ENVIRONMENT = {
|
|
803
817
|
portable: ["assets", "media"],
|
|
804
|
-
browser: ["assets", "media", "
|
|
818
|
+
browser: ["assets", "media", "gpu"],
|
|
805
819
|
server: ["files", "assets", "media", "python", "shell"]
|
|
806
820
|
};
|
|
807
821
|
ALLOWED = CAPABILITIES_BY_ENVIRONMENT;
|
|
@@ -814,7 +828,7 @@ var init_validate = __esm({
|
|
|
814
828
|
);
|
|
815
829
|
OUTPUT_FIELDS = fields("kind type description");
|
|
816
830
|
PROP_FIELDS = fields(
|
|
817
|
-
"type default label description min max step accept control options"
|
|
831
|
+
"type default label description min max step accept control options expression"
|
|
818
832
|
);
|
|
819
833
|
}
|
|
820
834
|
});
|
|
@@ -5750,6 +5764,17 @@ var Node = class _Node {
|
|
|
5750
5764
|
this.portsUsedDuringSetup = /* @__PURE__ */ new Set();
|
|
5751
5765
|
// Port index map for O(1) lookups (portId -> port)
|
|
5752
5766
|
this._portMap = /* @__PURE__ */ new Map();
|
|
5767
|
+
// ============ Props System ============
|
|
5768
|
+
/**
|
|
5769
|
+
* Add a parameter to this node
|
|
5770
|
+
* Short alias for defineProp - use in setup()
|
|
5771
|
+
*/
|
|
5772
|
+
/** Props whose declared default expression has already been applied — see
|
|
5773
|
+
* `bindParameterProp`, which must not reinstate one the author deleted. */
|
|
5774
|
+
this.defaultExpressionsApplied = /* @__PURE__ */ new Set();
|
|
5775
|
+
/** Props whose plain stored value explicitly overrides a declared default
|
|
5776
|
+
* expression. The marker survives even when that value equals the default. */
|
|
5777
|
+
this.plainPropOverrides = /* @__PURE__ */ new Set();
|
|
5753
5778
|
this.id = id;
|
|
5754
5779
|
this.type = type;
|
|
5755
5780
|
this.graph = graph;
|
|
@@ -5884,11 +5909,6 @@ var Node = class _Node {
|
|
|
5884
5909
|
targetPort.onChange?.(value);
|
|
5885
5910
|
}
|
|
5886
5911
|
}
|
|
5887
|
-
// ============ Props System ============
|
|
5888
|
-
/**
|
|
5889
|
-
* Add a parameter to this node
|
|
5890
|
-
* Short alias for defineProp - use in setup()
|
|
5891
|
-
*/
|
|
5892
5912
|
addParm(name, config) {
|
|
5893
5913
|
this.props[name] = config;
|
|
5894
5914
|
this.propsDeclaredByCode.add(name);
|
|
@@ -5989,6 +6009,9 @@ var Node = class _Node {
|
|
|
5989
6009
|
deleteExpression: () => {
|
|
5990
6010
|
const { expression: _, expressionError: __, ...rest } = self.props[name];
|
|
5991
6011
|
self.props[name] = rest;
|
|
6012
|
+
if (self.defaultExpressionsApplied.has(name)) {
|
|
6013
|
+
self.plainPropOverrides.add(name);
|
|
6014
|
+
}
|
|
5992
6015
|
self.updateTimeDependent();
|
|
5993
6016
|
self.markDirty();
|
|
5994
6017
|
},
|
|
@@ -6068,6 +6091,12 @@ var Node = class _Node {
|
|
|
6068
6091
|
...channel ? { channel } : {}
|
|
6069
6092
|
};
|
|
6070
6093
|
}
|
|
6094
|
+
preservePlainProp(name) {
|
|
6095
|
+
this.plainPropOverrides.add(name);
|
|
6096
|
+
}
|
|
6097
|
+
preservesPlainProp(name) {
|
|
6098
|
+
return this.plainPropOverrides.has(name) && this.defaultExpressionsApplied.has(name);
|
|
6099
|
+
}
|
|
6071
6100
|
/** Read a channel back off disk. Bad data yields no channel, never a throw. */
|
|
6072
6101
|
restorePropChannel(name, data) {
|
|
6073
6102
|
if (!this.props[name]) return;
|
|
@@ -6215,6 +6244,14 @@ var Node = class _Node {
|
|
|
6215
6244
|
this.props[name] = prop;
|
|
6216
6245
|
this.props = { ...this.props };
|
|
6217
6246
|
}
|
|
6247
|
+
if (options.defaultExpression && !this.defaultExpressionsApplied.has(name)) {
|
|
6248
|
+
this.defaultExpressionsApplied.add(name);
|
|
6249
|
+
if (!this.plainPropOverrides.has(name) && !prop.expression) {
|
|
6250
|
+
prop.expression = options.defaultExpression;
|
|
6251
|
+
this.props = { ...this.props };
|
|
6252
|
+
this.updateTimeDependent();
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6218
6255
|
prop.fromParameter = name;
|
|
6219
6256
|
prop.hidden = true;
|
|
6220
6257
|
const control = paramControlType(parameter.dataType);
|
|
@@ -8103,6 +8140,7 @@ var silentProgress = { report: () => {
|
|
|
8103
8140
|
function parameterOptions(definition) {
|
|
8104
8141
|
return {
|
|
8105
8142
|
...definition.label ? { label: definition.label } : {},
|
|
8143
|
+
...definition.expression ? { defaultExpression: definition.expression } : {},
|
|
8106
8144
|
...definition.min === void 0 ? {} : { min: definition.min },
|
|
8107
8145
|
...definition.max === void 0 ? {} : { max: definition.max },
|
|
8108
8146
|
...definition.step === void 0 ? {} : { step: definition.step },
|
|
@@ -8112,6 +8150,9 @@ function parameterOptions(definition) {
|
|
|
8112
8150
|
promotable: false
|
|
8113
8151
|
};
|
|
8114
8152
|
}
|
|
8153
|
+
function asGpuHost(value) {
|
|
8154
|
+
return value && typeof value.ensure === "function" && typeof value.forNode === "function" ? value : null;
|
|
8155
|
+
}
|
|
8115
8156
|
function attachDefinition(node, registration, capabilities) {
|
|
8116
8157
|
const definition = registration.definition;
|
|
8117
8158
|
const pendingTriggers = /* @__PURE__ */ new Map();
|
|
@@ -8186,12 +8227,18 @@ function attachDefinition(node, registration, capabilities) {
|
|
|
8186
8227
|
if (!port) throw new Error(`Missing output ${registration.moduleId}.${name}`);
|
|
8187
8228
|
return [name, output.kind === "trigger" ? { trigger: (payload) => port.trigger(payload) } : { set: (value) => port.setValue(value) }];
|
|
8188
8229
|
}));
|
|
8230
|
+
let nodeCapabilities = capabilities;
|
|
8231
|
+
const gpuHost = (definition.capabilities ?? []).includes("gpu") ? asGpuHost(capabilities.gpu) : null;
|
|
8232
|
+
if (gpuHost) {
|
|
8233
|
+
await gpuHost.ensure();
|
|
8234
|
+
nodeCapabilities = { ...capabilities, gpu: gpuHost.forNode(node.id) };
|
|
8235
|
+
}
|
|
8189
8236
|
const context = {
|
|
8190
8237
|
nodeId: node.id,
|
|
8191
8238
|
inputs: inputValues,
|
|
8192
8239
|
outputs,
|
|
8193
8240
|
props: Object.fromEntries(node.parameters.map((parameter) => [parameter.name, parameter.value])),
|
|
8194
|
-
capabilities,
|
|
8241
|
+
capabilities: nodeCapabilities,
|
|
8195
8242
|
signal: new AbortController().signal,
|
|
8196
8243
|
progress: silentProgress
|
|
8197
8244
|
};
|
|
@@ -8230,8 +8277,140 @@ var browserAssetCapability = {
|
|
|
8230
8277
|
}
|
|
8231
8278
|
};
|
|
8232
8279
|
|
|
8280
|
+
// src/nodes/definition/gpuCapability.ts
|
|
8281
|
+
var PAGE_SCOPE = "\0page";
|
|
8282
|
+
var MISSING_DEVICE = "The gpu capability was used before its device was ready \u2014 the host must await ensure() before a cook.";
|
|
8283
|
+
function createStudioGpuCapability() {
|
|
8284
|
+
const gpu = globalThis.navigator?.gpu;
|
|
8285
|
+
if (!gpu) return void 0;
|
|
8286
|
+
let device = null;
|
|
8287
|
+
let adapterInfo = null;
|
|
8288
|
+
let limits = {};
|
|
8289
|
+
let starting = null;
|
|
8290
|
+
let destroyed = false;
|
|
8291
|
+
const caches = /* @__PURE__ */ new Map();
|
|
8292
|
+
function dropCaches() {
|
|
8293
|
+
for (const entries of caches.values()) {
|
|
8294
|
+
for (const entry of entries.values()) entry.destroy?.(entry.value);
|
|
8295
|
+
}
|
|
8296
|
+
caches.clear();
|
|
8297
|
+
}
|
|
8298
|
+
async function start() {
|
|
8299
|
+
const adapter = await gpu.requestAdapter();
|
|
8300
|
+
if (!adapter) {
|
|
8301
|
+
throw new Error("No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on.");
|
|
8302
|
+
}
|
|
8303
|
+
const next = await adapter.requestDevice();
|
|
8304
|
+
const info = adapter.info;
|
|
8305
|
+
device = next;
|
|
8306
|
+
adapterInfo = {
|
|
8307
|
+
vendor: info?.vendor ?? "",
|
|
8308
|
+
architecture: info?.architecture ?? "",
|
|
8309
|
+
device: info?.device ?? "",
|
|
8310
|
+
description: info?.description ?? ""
|
|
8311
|
+
};
|
|
8312
|
+
limits = readLimits(next.limits ?? adapter.limits);
|
|
8313
|
+
void next.lost.then((info2) => {
|
|
8314
|
+
if (destroyed || info2?.reason === "destroyed") return;
|
|
8315
|
+
device = null;
|
|
8316
|
+
starting = null;
|
|
8317
|
+
dropCaches();
|
|
8318
|
+
console.warn(`The GPU device was lost (${info2?.reason ?? "unknown"}) \u2014 ${info2?.message || "no message given"}. A fresh device will be requested on the next cook.`);
|
|
8319
|
+
});
|
|
8320
|
+
}
|
|
8321
|
+
const host = {
|
|
8322
|
+
get device() {
|
|
8323
|
+
if (!device) throw new Error(MISSING_DEVICE);
|
|
8324
|
+
return device;
|
|
8325
|
+
},
|
|
8326
|
+
get adapterInfo() {
|
|
8327
|
+
if (!adapterInfo) throw new Error(MISSING_DEVICE);
|
|
8328
|
+
return adapterInfo;
|
|
8329
|
+
},
|
|
8330
|
+
get limits() {
|
|
8331
|
+
return limits;
|
|
8332
|
+
},
|
|
8333
|
+
cache(key2, create, destroy) {
|
|
8334
|
+
return host.forNode(PAGE_SCOPE).cache(key2, create, destroy);
|
|
8335
|
+
},
|
|
8336
|
+
async ensure() {
|
|
8337
|
+
if (destroyed) throw new Error("The gpu capability has been disposed.");
|
|
8338
|
+
if (device) return;
|
|
8339
|
+
starting ??= start().catch((error) => {
|
|
8340
|
+
starting = null;
|
|
8341
|
+
throw error;
|
|
8342
|
+
});
|
|
8343
|
+
await starting;
|
|
8344
|
+
},
|
|
8345
|
+
forNode(nodeId) {
|
|
8346
|
+
return {
|
|
8347
|
+
get device() {
|
|
8348
|
+
if (!device) throw new Error(MISSING_DEVICE);
|
|
8349
|
+
return device;
|
|
8350
|
+
},
|
|
8351
|
+
get adapterInfo() {
|
|
8352
|
+
if (!adapterInfo) throw new Error(MISSING_DEVICE);
|
|
8353
|
+
return adapterInfo;
|
|
8354
|
+
},
|
|
8355
|
+
get limits() {
|
|
8356
|
+
return limits;
|
|
8357
|
+
},
|
|
8358
|
+
cache(key2, create, destroy) {
|
|
8359
|
+
if (!device) throw new Error(MISSING_DEVICE);
|
|
8360
|
+
let entries = caches.get(nodeId);
|
|
8361
|
+
if (!entries) {
|
|
8362
|
+
entries = /* @__PURE__ */ new Map();
|
|
8363
|
+
caches.set(nodeId, entries);
|
|
8364
|
+
}
|
|
8365
|
+
const existing = entries.get(key2);
|
|
8366
|
+
if (existing) return existing.value;
|
|
8367
|
+
const value = create(device);
|
|
8368
|
+
entries.set(key2, { value, destroy });
|
|
8369
|
+
return value;
|
|
8370
|
+
}
|
|
8371
|
+
};
|
|
8372
|
+
},
|
|
8373
|
+
async dispose() {
|
|
8374
|
+
destroyed = true;
|
|
8375
|
+
dropCaches();
|
|
8376
|
+
device?.destroy();
|
|
8377
|
+
device = null;
|
|
8378
|
+
starting = null;
|
|
8379
|
+
}
|
|
8380
|
+
};
|
|
8381
|
+
return host;
|
|
8382
|
+
}
|
|
8383
|
+
function readLimits(source) {
|
|
8384
|
+
if (!source || typeof source !== "object") return {};
|
|
8385
|
+
const out = {};
|
|
8386
|
+
for (const key2 of limitNames(source)) {
|
|
8387
|
+
const value = source[key2];
|
|
8388
|
+
if (typeof value === "number") out[key2] = value;
|
|
8389
|
+
}
|
|
8390
|
+
return out;
|
|
8391
|
+
}
|
|
8392
|
+
function limitNames(source) {
|
|
8393
|
+
const names = new Set(Object.keys(source));
|
|
8394
|
+
for (let cursor = Object.getPrototypeOf(source); cursor && cursor !== Object.prototype; cursor = Object.getPrototypeOf(cursor)) {
|
|
8395
|
+
for (const key2 of Object.getOwnPropertyNames(cursor)) if (key2 !== "constructor") names.add(key2);
|
|
8396
|
+
}
|
|
8397
|
+
return [...names];
|
|
8398
|
+
}
|
|
8399
|
+
|
|
8233
8400
|
// src/nodes/definition/projectDefinition.ts
|
|
8234
|
-
var studioCapabilities = {
|
|
8401
|
+
var studioCapabilities = {
|
|
8402
|
+
assets: browserAssetCapability,
|
|
8403
|
+
// Installed only where the browser has WebGPU, and absent rather than
|
|
8404
|
+
// stubbed otherwise. That is what makes the failure a sentence: the preflight
|
|
8405
|
+
// reports `runtime/missing-capability` and the adapter throws "requires the
|
|
8406
|
+
// Studio gpu capability", both naming what is missing. A stub that threw on
|
|
8407
|
+
// first use would name nothing.
|
|
8408
|
+
...gpuCapabilityIfAvailable()
|
|
8409
|
+
};
|
|
8410
|
+
function gpuCapabilityIfAvailable() {
|
|
8411
|
+
const gpu = createStudioGpuCapability();
|
|
8412
|
+
return gpu ? { gpu } : {};
|
|
8413
|
+
}
|
|
8235
8414
|
function declaresDefinitionV1(value) {
|
|
8236
8415
|
return typeof value === "object" && value !== null && value.apiVersion === 1;
|
|
8237
8416
|
}
|
|
@@ -9429,7 +9608,9 @@ var Graph = class _Graph {
|
|
|
9429
9608
|
result.parent = node.parent.id;
|
|
9430
9609
|
}
|
|
9431
9610
|
const rawParameterValue = (p) => node.rawParameterValue(p.name);
|
|
9432
|
-
const changedParameters = (node.parameters ?? []).filter(
|
|
9611
|
+
const changedParameters = (node.parameters ?? []).filter(
|
|
9612
|
+
(p) => p.promoted || node.preservesPlainProp(p.name) || JSON.stringify(rawParameterValue(p)) !== JSON.stringify(p.defaultValue)
|
|
9613
|
+
);
|
|
9433
9614
|
const parameters = changedParameters.filter((p) => p.documentField !== "props").map((p) => p.promoted ? { name: p.name, value: rawParameterValue(p), promoted: true } : { name: p.name, value: rawParameterValue(p) });
|
|
9434
9615
|
if (parameters.length > 0) {
|
|
9435
9616
|
result.params = parameters;
|
|
@@ -9644,6 +9825,12 @@ var Graph = class _Graph {
|
|
|
9644
9825
|
}
|
|
9645
9826
|
if (expression && node.props[key2]) {
|
|
9646
9827
|
node.props[key2].expression = expression;
|
|
9828
|
+
} else {
|
|
9829
|
+
node.preservePlainProp(key2);
|
|
9830
|
+
}
|
|
9831
|
+
if (!expression && node.props[key2]?.expression) {
|
|
9832
|
+
delete node.props[key2].expression;
|
|
9833
|
+
node.props[key2] = { ...node.props[key2] };
|
|
9647
9834
|
}
|
|
9648
9835
|
if (propData?.channel && node.props[key2]) {
|
|
9649
9836
|
node.restorePropChannel(key2, propData.channel);
|
|
@@ -11411,6 +11598,9 @@ var PREFLIGHT_WARNING_CODES = /* @__PURE__ */ new Set([
|
|
|
11411
11598
|
"runtime/preflight-environment",
|
|
11412
11599
|
"runtime/stray-params"
|
|
11413
11600
|
]);
|
|
11601
|
+
var RUN_TOLERATED_CODES = /* @__PURE__ */ new Set([
|
|
11602
|
+
"runtime/stray-params"
|
|
11603
|
+
]);
|
|
11414
11604
|
var MutableAbortSignal = class {
|
|
11415
11605
|
constructor(report) {
|
|
11416
11606
|
this.report = report;
|
|
@@ -11559,6 +11749,46 @@ var Graph2 = class {
|
|
|
11559
11749
|
this.frame = MIN_FRAME;
|
|
11560
11750
|
this.fps = DEFAULT_FPS;
|
|
11561
11751
|
this.byId = new Map(nodes.map((node) => [node.id, node]));
|
|
11752
|
+
const byInput = /* @__PURE__ */ new Map();
|
|
11753
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
11754
|
+
const connectedInputs = /* @__PURE__ */ new Set();
|
|
11755
|
+
const connectedOutputs = /* @__PURE__ */ new Set();
|
|
11756
|
+
for (const connection of connections) {
|
|
11757
|
+
connectedInputs.add(
|
|
11758
|
+
`${connection.target.nodeId}\0${connection.target.inputName}`
|
|
11759
|
+
);
|
|
11760
|
+
connectedOutputs.add(
|
|
11761
|
+
`${connection.source.nodeId}\0${connection.source.outputName}`
|
|
11762
|
+
);
|
|
11763
|
+
if (connection.kind !== "data") continue;
|
|
11764
|
+
const inputKey = `${connection.target.nodeId}\0${connection.target.inputName}`;
|
|
11765
|
+
const inputConnections = byInput.get(inputKey) ?? [];
|
|
11766
|
+
inputConnections.push(connection);
|
|
11767
|
+
byInput.set(inputKey, inputConnections);
|
|
11768
|
+
const nodeConnections = byNode.get(connection.target.nodeId) ?? [];
|
|
11769
|
+
nodeConnections.push(connection);
|
|
11770
|
+
byNode.set(connection.target.nodeId, nodeConnections);
|
|
11771
|
+
}
|
|
11772
|
+
this.dataConnectionsByInput = new Map(
|
|
11773
|
+
[...byInput].map(([key2, inputConnections]) => [
|
|
11774
|
+
key2,
|
|
11775
|
+
Object.freeze(
|
|
11776
|
+
inputConnections.sort(
|
|
11777
|
+
(first, second) => (first.variadicIndex ?? first.order) - (second.variadicIndex ?? second.order)
|
|
11778
|
+
)
|
|
11779
|
+
)
|
|
11780
|
+
])
|
|
11781
|
+
);
|
|
11782
|
+
this.dataConnectionsByNode = new Map(
|
|
11783
|
+
[...byNode].map(([key2, nodeConnections]) => [
|
|
11784
|
+
key2,
|
|
11785
|
+
Object.freeze(
|
|
11786
|
+
nodeConnections.sort((first, second) => first.order - second.order)
|
|
11787
|
+
)
|
|
11788
|
+
])
|
|
11789
|
+
);
|
|
11790
|
+
this.connectedInputs = connectedInputs;
|
|
11791
|
+
this.connectedOutputs = connectedOutputs;
|
|
11562
11792
|
this.graphInputs = graphBoundaries(nodes, "cascade.core.Input", "inputName");
|
|
11563
11793
|
this.graphOutputs = graphBoundaries(nodes, "cascade.core.Output", "outputName");
|
|
11564
11794
|
this.authoredAnnotations = Object.freeze(
|
|
@@ -11568,9 +11798,7 @@ var Graph2 = class {
|
|
|
11568
11798
|
nodes,
|
|
11569
11799
|
node: (id) => this.byId.get(id),
|
|
11570
11800
|
inputNode: (node, index) => {
|
|
11571
|
-
const feeding = this.
|
|
11572
|
-
(connection) => connection.kind === "data" && connection.target.nodeId === node.id
|
|
11573
|
-
).sort((a, b) => a.order - b.order);
|
|
11801
|
+
const feeding = this.dataConnectionsByNode.get(node.id) ?? [];
|
|
11574
11802
|
const source = feeding[index]?.source.nodeId;
|
|
11575
11803
|
return (source ? this.byId.get(source) : void 0) ?? null;
|
|
11576
11804
|
},
|
|
@@ -11614,9 +11842,7 @@ var Graph2 = class {
|
|
|
11614
11842
|
{
|
|
11615
11843
|
kind: definition.kind,
|
|
11616
11844
|
type: definition.kind === "trigger" ? "trigger" : definition.type,
|
|
11617
|
-
connected: this.
|
|
11618
|
-
(connection) => connection.target.nodeId === node.id && connection.target.inputName === name
|
|
11619
|
-
),
|
|
11845
|
+
connected: this.connectedInputs.has(`${node.id}\0${name}`),
|
|
11620
11846
|
...definition.kind === "data" ? { value: snapshot(node.inputs[name]) } : {}
|
|
11621
11847
|
}
|
|
11622
11848
|
]
|
|
@@ -11629,9 +11855,7 @@ var Graph2 = class {
|
|
|
11629
11855
|
{
|
|
11630
11856
|
kind: definition.kind,
|
|
11631
11857
|
type: definition.kind === "trigger" ? "trigger" : definition.type,
|
|
11632
|
-
connected: this.
|
|
11633
|
-
(connection) => connection.source.nodeId === node.id && connection.source.outputName === name
|
|
11634
|
-
),
|
|
11858
|
+
connected: this.connectedOutputs.has(`${node.id}\0${name}`),
|
|
11635
11859
|
...definition.kind === "data" && node.outputs.has(name) ? { value: snapshot(node.outputs.get(name)) } : {}
|
|
11636
11860
|
}
|
|
11637
11861
|
]
|
|
@@ -11981,11 +12205,7 @@ var Graph2 = class {
|
|
|
11981
12205
|
const index = integerProp(node.props.inputIndex);
|
|
11982
12206
|
return [name, this.dataInputValue(parent, `input_${index}`)];
|
|
11983
12207
|
}
|
|
11984
|
-
const connections = this.
|
|
11985
|
-
(item) => item.kind === "data" && item.target.nodeId === node.id && item.target.inputName === name
|
|
11986
|
-
).sort(
|
|
11987
|
-
(first, second) => (first.variadicIndex ?? first.order) - (second.variadicIndex ?? second.order)
|
|
11988
|
-
);
|
|
12208
|
+
const connections = this.dataConnectionsByInput.get(`${node.id}\0${name}`) ?? [];
|
|
11989
12209
|
if (definition.variadic) {
|
|
11990
12210
|
const values = connections.map(
|
|
11991
12211
|
(connection2) => this.requireNode(connection2.source.nodeId).outputs.get(
|
|
@@ -12215,7 +12435,7 @@ var Graph2 = class {
|
|
|
12215
12435
|
}
|
|
12216
12436
|
assertPreflight(nodes) {
|
|
12217
12437
|
const [first] = this.preflightDiagnostics(nodes).filter(
|
|
12218
|
-
(diagnostic2) => !
|
|
12438
|
+
(diagnostic2) => !RUN_TOLERATED_CODES.has(diagnostic2.code)
|
|
12219
12439
|
);
|
|
12220
12440
|
if (first) throw new CascadeRuntimeError(first.code, [first]);
|
|
12221
12441
|
}
|
|
@@ -12229,8 +12449,9 @@ var Graph2 = class {
|
|
|
12229
12449
|
}
|
|
12230
12450
|
runSelection(target) {
|
|
12231
12451
|
if (target.kind === "all") {
|
|
12452
|
+
const gatedDataDescendants = this.gatedDataDescendants();
|
|
12232
12453
|
const initial = this.nodes.filter(
|
|
12233
|
-
(node) => !isGated(node) && !
|
|
12454
|
+
(node) => !isGated(node) && !gatedDataDescendants.has(node.id)
|
|
12234
12455
|
);
|
|
12235
12456
|
const ids = new Set(initial.map((node) => node.id));
|
|
12236
12457
|
let changed = true;
|
|
@@ -12278,8 +12499,24 @@ var Graph2 = class {
|
|
|
12278
12499
|
}
|
|
12279
12500
|
return this.nodes.filter((node) => ids.has(node.id));
|
|
12280
12501
|
}
|
|
12281
|
-
|
|
12282
|
-
|
|
12502
|
+
gatedDataDescendants() {
|
|
12503
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
12504
|
+
for (const dependency of dataDependencies(this.nodes, this.connections)) {
|
|
12505
|
+
const targets = outgoing.get(dependency.sourceId) ?? [];
|
|
12506
|
+
targets.push(dependency.targetId);
|
|
12507
|
+
outgoing.set(dependency.sourceId, targets);
|
|
12508
|
+
}
|
|
12509
|
+
const queue = this.nodes.filter(isGated).map((node) => node.id);
|
|
12510
|
+
const descendants = new Set(queue);
|
|
12511
|
+
let queueIndex = 0;
|
|
12512
|
+
while (queueIndex < queue.length) {
|
|
12513
|
+
for (const targetId of outgoing.get(queue[queueIndex++]) ?? []) {
|
|
12514
|
+
if (descendants.has(targetId)) continue;
|
|
12515
|
+
descendants.add(targetId);
|
|
12516
|
+
queue.push(targetId);
|
|
12517
|
+
}
|
|
12518
|
+
}
|
|
12519
|
+
return descendants;
|
|
12283
12520
|
}
|
|
12284
12521
|
triggerDataAncestors(nodeId) {
|
|
12285
12522
|
const dependencies = this.dataClosure(nodeId).filter(
|
|
@@ -12455,7 +12692,17 @@ function materialize(authored, registration) {
|
|
|
12455
12692
|
`${authored.id}.${name}`
|
|
12456
12693
|
);
|
|
12457
12694
|
const bindings = {};
|
|
12458
|
-
|
|
12695
|
+
const storedProps = authored.props ?? {};
|
|
12696
|
+
for (const [name, definition] of Object.entries(
|
|
12697
|
+
registration.definition.props ?? {}
|
|
12698
|
+
)) {
|
|
12699
|
+
if (!definition.expression || name in storedProps) continue;
|
|
12700
|
+
bindings[name] = {
|
|
12701
|
+
value: props[name],
|
|
12702
|
+
expression: definition.expression
|
|
12703
|
+
};
|
|
12704
|
+
}
|
|
12705
|
+
for (const [name, saved] of Object.entries(storedProps)) {
|
|
12459
12706
|
const definition = registration.definition.props?.[name];
|
|
12460
12707
|
if (!definition) continue;
|
|
12461
12708
|
const object = saved && typeof saved === "object" && "value" in saved ? saved : null;
|
|
@@ -12466,7 +12713,10 @@ function materialize(authored, registration) {
|
|
|
12466
12713
|
`${authored.id}.${name}`
|
|
12467
12714
|
);
|
|
12468
12715
|
props[name] = value;
|
|
12469
|
-
if (!object)
|
|
12716
|
+
if (!object) {
|
|
12717
|
+
delete bindings[name];
|
|
12718
|
+
continue;
|
|
12719
|
+
}
|
|
12470
12720
|
const channel = deserializeChannel(object.channel);
|
|
12471
12721
|
const expression = typeof object.expression === "string" && object.expression ? object.expression : void 0;
|
|
12472
12722
|
const binding = {
|
|
@@ -12727,6 +12977,7 @@ function validateTopology(nodes, connections) {
|
|
|
12727
12977
|
topological(nodes, connections);
|
|
12728
12978
|
}
|
|
12729
12979
|
function topological(nodes, connections) {
|
|
12980
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
12730
12981
|
const indegree = new Map(nodes.map((node) => [node.id, 0]));
|
|
12731
12982
|
const outgoing = new Map(nodes.map((node) => [node.id, []]));
|
|
12732
12983
|
for (const dependency of graphDependencies(nodes, connections)) {
|
|
@@ -12738,13 +12989,14 @@ function topological(nodes, connections) {
|
|
|
12738
12989
|
}
|
|
12739
12990
|
const queue = nodes.filter((node) => indegree.get(node.id) === 0);
|
|
12740
12991
|
const result = [];
|
|
12741
|
-
|
|
12742
|
-
|
|
12992
|
+
let queueIndex = 0;
|
|
12993
|
+
while (queueIndex < queue.length) {
|
|
12994
|
+
const node = queue[queueIndex++];
|
|
12743
12995
|
result.push(node);
|
|
12744
12996
|
for (const id of outgoing.get(node.id)) {
|
|
12745
12997
|
indegree.set(id, indegree.get(id) - 1);
|
|
12746
12998
|
if (indegree.get(id) === 0)
|
|
12747
|
-
queue.push(
|
|
12999
|
+
queue.push(byId.get(id));
|
|
12748
13000
|
}
|
|
12749
13001
|
}
|
|
12750
13002
|
if (result.length !== nodes.length)
|
|
@@ -13163,6 +13415,15 @@ async function preflightAgainstRunHost(file, registrations, document2) {
|
|
|
13163
13415
|
}
|
|
13164
13416
|
if (!diagnostics.length) return;
|
|
13165
13417
|
const { errors, warnings } = classifyPreflight(diagnostics);
|
|
13418
|
+
reportPreflightWarnings(host, warnings);
|
|
13419
|
+
if (!errors.length) return;
|
|
13420
|
+
throw new Error([
|
|
13421
|
+
`This graph cannot run on the ${host.environment} host that "cascade run" uses:`,
|
|
13422
|
+
...errors.map(line),
|
|
13423
|
+
...capabilityAdvice(host, errors)
|
|
13424
|
+
].join("\n"));
|
|
13425
|
+
}
|
|
13426
|
+
function reportPreflightWarnings(host, warnings) {
|
|
13166
13427
|
const elsewhere = warnings.filter((item) => item.code === "runtime/preflight-environment");
|
|
13167
13428
|
const lossy = warnings.filter((item) => item.code === "runtime/stray-params");
|
|
13168
13429
|
if (elsewhere.length) {
|
|
@@ -13179,12 +13440,6 @@ async function preflightAgainstRunHost(file, registrations, document2) {
|
|
|
13179
13440
|
` This graph will run, on the defaults, and look like it worked.`
|
|
13180
13441
|
].join("\n"));
|
|
13181
13442
|
}
|
|
13182
|
-
if (!errors.length) return;
|
|
13183
|
-
throw new Error([
|
|
13184
|
-
`This graph cannot run on the ${host.environment} host that "cascade run" uses:`,
|
|
13185
|
-
...errors.map(line),
|
|
13186
|
-
...capabilityAdvice(host, errors)
|
|
13187
|
-
].join("\n"));
|
|
13188
13443
|
}
|
|
13189
13444
|
function line(item) {
|
|
13190
13445
|
return ` ${item.code}${item.path ? ` [${item.path}]` : ""}: ${item.message}`;
|
|
@@ -13242,21 +13497,31 @@ async function runDeterministicProjectGraph(file, document2, entryNode) {
|
|
|
13242
13497
|
if (deterministicCount !== nodes.length) {
|
|
13243
13498
|
throw new Error("Mixed deterministic and dynamic graphs are not executable until the bounded legacy adapter is implemented");
|
|
13244
13499
|
}
|
|
13500
|
+
const host = createProjectHost(file, prepared.registrations);
|
|
13245
13501
|
const runtime = createRuntime({
|
|
13246
|
-
host
|
|
13502
|
+
host,
|
|
13247
13503
|
nodes: prepared.registrations
|
|
13248
13504
|
});
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13505
|
+
let graph;
|
|
13506
|
+
try {
|
|
13507
|
+
graph = await runtime.load(document2);
|
|
13508
|
+
reportPreflightWarnings(
|
|
13509
|
+
host,
|
|
13510
|
+
classifyPreflight(graph.preflight()).warnings.filter((item) => item.code === "runtime/stray-params")
|
|
13511
|
+
);
|
|
13512
|
+
const result = await graph.run(entryNode ? { target: { kind: "node", nodeId: entryNode } } : {});
|
|
13513
|
+
if (result.status !== "completed") {
|
|
13514
|
+
throw new Error(result.diagnostics.map((item) => item.message).join("\n") || `Graph ${result.status}`);
|
|
13515
|
+
}
|
|
13516
|
+
return true;
|
|
13517
|
+
} finally {
|
|
13518
|
+
await graph?.dispose();
|
|
13519
|
+
await runtime.dispose();
|
|
13255
13520
|
}
|
|
13256
|
-
return true;
|
|
13257
13521
|
}
|
|
13258
13522
|
async function renderDeterministicProjectFrames(file, document2, options) {
|
|
13259
13523
|
const prepared = await prepare(file, document2, true);
|
|
13524
|
+
reportUnresolved(prepared);
|
|
13260
13525
|
const nodes = Array.isArray(document2?.nodes) ? document2.nodes : [];
|
|
13261
13526
|
const deterministicCount = nodes.filter((node) => prepared.deterministic.has(moduleId(node))).length;
|
|
13262
13527
|
if (!nodes.length || deterministicCount === 0) return null;
|
|
@@ -13264,11 +13529,13 @@ async function renderDeterministicProjectFrames(file, document2, options) {
|
|
|
13264
13529
|
throw new Error("Mixed deterministic and dynamic graphs are not executable until the bounded legacy adapter is implemented");
|
|
13265
13530
|
}
|
|
13266
13531
|
const project = new ProjectRoot(path9.dirname(path9.resolve(file)));
|
|
13532
|
+
const host = createProjectHost(file, prepared.registrations);
|
|
13267
13533
|
const runtime = createRuntime({
|
|
13268
|
-
host
|
|
13534
|
+
host,
|
|
13269
13535
|
nodes: prepared.registrations
|
|
13270
13536
|
});
|
|
13271
13537
|
const graph = await runtime.load(document2);
|
|
13538
|
+
reportPreflightWarnings(host, classifyPreflight(graph.preflight()).warnings);
|
|
13272
13539
|
const outDirectory = project.resolve(options.out);
|
|
13273
13540
|
await fs7.mkdir(outDirectory, { recursive: true });
|
|
13274
13541
|
const width = sequenceWidth(options.end);
|
|
@@ -13328,6 +13595,7 @@ function imageOutputs(graph, entryNode) {
|
|
|
13328
13595
|
}
|
|
13329
13596
|
async function inspectProjectGraph(file, document2) {
|
|
13330
13597
|
const prepared = await prepare(file, document2, false);
|
|
13598
|
+
reportUnresolved(prepared);
|
|
13331
13599
|
const registrations = new Map(
|
|
13332
13600
|
[...builtinNodeRegistrations, ...prepared.registrations].map((item) => [item.moduleId, item])
|
|
13333
13601
|
);
|
|
@@ -13422,7 +13690,7 @@ function unresolvedModule(id, node, tried, root) {
|
|
|
13422
13690
|
phase: "prepare",
|
|
13423
13691
|
code: "project/module-not-found",
|
|
13424
13692
|
path: node.id,
|
|
13425
|
-
message: `${id} has no module file \u2014 ${where}.
|
|
13693
|
+
message: `${id} has no module file \u2014 ${where}. Only known legacy modules can use saved-port compatibility, for inspection only; unknown and explicit project modules are rejected`
|
|
13426
13694
|
};
|
|
13427
13695
|
}
|
|
13428
13696
|
async function compileExecute(projectRoot, moduleFile) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cascade/gpu` — WebGPU's flag namespaces as plain values.
|
|
3
|
+
*
|
|
4
|
+
* Small, and it was load-bearing. TypeScript's DOM library declares the WebGPU
|
|
5
|
+
* *interfaces* but not the constant objects, so the only way to reach
|
|
6
|
+
* `GPUTextureUsage.RENDER_ATTACHMENT` was off `globalThis` — which
|
|
7
|
+
* `architecture/ambient-state` correctly forbids inside a node module, because
|
|
8
|
+
* an ambient read makes a node's inputs invisible to the static reader that
|
|
9
|
+
* lets `cascade check` say anything without cooking.
|
|
10
|
+
*
|
|
11
|
+
* So a sketch had to choose between a rule and a working shader, and it chose
|
|
12
|
+
* a project library file to hide the read in. Both stay honest now: the rule
|
|
13
|
+
* keeps its teeth and a node imports these instead. That is the whole reason
|
|
14
|
+
* this file exists, and it is why it had to land with the capability rather
|
|
15
|
+
* than after it — a rule whose only workaround is removed at the same moment
|
|
16
|
+
* is a rule; one enforced a release earlier is a nuisance.
|
|
17
|
+
*
|
|
18
|
+
* The values are fixed by the WebGPU specification, not by an implementation,
|
|
19
|
+
* which is what makes writing them down safe rather than a guess. They are
|
|
20
|
+
* checked against a real browser's own globals in `tests/gpu-usage-flags.test.ts`
|
|
21
|
+
* — the numbers below were verified against Chrome rather than recalled.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** `GPUBufferUsage`. */
|
|
25
|
+
export const BufferUsage = {
|
|
26
|
+
MAP_READ: 0x0001,
|
|
27
|
+
MAP_WRITE: 0x0002,
|
|
28
|
+
COPY_SRC: 0x0004,
|
|
29
|
+
COPY_DST: 0x0008,
|
|
30
|
+
INDEX: 0x0010,
|
|
31
|
+
VERTEX: 0x0020,
|
|
32
|
+
UNIFORM: 0x0040,
|
|
33
|
+
STORAGE: 0x0080,
|
|
34
|
+
INDIRECT: 0x0100,
|
|
35
|
+
QUERY_RESOLVE: 0x0200,
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
/** `GPUTextureUsage`. */
|
|
39
|
+
export const TextureUsage = {
|
|
40
|
+
COPY_SRC: 0x01,
|
|
41
|
+
COPY_DST: 0x02,
|
|
42
|
+
TEXTURE_BINDING: 0x04,
|
|
43
|
+
STORAGE_BINDING: 0x08,
|
|
44
|
+
RENDER_ATTACHMENT: 0x10,
|
|
45
|
+
/**
|
|
46
|
+
* Chrome 147 has this and it is newer than the rest — verified present
|
|
47
|
+
* 2026-09-08 alongside the others, but do not assume every implementation
|
|
48
|
+
* carries it. Included rather than omitted deliberately: leaving it out
|
|
49
|
+
* would send a node that wants it back to `globalThis`, which is the exact
|
|
50
|
+
* ambient read this file exists to remove, so an honest constant with a
|
|
51
|
+
* caveat beats an omission that forces a rule violation.
|
|
52
|
+
*/
|
|
53
|
+
TRANSIENT_ATTACHMENT: 0x20,
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
/** `GPUShaderStage`, for a bind group layout entry's `visibility`. */
|
|
57
|
+
export const ShaderStage = {
|
|
58
|
+
VERTEX: 0x1,
|
|
59
|
+
FRAGMENT: 0x2,
|
|
60
|
+
COMPUTE: 0x4,
|
|
61
|
+
} as const;
|
|
62
|
+
|
|
63
|
+
/** `GPUMapMode`, for `buffer.mapAsync`. */
|
|
64
|
+
export const MapMode = {
|
|
65
|
+
READ: 0x1,
|
|
66
|
+
WRITE: 0x2,
|
|
67
|
+
} as const;
|
|
68
|
+
|
|
69
|
+
/** `GPUColorWrite`, for a colour target's `writeMask`. */
|
|
70
|
+
export const ColorWrite = {
|
|
71
|
+
RED: 0x1,
|
|
72
|
+
GREEN: 0x2,
|
|
73
|
+
BLUE: 0x4,
|
|
74
|
+
ALPHA: 0x8,
|
|
75
|
+
ALL: 0xf,
|
|
76
|
+
} as const;
|