@nodegx/preview 0.2.7 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/cli.js +716 -495
- package/dist/browser/noodl-graph-target.js +92 -92
- package/dist/browser/noodl-preview-in-page.js +32 -29
- package/dist/external/deploy/noodl.deploy.js +1 -1
- package/dist/nodegx-deploy.cjs +566 -58
- package/dist/nodegx-deploy.inputs.json +5 -0
- package/dist/noodl-preview.cjs +656 -86
- package/dist/noodl-preview.inputs.json +4 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/noodl-preview.cjs
CHANGED
|
@@ -13786,7 +13786,7 @@ var require_common2 = __commonJS({
|
|
|
13786
13786
|
exports2.ownProp = ownProp;
|
|
13787
13787
|
exports2.makeAbs = makeAbs;
|
|
13788
13788
|
exports2.finish = finish2;
|
|
13789
|
-
exports2.mark =
|
|
13789
|
+
exports2.mark = mark2;
|
|
13790
13790
|
exports2.isIgnored = isIgnored;
|
|
13791
13791
|
exports2.childrenIgnored = childrenIgnored;
|
|
13792
13792
|
function ownProp(obj, field) {
|
|
@@ -13922,7 +13922,7 @@ var require_common2 = __commonJS({
|
|
|
13922
13922
|
});
|
|
13923
13923
|
self2.found = all;
|
|
13924
13924
|
}
|
|
13925
|
-
function
|
|
13925
|
+
function mark2(self2, p) {
|
|
13926
13926
|
var abs = makeAbs(self2, p);
|
|
13927
13927
|
var c = self2.cache[abs];
|
|
13928
13928
|
var m = p;
|
|
@@ -19380,10 +19380,10 @@ var require_nodescope = __commonJS({
|
|
|
19380
19380
|
} else {
|
|
19381
19381
|
const parameters = nodeModel.parameters;
|
|
19382
19382
|
var inputNames = Object.keys(parameters);
|
|
19383
|
-
const
|
|
19383
|
+
const RUN_ON_CHANGE_PREFIX5 = "runOnChange-";
|
|
19384
19384
|
const metadata = this.context.nodeRegister.hasNode(node.name) ? this.context.nodeRegister.getNodeMetadata(node.name) : void 0;
|
|
19385
19385
|
const priorityOf = function(name2) {
|
|
19386
|
-
if (name2.indexOf(
|
|
19386
|
+
if (name2.indexOf(RUN_ON_CHANGE_PREFIX5) === 0) return Number.MAX_SAFE_INTEGER;
|
|
19387
19387
|
const input2 = metadata ? metadata.inputs[name2] : void 0;
|
|
19388
19388
|
return input2 ? input2.inputPriority : 0;
|
|
19389
19389
|
};
|
|
@@ -21345,11 +21345,11 @@ var require_editorconnection = __commonJS({
|
|
|
21345
21345
|
});
|
|
21346
21346
|
};
|
|
21347
21347
|
var serviceRequests = {};
|
|
21348
|
-
EditorConnection.prototype.sendServiceRequest = function(
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
serviceRequests[
|
|
21352
|
-
this.send(
|
|
21348
|
+
EditorConnection.prototype.sendServiceRequest = function(request2, callback) {
|
|
21349
|
+
request2.token = guid4();
|
|
21350
|
+
request2.clientId = this.clientId;
|
|
21351
|
+
serviceRequests[request2.token] = callback;
|
|
21352
|
+
this.send(request2);
|
|
21353
21353
|
};
|
|
21354
21354
|
EditorConnection.prototype.close = function() {
|
|
21355
21355
|
this.reconnectOnClose = false;
|
|
@@ -23328,7 +23328,10 @@ var require_nodedefinition = __commonJS({
|
|
|
23328
23328
|
node.registerOutput(name2, {
|
|
23329
23329
|
getter: function() {
|
|
23330
23330
|
return void 0;
|
|
23331
|
-
}
|
|
23331
|
+
},
|
|
23332
|
+
onFirstConnectionAdded: output.onFirstConnectionAdded,
|
|
23333
|
+
onLastConnectionRemoved: output.onLastConnectionRemoved
|
|
23334
|
+
// a signal keeps its wire hooks (P121-TWO003)
|
|
23332
23335
|
});
|
|
23333
23336
|
} else {
|
|
23334
23337
|
node.registerOutput(name2, output);
|
|
@@ -24781,12 +24784,12 @@ var require_restnode = __commonJS({
|
|
|
24781
24784
|
this.scheduleAfterInputsHaveUpdated(this.doFetch.bind(this));
|
|
24782
24785
|
}
|
|
24783
24786
|
},
|
|
24784
|
-
doResponse: function(status, response,
|
|
24787
|
+
doResponse: function(status, response, request2) {
|
|
24785
24788
|
if (this._internal.responseFunc) {
|
|
24786
24789
|
this._internal.responseFunc.apply(this._internal.self, [
|
|
24787
24790
|
this._internal.inputValues,
|
|
24788
24791
|
this._internal.outputValuesProxy,
|
|
24789
|
-
{ status, content: response, request }
|
|
24792
|
+
{ status, content: response, request: request2 }
|
|
24790
24793
|
]);
|
|
24791
24794
|
}
|
|
24792
24795
|
this._internal.inspectData = { status, content: response };
|
|
@@ -24796,11 +24799,11 @@ var require_restnode = __commonJS({
|
|
|
24796
24799
|
this.sendSignalOnOutput("failure");
|
|
24797
24800
|
}
|
|
24798
24801
|
},
|
|
24799
|
-
doExternalFetch: function(
|
|
24800
|
-
let url =
|
|
24801
|
-
if (Object.keys(
|
|
24802
|
-
const parameters = Object.keys(
|
|
24803
|
-
return p + "=" + encodeURIComponent(
|
|
24802
|
+
doExternalFetch: function(request2) {
|
|
24803
|
+
let url = request2.resource;
|
|
24804
|
+
if (Object.keys(request2.parameters).length > 0) {
|
|
24805
|
+
const parameters = Object.keys(request2.parameters).map(function(p) {
|
|
24806
|
+
return p + "=" + encodeURIComponent(request2.parameters[p]);
|
|
24804
24807
|
});
|
|
24805
24808
|
url += "?" + parameters.join("&");
|
|
24806
24809
|
}
|
|
@@ -24808,9 +24811,9 @@ var require_restnode = __commonJS({
|
|
|
24808
24811
|
const _this = this;
|
|
24809
24812
|
const xhr = new window.XMLHttpRequest();
|
|
24810
24813
|
this._xhr = xhr;
|
|
24811
|
-
xhr.open(
|
|
24812
|
-
for (const header in
|
|
24813
|
-
xhr.setRequestHeader(header,
|
|
24814
|
+
xhr.open(request2.method, url, true);
|
|
24815
|
+
for (const header in request2.headers) {
|
|
24816
|
+
xhr.setRequestHeader(header, request2.headers[header]);
|
|
24814
24817
|
}
|
|
24815
24818
|
xhr.onreadystatechange = function() {
|
|
24816
24819
|
let sentResponse = false;
|
|
@@ -24822,11 +24825,11 @@ var require_restnode = __commonJS({
|
|
|
24822
24825
|
if (responseType) {
|
|
24823
24826
|
responseType = responseType.toLowerCase();
|
|
24824
24827
|
const responseData = responseType.indexOf("json") !== -1 ? JSON.parse(rawResponse) : rawResponse;
|
|
24825
|
-
_this.doResponse(statusCode, responseData,
|
|
24828
|
+
_this.doResponse(statusCode, responseData, request2);
|
|
24826
24829
|
sentResponse = true;
|
|
24827
24830
|
}
|
|
24828
24831
|
if (sentResponse === false) {
|
|
24829
|
-
_this.doResponse(statusCode, rawResponse,
|
|
24832
|
+
_this.doResponse(statusCode, rawResponse, request2);
|
|
24830
24833
|
}
|
|
24831
24834
|
}
|
|
24832
24835
|
};
|
|
@@ -24838,30 +24841,30 @@ var require_restnode = __commonJS({
|
|
|
24838
24841
|
delete this._xhr;
|
|
24839
24842
|
_this.sendSignalOnOutput("canceled");
|
|
24840
24843
|
};
|
|
24841
|
-
if (
|
|
24844
|
+
if (request2.content) {
|
|
24842
24845
|
xhr.setRequestHeader("Content-Type", "application/json");
|
|
24843
|
-
xhr.send(JSON.stringify(
|
|
24846
|
+
xhr.send(JSON.stringify(request2.content));
|
|
24844
24847
|
} else {
|
|
24845
24848
|
xhr.send();
|
|
24846
24849
|
}
|
|
24847
24850
|
} else {
|
|
24848
24851
|
const headers = Object.assign(
|
|
24849
24852
|
{},
|
|
24850
|
-
|
|
24851
|
-
|
|
24853
|
+
request2.headers,
|
|
24854
|
+
request2.content ? {
|
|
24852
24855
|
"Content-Type": "application/json"
|
|
24853
24856
|
} : {}
|
|
24854
24857
|
);
|
|
24855
24858
|
fetch(url, {
|
|
24856
|
-
method:
|
|
24859
|
+
method: request2.method,
|
|
24857
24860
|
headers,
|
|
24858
|
-
body:
|
|
24861
|
+
body: request2.content ? JSON.stringify(request2.content) : void 0
|
|
24859
24862
|
}).then((response) => {
|
|
24860
24863
|
const responseType = response.headers.get("content-type");
|
|
24861
24864
|
if (responseType) {
|
|
24862
24865
|
if (responseType.indexOf("/json") !== -1) {
|
|
24863
24866
|
response.json().then((json) => {
|
|
24864
|
-
this.doResponse(response.status, json,
|
|
24867
|
+
this.doResponse(response.status, json, request2);
|
|
24865
24868
|
});
|
|
24866
24869
|
} else {
|
|
24867
24870
|
if (this.context.editorConnection) {
|
|
@@ -24877,7 +24880,7 @@ var require_restnode = __commonJS({
|
|
|
24877
24880
|
}
|
|
24878
24881
|
} else {
|
|
24879
24882
|
response.text().then((raw) => {
|
|
24880
|
-
this.doResponse(response.status, raw,
|
|
24883
|
+
this.doResponse(response.status, raw, request2);
|
|
24881
24884
|
});
|
|
24882
24885
|
}
|
|
24883
24886
|
}).catch((e) => {
|
|
@@ -24895,7 +24898,7 @@ var require_restnode = __commonJS({
|
|
|
24895
24898
|
resource = resource.replace("{" + key + "}", this._internal.inputValues[key]);
|
|
24896
24899
|
}
|
|
24897
24900
|
}
|
|
24898
|
-
const
|
|
24901
|
+
const request2 = {
|
|
24899
24902
|
resource,
|
|
24900
24903
|
headers: {},
|
|
24901
24904
|
method: this._internal.method !== void 0 ? this._internal.method : "GET",
|
|
@@ -24905,10 +24908,10 @@ var require_restnode = __commonJS({
|
|
|
24905
24908
|
this._internal.requestFunc.apply(this._internal.self, [
|
|
24906
24909
|
this._internal.inputValues,
|
|
24907
24910
|
this._internal.outputValuesProxy,
|
|
24908
|
-
|
|
24911
|
+
request2
|
|
24909
24912
|
]);
|
|
24910
24913
|
}
|
|
24911
|
-
this.doExternalFetch(
|
|
24914
|
+
this.doExternalFetch(request2);
|
|
24912
24915
|
},
|
|
24913
24916
|
cancelFetch: function() {
|
|
24914
24917
|
if (typeof _noodl_cloud_runtime_version === "undefined") {
|
|
@@ -27291,6 +27294,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27291
27294
|
return names;
|
|
27292
27295
|
}
|
|
27293
27296
|
function publishHeldWrites(node) {
|
|
27297
|
+
sendHeldChanges(node);
|
|
27294
27298
|
node._internal.heldWrites.forEach((prop) => {
|
|
27295
27299
|
node.registerOutputIfNeeded("out-" + prop);
|
|
27296
27300
|
node.flagOutputDirty("out-" + prop);
|
|
@@ -27334,7 +27338,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27334
27338
|
if (value2 !== this._internal.outputValues[prop]) {
|
|
27335
27339
|
this.registerOutputIfNeeded("out-" + prop);
|
|
27336
27340
|
this._internal.outputValues[prop] = value2;
|
|
27337
|
-
this
|
|
27341
|
+
holdChange(this, prop);
|
|
27338
27342
|
this._internal.heldWrites.delete(prop);
|
|
27339
27343
|
} else {
|
|
27340
27344
|
this._internal.heldWrites.add(prop);
|
|
@@ -27513,7 +27517,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27513
27517
|
});
|
|
27514
27518
|
},
|
|
27515
27519
|
runScript: async function() {
|
|
27516
|
-
const func = this._internal.func;
|
|
27520
|
+
const func = sendingChangesAfterTheSyncPart(this, this._internal.func);
|
|
27517
27521
|
const tokens = this._internal.pendingRunOutcomes || [];
|
|
27518
27522
|
this._internal.pendingRunOutcomes = void 0;
|
|
27519
27523
|
if (func === void 0) {
|
|
@@ -27955,6 +27959,32 @@ var require_simplejavascript = __commonJS({
|
|
|
27955
27959
|
});
|
|
27956
27960
|
}
|
|
27957
27961
|
};
|
|
27962
|
+
function holdChange(node, prop) {
|
|
27963
|
+
const internal2 = node._internal;
|
|
27964
|
+
if (!internal2.heldChanges) internal2.heldChanges = /* @__PURE__ */ new Set();
|
|
27965
|
+
internal2.heldChanges.add(prop);
|
|
27966
|
+
if (internal2.heldChangesQueued) return;
|
|
27967
|
+
internal2.heldChangesQueued = true;
|
|
27968
|
+
Promise.resolve().then(() => sendHeldChanges(node));
|
|
27969
|
+
}
|
|
27970
|
+
function sendHeldChanges(node) {
|
|
27971
|
+
const internal2 = node._internal;
|
|
27972
|
+
internal2.heldChangesQueued = false;
|
|
27973
|
+
const held = internal2.heldChanges;
|
|
27974
|
+
if (!held || held.size === 0 || node._deleted) return;
|
|
27975
|
+
internal2.heldChanges = /* @__PURE__ */ new Set();
|
|
27976
|
+
held.forEach((prop) => node.flagOutputDirty("out-" + prop));
|
|
27977
|
+
}
|
|
27978
|
+
function sendingChangesAfterTheSyncPart(node, func) {
|
|
27979
|
+
if (func === void 0) return void 0;
|
|
27980
|
+
return function(...args) {
|
|
27981
|
+
try {
|
|
27982
|
+
return func.apply(this, args);
|
|
27983
|
+
} finally {
|
|
27984
|
+
sendHeldChanges(node);
|
|
27985
|
+
}
|
|
27986
|
+
};
|
|
27987
|
+
}
|
|
27958
27988
|
module2.exports = SimpleJavascriptNodeModule;
|
|
27959
27989
|
}
|
|
27960
27990
|
});
|
|
@@ -29891,22 +29921,22 @@ function connectionKey(dialect, base, token, where2) {
|
|
|
29891
29921
|
const filter2 = filterKey(where2);
|
|
29892
29922
|
return dialect + " " + base + " " + token + (filter2 ? " #" + filter2 : "");
|
|
29893
29923
|
}
|
|
29894
|
-
function connectionFor(
|
|
29895
|
-
const host =
|
|
29924
|
+
function connectionFor(request2) {
|
|
29925
|
+
const host = request2.host || DEFAULT_HOST2;
|
|
29896
29926
|
let pool = POOLS.get(host);
|
|
29897
29927
|
if (!pool) {
|
|
29898
29928
|
pool = /* @__PURE__ */ new Map();
|
|
29899
29929
|
POOLS.set(host, pool);
|
|
29900
29930
|
}
|
|
29901
|
-
const key = connectionKey(
|
|
29931
|
+
const key = connectionKey(request2.dialect.name, request2.base, request2.token, request2.where);
|
|
29902
29932
|
const existing = pool.get(key);
|
|
29903
29933
|
if (existing) return existing;
|
|
29904
29934
|
const created = new SharedSseConnection(
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29935
|
+
request2.dialect,
|
|
29936
|
+
request2.base,
|
|
29937
|
+
request2.token,
|
|
29938
|
+
request2.EventSourceImpl,
|
|
29939
|
+
request2.fetchImpl,
|
|
29910
29940
|
() => {
|
|
29911
29941
|
const current = POOLS.get(host);
|
|
29912
29942
|
if (current && current.get(key) === created) current.delete(key);
|
|
@@ -32970,8 +33000,8 @@ var init_probes = __esm({
|
|
|
32970
33000
|
const cacheKey = `${backendId}\0${key}`;
|
|
32971
33001
|
const existing = this._inFlight.get(cacheKey);
|
|
32972
33002
|
if (existing) return existing;
|
|
32973
|
-
const
|
|
32974
|
-
const pending = this._runner(
|
|
33003
|
+
const request2 = { key, probe: capability.probe, url: url.replace(/\/+$/, ""), type };
|
|
33004
|
+
const pending = this._runner(request2).then((outcome) => {
|
|
32975
33005
|
this._record(backendId, key, outcome);
|
|
32976
33006
|
return outcome;
|
|
32977
33007
|
}).catch(() => {
|
|
@@ -41514,8 +41544,8 @@ var require_dbmodelcrudbase = __commonJS({
|
|
|
41514
41544
|
const store = CloudStore7.forBackend(modelScope, backendId);
|
|
41515
41545
|
return store ? store.currentUserId() : void 0;
|
|
41516
41546
|
} else {
|
|
41517
|
-
const
|
|
41518
|
-
return
|
|
41547
|
+
const request2 = modelScope.get("Request");
|
|
41548
|
+
return request2.UserId;
|
|
41519
41549
|
}
|
|
41520
41550
|
}
|
|
41521
41551
|
function _addAccessControl(def2) {
|
|
@@ -69189,6 +69219,133 @@ var init_tooltips = __esm({
|
|
|
69189
69219
|
}
|
|
69190
69220
|
});
|
|
69191
69221
|
|
|
69222
|
+
// ../noodl-viewer-react/src/keyboard-click.ts
|
|
69223
|
+
function holdsPressable(el) {
|
|
69224
|
+
return el.querySelector(PRESSABLE_INSIDE) !== null;
|
|
69225
|
+
}
|
|
69226
|
+
function clickIsWired(node) {
|
|
69227
|
+
if (!node.hasOutput("onClick")) return false;
|
|
69228
|
+
return reachesSomething(node.getOutput("onClick"), 0);
|
|
69229
|
+
}
|
|
69230
|
+
function reachesSomething(output, depth) {
|
|
69231
|
+
if (!output.hasConnections()) return false;
|
|
69232
|
+
if (!Array.isArray(output.connections)) return true;
|
|
69233
|
+
return output.connections.some((c) => !goesNowhere(c, depth));
|
|
69234
|
+
}
|
|
69235
|
+
function goesNowhere(connection, depth) {
|
|
69236
|
+
const target = connection.node;
|
|
69237
|
+
if (!target || target.name !== "Component Outputs" || typeof connection.inputPortName !== "string") return false;
|
|
69238
|
+
const owner = target.nodeScope?.componentOwner;
|
|
69239
|
+
if (!owner || depth > 16 || owner._internal?.creatorCallbacks) return false;
|
|
69240
|
+
if (typeof owner.hasOutput !== "function" || typeof owner.getOutput !== "function") return false;
|
|
69241
|
+
if (!owner.hasOutput(connection.inputPortName)) return true;
|
|
69242
|
+
return !reachesSomething(owner.getOutput(connection.inputPortName), depth + 1);
|
|
69243
|
+
}
|
|
69244
|
+
function isNativelyInteractive(el) {
|
|
69245
|
+
const tag = el.tagName;
|
|
69246
|
+
if (tag === "BUTTON" || tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA" || tag === "SUMMARY") return true;
|
|
69247
|
+
if ((tag === "A" || tag === "AREA") && el.hasAttribute("href")) return true;
|
|
69248
|
+
if ((tag === "VIDEO" || tag === "AUDIO") && el.hasAttribute("controls")) return true;
|
|
69249
|
+
return el.isContentEditable === true;
|
|
69250
|
+
}
|
|
69251
|
+
function isSpace(e) {
|
|
69252
|
+
return e.key === " " || e.key === "Spacebar";
|
|
69253
|
+
}
|
|
69254
|
+
function isOwnKey(el, e) {
|
|
69255
|
+
return e.target === el && !e.defaultPrevented && !e.altKey && !e.ctrlKey && !e.metaKey && !e.isComposing;
|
|
69256
|
+
}
|
|
69257
|
+
function mark(el) {
|
|
69258
|
+
let spaceArmed = false;
|
|
69259
|
+
const onKeyDown2 = (e) => {
|
|
69260
|
+
if (!isOwnKey(el, e)) return;
|
|
69261
|
+
if (e.key === "Enter") {
|
|
69262
|
+
e.preventDefault();
|
|
69263
|
+
if (!e.repeat) el.click();
|
|
69264
|
+
} else if (isSpace(e)) {
|
|
69265
|
+
e.preventDefault();
|
|
69266
|
+
if (!e.repeat) spaceArmed = true;
|
|
69267
|
+
}
|
|
69268
|
+
};
|
|
69269
|
+
const onKeyUp = (e) => {
|
|
69270
|
+
if (!isSpace(e)) return;
|
|
69271
|
+
const armed = spaceArmed;
|
|
69272
|
+
spaceArmed = false;
|
|
69273
|
+
if (armed && e.target === el && !e.defaultPrevented) el.click();
|
|
69274
|
+
};
|
|
69275
|
+
const m = { el, added: [], onKeyDown: onKeyDown2, onKeyUp, observer: null };
|
|
69276
|
+
if (isNativelyInteractive(el)) return m;
|
|
69277
|
+
refresh(m);
|
|
69278
|
+
if (typeof MutationObserver !== "undefined") {
|
|
69279
|
+
m.observer = new MutationObserver(() => refresh(m));
|
|
69280
|
+
m.observer.observe(el, {
|
|
69281
|
+
childList: true,
|
|
69282
|
+
subtree: true,
|
|
69283
|
+
attributes: true,
|
|
69284
|
+
attributeFilter: ["tabindex", "contenteditable", "href", "controls", "type"]
|
|
69285
|
+
});
|
|
69286
|
+
}
|
|
69287
|
+
return m;
|
|
69288
|
+
}
|
|
69289
|
+
function refresh(m) {
|
|
69290
|
+
const { el } = m;
|
|
69291
|
+
const want = !holdsPressable(el);
|
|
69292
|
+
if (want && !m.added.length) {
|
|
69293
|
+
if (!el.hasAttribute("tabindex")) {
|
|
69294
|
+
el.setAttribute("tabindex", "0");
|
|
69295
|
+
m.added.push("tabindex");
|
|
69296
|
+
}
|
|
69297
|
+
if (!el.hasAttribute("role")) {
|
|
69298
|
+
el.setAttribute("role", "button");
|
|
69299
|
+
m.added.push("role");
|
|
69300
|
+
}
|
|
69301
|
+
if (m.added.length) {
|
|
69302
|
+
el.setAttribute(KEYBOARD_CLICK_ATTRIBUTE, "");
|
|
69303
|
+
m.added.push(KEYBOARD_CLICK_ATTRIBUTE);
|
|
69304
|
+
el.addEventListener("keydown", m.onKeyDown);
|
|
69305
|
+
el.addEventListener("keyup", m.onKeyUp);
|
|
69306
|
+
}
|
|
69307
|
+
} else if (!want && m.added.length) {
|
|
69308
|
+
giveBack(m);
|
|
69309
|
+
}
|
|
69310
|
+
}
|
|
69311
|
+
function giveBack(m) {
|
|
69312
|
+
for (const name2 of m.added) m.el.removeAttribute(name2);
|
|
69313
|
+
m.added = [];
|
|
69314
|
+
m.el.removeEventListener("keydown", m.onKeyDown);
|
|
69315
|
+
m.el.removeEventListener("keyup", m.onKeyUp);
|
|
69316
|
+
}
|
|
69317
|
+
function unmark(m) {
|
|
69318
|
+
m.observer?.disconnect();
|
|
69319
|
+
giveBack(m);
|
|
69320
|
+
}
|
|
69321
|
+
function syncKeyboardClick(node) {
|
|
69322
|
+
const el = typeof node.getDOMElement === "function" ? node.getDOMElement() : null;
|
|
69323
|
+
const want = el && clickIsWired(node) ? el : null;
|
|
69324
|
+
const was = marked.get(node);
|
|
69325
|
+
if (was && was.el === want) return;
|
|
69326
|
+
if (was) {
|
|
69327
|
+
unmark(was);
|
|
69328
|
+
marked.delete(node);
|
|
69329
|
+
}
|
|
69330
|
+
if (want) marked.set(node, mark(want));
|
|
69331
|
+
}
|
|
69332
|
+
var KEYBOARD_CLICK_ATTRIBUTE, PRESSABLE_INSIDE, marked, KEYBOARD_CLICK_HOOKS;
|
|
69333
|
+
var init_keyboard_click = __esm({
|
|
69334
|
+
"../noodl-viewer-react/src/keyboard-click.ts"() {
|
|
69335
|
+
KEYBOARD_CLICK_ATTRIBUTE = "data-ndl-keyboard-click";
|
|
69336
|
+
PRESSABLE_INSIDE = 'button, input:not([type="hidden"]), select, textarea, summary, a[href], area[href], video[controls], audio[controls], [contenteditable=""], [contenteditable="true"], [tabindex]:not([tabindex="-1"])';
|
|
69337
|
+
marked = /* @__PURE__ */ new WeakMap();
|
|
69338
|
+
KEYBOARD_CLICK_HOOKS = {
|
|
69339
|
+
onFirstConnectionAdded() {
|
|
69340
|
+
syncKeyboardClick(this);
|
|
69341
|
+
},
|
|
69342
|
+
onLastConnectionRemoved() {
|
|
69343
|
+
syncKeyboardClick(this);
|
|
69344
|
+
}
|
|
69345
|
+
};
|
|
69346
|
+
}
|
|
69347
|
+
});
|
|
69348
|
+
|
|
69192
69349
|
// ../noodl-viewer-react/src/node-shared-port-definitions.ts
|
|
69193
69350
|
function humanCorner(suffix) {
|
|
69194
69351
|
return suffix.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -69456,6 +69613,7 @@ var init_node_shared_port_definitions = __esm({
|
|
|
69456
69613
|
init_fontloader();
|
|
69457
69614
|
init_media_source();
|
|
69458
69615
|
init_tooltips();
|
|
69616
|
+
init_keyboard_click();
|
|
69459
69617
|
SIZE_MODES = ["explicit", "contentWidth", "contentHeight", "contentSize"];
|
|
69460
69618
|
node_shared_port_definitions_default = {
|
|
69461
69619
|
addInputProps: addInputProps2,
|
|
@@ -69963,7 +70121,9 @@ var init_node_shared_port_definitions = __esm({
|
|
|
69963
70121
|
description: "Fires when this element is clicked or tapped",
|
|
69964
70122
|
group: "Events",
|
|
69965
70123
|
type: "signal",
|
|
69966
|
-
propPath: "pointer"
|
|
70124
|
+
propPath: "pointer",
|
|
70125
|
+
...KEYBOARD_CLICK_HOOKS
|
|
70126
|
+
// P121-TWO003-GROUPKEYBOARD: wired, the element is a button to Tab and a screen reader
|
|
69967
70127
|
},
|
|
69968
70128
|
pointerDown: {
|
|
69969
70129
|
displayName: "Pointer Down",
|
|
@@ -73684,6 +73844,7 @@ function createNodeFromReactComponent(def2) {
|
|
|
73684
73844
|
}
|
|
73685
73845
|
if (element) syncDataAttributesOnElement(this);
|
|
73686
73846
|
if (element) elementChanged(this, element);
|
|
73847
|
+
syncKeyboardClick(this);
|
|
73687
73848
|
},
|
|
73688
73849
|
/**
|
|
73689
73850
|
* NDA-012 (Visual) check A3 — run an imperative action on the inner React component,
|
|
@@ -74110,6 +74271,7 @@ var init_react_component_node = __esm({
|
|
|
74110
74271
|
init_mergedeep();
|
|
74111
74272
|
init_node_shared_port_definitions();
|
|
74112
74273
|
init_node_transitions();
|
|
74274
|
+
init_keyboard_click();
|
|
74113
74275
|
ICON_SOURCE_DIAGNOSTIC = "visual/icon-source-not-an-icon";
|
|
74114
74276
|
DATA_ATTRIBUTE_NAME_PATTERN = "^data-[a-z0-9][a-z0-9._-]*$";
|
|
74115
74277
|
DATA_ATTRIBUTE_NAME = new RegExp(DATA_ATTRIBUTE_NAME_PATTERN);
|
|
@@ -80880,6 +81042,38 @@ function _typeName(t) {
|
|
|
80880
81042
|
if (typeof t === "object") return t.name;
|
|
80881
81043
|
else return t;
|
|
80882
81044
|
}
|
|
81045
|
+
function isInsideRow(node, row) {
|
|
81046
|
+
if (node === row) return true;
|
|
81047
|
+
let owner = (node.parentNodeScope || node.nodeScope)?.componentOwner;
|
|
81048
|
+
for (let depth = 0; owner && depth < 64; depth++) {
|
|
81049
|
+
if (owner === row) return true;
|
|
81050
|
+
const next = (owner.parentNodeScope || owner.nodeScope)?.componentOwner;
|
|
81051
|
+
if (next === owner) return false;
|
|
81052
|
+
owner = next;
|
|
81053
|
+
}
|
|
81054
|
+
return false;
|
|
81055
|
+
}
|
|
81056
|
+
function settleRow(context, row) {
|
|
81057
|
+
const self2 = row;
|
|
81058
|
+
for (let pass = 0; pass < 10; pass++) {
|
|
81059
|
+
const dirty = context._dirtyNodes;
|
|
81060
|
+
if (!Array.isArray(dirty)) return;
|
|
81061
|
+
const mine = [];
|
|
81062
|
+
for (let i2 = 0; i2 < dirty.length; i2++) {
|
|
81063
|
+
const node = dirty[i2];
|
|
81064
|
+
if (node && node._dirty && !node._deleted && mine.indexOf(node) === -1 && isInsideRow(node, self2))
|
|
81065
|
+
mine.push(node);
|
|
81066
|
+
}
|
|
81067
|
+
if (mine.length === 0) return;
|
|
81068
|
+
for (let i2 = 0; i2 < mine.length; i2++) {
|
|
81069
|
+
try {
|
|
81070
|
+
mine[i2].update();
|
|
81071
|
+
} catch (e) {
|
|
81072
|
+
console.error(e);
|
|
81073
|
+
}
|
|
81074
|
+
}
|
|
81075
|
+
}
|
|
81076
|
+
}
|
|
80883
81077
|
var import_react16, import_runtime4, import_collection, import_model5, ITEMS_DIAGNOSTIC, defaultDynamicScript, ForEachDefinition, defaultMapCode, ForEachModule, foreach_default;
|
|
80884
81078
|
var init_foreach = __esm({
|
|
80885
81079
|
"../noodl-viewer-react/src/nodes/std-library/data/foreach.tsx"() {
|
|
@@ -81253,6 +81447,7 @@ var init_foreach = __esm({
|
|
|
81253
81447
|
}
|
|
81254
81448
|
}
|
|
81255
81449
|
};
|
|
81450
|
+
settleRow(this.context, itemNode);
|
|
81256
81451
|
const forEachActions = itemNode.nodeScope.getNodesWithType("For Each Actions");
|
|
81257
81452
|
for (let j = 0; j < forEachActions.length; j++) {
|
|
81258
81453
|
forEachActions[j].signalAdded();
|
|
@@ -90070,10 +90265,16 @@ function Text(props) {
|
|
|
90070
90265
|
...pointerProps(props),
|
|
90071
90266
|
style: style2
|
|
90072
90267
|
},
|
|
90073
|
-
|
|
90268
|
+
style2.textOverflow === "ellipsis" ? (
|
|
90269
|
+
// P18-S112-FLEXELLIPSIS — the Text is `display: flex` (its default CSS, which Text Vertical Align's `alignItems`
|
|
90270
|
+
// needs), so its words were an anonymous flex item and `text-overflow` on the element drew no "…": Ellipsis
|
|
90271
|
+
// looked the same as Clip. The words go in a block of their own that may shrink to the box and draws the "…";
|
|
90272
|
+
// the element keeps its flex, so both alignments still place them.
|
|
90273
|
+
/* @__PURE__ */ import_react22.default.createElement("span", { style: { ...ELLIPSIS_LINE, overflow: style2.overflow } }, renderableText(props.text))
|
|
90274
|
+
) : renderableText(props.text)
|
|
90074
90275
|
);
|
|
90075
90276
|
}
|
|
90076
|
-
var import_react22;
|
|
90277
|
+
var import_react22, ELLIPSIS_LINE;
|
|
90077
90278
|
var init_Text = __esm({
|
|
90078
90279
|
"../noodl-viewer-react/src/components/visual/Text/Text.tsx"() {
|
|
90079
90280
|
import_react22 = __toESM(require_react());
|
|
@@ -90081,6 +90282,7 @@ var init_Text = __esm({
|
|
|
90081
90282
|
init_layout();
|
|
90082
90283
|
init_pointerlisteners();
|
|
90083
90284
|
init_noodl_root_ref();
|
|
90285
|
+
ELLIPSIS_LINE = { minWidth: 0, textOverflow: "ellipsis", whiteSpace: "nowrap" };
|
|
90084
90286
|
}
|
|
90085
90287
|
});
|
|
90086
90288
|
|
|
@@ -90454,7 +90656,6 @@ var init_Video = __esm({
|
|
|
90454
90656
|
...videoProps,
|
|
90455
90657
|
playsInline: true,
|
|
90456
90658
|
src,
|
|
90457
|
-
...pointerProps(this.props),
|
|
90458
90659
|
ref: (video) => {
|
|
90459
90660
|
this.video = video;
|
|
90460
90661
|
this.props.innerRef(video);
|
|
@@ -90473,7 +90674,7 @@ var init_Video = __esm({
|
|
|
90473
90674
|
this.canPlay = false;
|
|
90474
90675
|
}
|
|
90475
90676
|
setSourceObject(src) {
|
|
90476
|
-
if (this.video.srcObject !== src) {
|
|
90677
|
+
if (this.video && this.video.srcObject !== src) {
|
|
90477
90678
|
this.video.srcObject = src;
|
|
90478
90679
|
this.canPlay = false;
|
|
90479
90680
|
}
|
|
@@ -90579,6 +90780,7 @@ var init_Video = __esm({
|
|
|
90579
90780
|
CachedVideo,
|
|
90580
90781
|
{
|
|
90581
90782
|
...props.dom,
|
|
90783
|
+
...pointerProps(this.props),
|
|
90582
90784
|
className: withDefaultsClass(props.className, defaultsClass),
|
|
90583
90785
|
style: style2,
|
|
90584
90786
|
innerRef: (video) => {
|
|
@@ -97687,6 +97889,7 @@ var init_parentcomponentobject = __esm({
|
|
|
97687
97889
|
*/
|
|
97688
97890
|
nodeScopeDidInitialize() {
|
|
97689
97891
|
this.context.scheduleAfterUpdate(() => {
|
|
97892
|
+
if (this._deleted) return;
|
|
97690
97893
|
this._internal.resolutionIsLoud = true;
|
|
97691
97894
|
this.onComponentStateNodesChanged();
|
|
97692
97895
|
});
|
|
@@ -102063,7 +102266,7 @@ var init_signinwith = __esm({
|
|
|
102063
102266
|
const tokens = [this.beginOutcome()];
|
|
102064
102267
|
if (state2.succeeded) {
|
|
102065
102268
|
this._internal.notice = state2.notice;
|
|
102066
|
-
this._internal.error =
|
|
102269
|
+
this._internal.error = "";
|
|
102067
102270
|
this.flagOutputDirty("notice");
|
|
102068
102271
|
this.flagOutputDirty("error");
|
|
102069
102272
|
reportOutcomes(this, tokens, "done");
|
|
@@ -114719,7 +114922,7 @@ var require_package = __commonJS({
|
|
|
114719
114922
|
"package.json"(exports2, module2) {
|
|
114720
114923
|
module2.exports = {
|
|
114721
114924
|
name: "@nodegx/preview",
|
|
114722
|
-
version: "0.2.
|
|
114925
|
+
version: "0.2.8",
|
|
114723
114926
|
description: "Standalone live-preview harness: renders a NodeGX (OpenNoodl) v2 project in a browser with no editor process, and reloads it when the files on disk change.",
|
|
114724
114927
|
license: "GPL-3.0-only",
|
|
114725
114928
|
homepage: "https://nodegx.io",
|
|
@@ -164665,7 +164868,7 @@ var node_catalog_default = {
|
|
|
164665
164868
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
164666
164869
|
],
|
|
164667
164870
|
visible: [
|
|
164668
|
-
"always: shown or hidden at
|
|
164871
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
164669
164872
|
]
|
|
164670
164873
|
},
|
|
164671
164874
|
warnings: {
|
|
@@ -166850,7 +167053,7 @@ var node_catalog_default = {
|
|
|
166850
167053
|
"always: the page, or the scrolling Group holding it, scrolls until the box can be seen"
|
|
166851
167054
|
],
|
|
166852
167055
|
visible: [
|
|
166853
|
-
"always: shown or hidden at
|
|
167056
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
166854
167057
|
]
|
|
166855
167058
|
},
|
|
166856
167059
|
warnings: {
|
|
@@ -169462,7 +169665,7 @@ var node_catalog_default = {
|
|
|
169462
169665
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
169463
169666
|
],
|
|
169464
169667
|
visible: [
|
|
169465
|
-
"always: shown or hidden at
|
|
169668
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
169466
169669
|
]
|
|
169467
169670
|
},
|
|
169468
169671
|
warnings: {
|
|
@@ -171644,7 +171847,7 @@ var node_catalog_default = {
|
|
|
171644
171847
|
"always: the page, or the scrolling Group holding it, scrolls until the button can be seen"
|
|
171645
171848
|
],
|
|
171646
171849
|
visible: [
|
|
171647
|
-
"always: shown or hidden at
|
|
171850
|
+
"always: shown or hidden at once, its space kept; hidden, it takes no click and a Focus does not reach it"
|
|
171648
171851
|
]
|
|
171649
171852
|
},
|
|
171650
171853
|
warnings: {
|
|
@@ -174049,7 +174252,7 @@ var node_catalog_default = {
|
|
|
174049
174252
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
174050
174253
|
],
|
|
174051
174254
|
visible: [
|
|
174052
|
-
"always: shown or hidden at
|
|
174255
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
174053
174256
|
]
|
|
174054
174257
|
},
|
|
174055
174258
|
warnings: {
|
|
@@ -176919,7 +177122,7 @@ var node_catalog_default = {
|
|
|
176919
177122
|
"always: the size it holds now, a bare number read in the unit it held; drawn at the next paint"
|
|
176920
177123
|
],
|
|
176921
177124
|
visible: [
|
|
176922
|
-
"always: shown or hidden at
|
|
177125
|
+
"always: shown or hidden at once, its space kept; hidden, a Focus does not reach it"
|
|
176923
177126
|
],
|
|
176924
177127
|
opacity: [
|
|
176925
177128
|
"always: how see-through it is; at exactly 0 it takes no pointer, and an act goes to what is behind it"
|
|
@@ -181761,7 +181964,7 @@ var node_catalog_default = {
|
|
|
181761
181964
|
},
|
|
181762
181965
|
isSignal: false,
|
|
181763
181966
|
default: false,
|
|
181764
|
-
description: "Whether a request with no valid session token is allowed to run this function at all"
|
|
181967
|
+
description: "Whether a request with no valid session token is allowed to run this function at all. A run the app starts itself \u2014 a schedule, a row change, a run from Studio \u2014 is always allowed and sees nobody signed in; a webhook is a request like any other"
|
|
181765
181968
|
},
|
|
181766
181969
|
{
|
|
181767
181970
|
name: "params",
|
|
@@ -183818,14 +184021,14 @@ var TOKEN_COOKIE = "nodegx_token";
|
|
|
183818
184021
|
function mintToken() {
|
|
183819
184022
|
return (0, import_node_crypto.randomBytes)(32).toString("hex");
|
|
183820
184023
|
}
|
|
183821
|
-
function resolveAccess(
|
|
183822
|
-
const shared =
|
|
184024
|
+
function resolveAccess(request2 = {}) {
|
|
184025
|
+
const shared = request2.share === true;
|
|
183823
184026
|
return {
|
|
183824
|
-
host: shared ?
|
|
184027
|
+
host: shared ? request2.host || ALL_INTERFACES : LOOPBACK,
|
|
183825
184028
|
shared,
|
|
183826
184029
|
// A shared server always has a token. An unshared one still gets one, so that turning
|
|
183827
184030
|
// sharing on later cannot find itself without a credential and invent a weaker path.
|
|
183828
|
-
token:
|
|
184031
|
+
token: request2.token || mintToken()
|
|
183829
184032
|
};
|
|
183830
184033
|
}
|
|
183831
184034
|
function tokenMatches(candidate, token) {
|
|
@@ -183841,15 +184044,15 @@ function isLoopbackAddress(address) {
|
|
|
183841
184044
|
if (addr === "::1") return true;
|
|
183842
184045
|
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(addr);
|
|
183843
184046
|
}
|
|
183844
|
-
function tokenFromRequest(
|
|
183845
|
-
const headers =
|
|
184047
|
+
function tokenFromRequest(request2) {
|
|
184048
|
+
const headers = request2.headers || {};
|
|
183846
184049
|
const header = headers[TOKEN_HEADER];
|
|
183847
184050
|
if (typeof header === "string" && header.length > 0) return header;
|
|
183848
184051
|
const auth = headers["authorization"];
|
|
183849
184052
|
if (typeof auth === "string" && /^Bearer\s+/i.test(auth)) return auth.replace(/^Bearer\s+/i, "");
|
|
183850
|
-
if (typeof
|
|
184053
|
+
if (typeof request2.url === "string") {
|
|
183851
184054
|
try {
|
|
183852
|
-
const query = new URL(
|
|
184055
|
+
const query = new URL(request2.url, "http://placeholder.invalid").searchParams.get(TOKEN_QUERY);
|
|
183853
184056
|
if (query) return query;
|
|
183854
184057
|
} catch {
|
|
183855
184058
|
}
|
|
@@ -183863,11 +184066,11 @@ function tokenFromRequest(request) {
|
|
|
183863
184066
|
}
|
|
183864
184067
|
return null;
|
|
183865
184068
|
}
|
|
183866
|
-
function authoriseRequest(
|
|
183867
|
-
if (!access.shared || isLoopbackAddress(
|
|
184069
|
+
function authoriseRequest(request2, access) {
|
|
184070
|
+
if (!access.shared || isLoopbackAddress(request2.remoteAddress)) {
|
|
183868
184071
|
return { ok: true, reason: "loopback", setCookie: null };
|
|
183869
184072
|
}
|
|
183870
|
-
const presented = tokenFromRequest(
|
|
184073
|
+
const presented = tokenFromRequest(request2);
|
|
183871
184074
|
if (presented === null) {
|
|
183872
184075
|
return { ok: false, reason: "no-token", status: 401, body: UNAUTHORISED_BODY };
|
|
183873
184076
|
}
|
|
@@ -185708,8 +185911,8 @@ function renderMarkdown(md) {
|
|
|
185708
185911
|
continue;
|
|
185709
185912
|
}
|
|
185710
185913
|
if (lines.every((l) => /^\s*>\s?/.test(l))) {
|
|
185711
|
-
const
|
|
185712
|
-
html.push(`<blockquote>${renderMarkdown(
|
|
185914
|
+
const quoted2 = lines.map((l) => l.replace(/^\s*>\s?/, "")).join("\n");
|
|
185915
|
+
html.push(`<blockquote>${renderMarkdown(quoted2)}</blockquote>`);
|
|
185713
185916
|
continue;
|
|
185714
185917
|
}
|
|
185715
185918
|
if (lines.every((l) => /^\s*[-*]\s+/.test(l))) {
|
|
@@ -190891,18 +191094,18 @@ function capabilityProbes() {
|
|
|
190891
191094
|
if (!instance) instance = new CapabilityProbeCache({ runner: runProbe });
|
|
190892
191095
|
return instance;
|
|
190893
191096
|
}
|
|
190894
|
-
async function runProbe(
|
|
190895
|
-
switch (
|
|
191097
|
+
async function runProbe(request2) {
|
|
191098
|
+
switch (request2.key) {
|
|
190896
191099
|
case "realtime.subscribe":
|
|
190897
|
-
return probeRealtime(
|
|
191100
|
+
return probeRealtime(request2);
|
|
190898
191101
|
case "data.aggregate":
|
|
190899
|
-
return probeAggregate(
|
|
191102
|
+
return probeAggregate(request2);
|
|
190900
191103
|
default:
|
|
190901
|
-
throw new Error(`No probe implemented for ${
|
|
191104
|
+
throw new Error(`No probe implemented for ${request2.key}`);
|
|
190902
191105
|
}
|
|
190903
191106
|
}
|
|
190904
|
-
function probeRealtime(
|
|
190905
|
-
const url = websocketUrl(
|
|
191107
|
+
function probeRealtime(request2) {
|
|
191108
|
+
const url = websocketUrl(request2.url, request2.probe.path);
|
|
190906
191109
|
return new Promise((resolve5) => {
|
|
190907
191110
|
let settled = false;
|
|
190908
191111
|
let socket = null;
|
|
@@ -190935,8 +191138,8 @@ function probeRealtime(request) {
|
|
|
190935
191138
|
socket.onclose = () => settle({ verdict: "unsupported", at: Date.now(), detail: "the websocket closed without upgrading" });
|
|
190936
191139
|
});
|
|
190937
191140
|
}
|
|
190938
|
-
async function probeAggregate(
|
|
190939
|
-
const response = await fetchWithDeadline(`${
|
|
191141
|
+
async function probeAggregate(request2) {
|
|
191142
|
+
const response = await fetchWithDeadline(`${request2.url}${request2.probe.path}`, request2.probe.method);
|
|
190940
191143
|
if (!response) return { verdict: "unsupported", at: Date.now(), detail: "the server did not answer" };
|
|
190941
191144
|
if (response.status !== 200) {
|
|
190942
191145
|
return { verdict: "unsupported", at: Date.now(), detail: `the server answered ${response.status}` };
|
|
@@ -194529,6 +194732,55 @@ function cloudPortMeaning(type, plug) {
|
|
|
194529
194732
|
init_scriptfiles();
|
|
194530
194733
|
var FUNCTION_NODE_TYPE = "JavaScriptFunction";
|
|
194531
194734
|
|
|
194735
|
+
// ../noodl-editor/src/editor/src/models/nodelibrary/cloudDynamicPorts.ts
|
|
194736
|
+
init_function_ports();
|
|
194737
|
+
function proplist(parameters, name2) {
|
|
194738
|
+
const value2 = parameters[name2];
|
|
194739
|
+
return Array.isArray(value2) ? value2 : [];
|
|
194740
|
+
}
|
|
194741
|
+
function scriptPortsFromSource(script) {
|
|
194742
|
+
const ports = [];
|
|
194743
|
+
if (typeof script !== "string") return ports;
|
|
194744
|
+
const names = functionScriptPortNames(script);
|
|
194745
|
+
const add = (name2, plug, type, prefix) => {
|
|
194746
|
+
if (ports.some((p) => p.name === prefix + name2 && p.plug === plug)) return;
|
|
194747
|
+
ports.push({ name: prefix + name2, displayName: name2, plug, type, group: plug === "input" ? "Inputs" : "Outputs" });
|
|
194748
|
+
};
|
|
194749
|
+
for (const name2 of names.inputs) add(name2, "input", "*", "in-");
|
|
194750
|
+
for (const name2 of names.signals) add(name2, "output", "signal", "out-");
|
|
194751
|
+
for (const name2 of names.outputs) add(name2, "output", "*", "out-");
|
|
194752
|
+
return ports;
|
|
194753
|
+
}
|
|
194754
|
+
function scriptPortsForNode(node) {
|
|
194755
|
+
const parameters = node.parameters || {};
|
|
194756
|
+
const ports = [];
|
|
194757
|
+
for (const spec of proplist(parameters, "scriptOutputs")) {
|
|
194758
|
+
if (!spec.label) continue;
|
|
194759
|
+
ports.push({
|
|
194760
|
+
name: "out-" + spec.label,
|
|
194761
|
+
displayName: spec.label,
|
|
194762
|
+
plug: "output",
|
|
194763
|
+
type: parameters["outtype-" + spec.label] || "*",
|
|
194764
|
+
group: "Outputs"
|
|
194765
|
+
});
|
|
194766
|
+
}
|
|
194767
|
+
for (const spec of proplist(parameters, "scriptInputs")) {
|
|
194768
|
+
if (!spec.label) continue;
|
|
194769
|
+
ports.push({
|
|
194770
|
+
name: "in-" + spec.label,
|
|
194771
|
+
displayName: spec.label,
|
|
194772
|
+
plug: "input",
|
|
194773
|
+
type: parameters["intype-" + spec.label] || "string",
|
|
194774
|
+
group: "Inputs"
|
|
194775
|
+
});
|
|
194776
|
+
}
|
|
194777
|
+
for (const port of scriptPortsFromSource(parameters["functionScript"])) {
|
|
194778
|
+
if (ports.some((p) => p.name === port.name && p.plug === port.plug)) continue;
|
|
194779
|
+
ports.push(port);
|
|
194780
|
+
}
|
|
194781
|
+
return ports;
|
|
194782
|
+
}
|
|
194783
|
+
|
|
194532
194784
|
// ../noodl-editor/src/editor/src/validation/functionPorts.ts
|
|
194533
194785
|
var SCRIPT_NODE_API_MEMBERS = [
|
|
194534
194786
|
"Inputs",
|
|
@@ -194784,7 +195036,11 @@ var AUTHORED_BLOCKING_WARNINGS = /* @__PURE__ */ new Set([
|
|
|
194784
195036
|
// Bug P121-TWO003-SCRIPTCOMPILE — the runtime's own compile fails, so every Run fails. No benign
|
|
194785
195037
|
// reading for a graph an agent just wrote; the write said nothing before, and the first person
|
|
194786
195038
|
// to run the function got "That could not be done."
|
|
194787
|
-
"function-script-not-compiled" /* FunctionScriptNotCompiled
|
|
195039
|
+
"function-script-not-compiled" /* FunctionScriptNotCompiled */,
|
|
195040
|
+
// Bug P127-BKO006-RUNONCHANGEGUESS — a `runOnChange-` name a Function does not have is never read
|
|
195041
|
+
// (2,805 right and 5 wrong over 2,718 Functions, all five agent-written); the message names the
|
|
195042
|
+
// right one.
|
|
195043
|
+
"function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */
|
|
194788
195044
|
]);
|
|
194789
195045
|
|
|
194790
195046
|
// ../noodl-editor/src/editor/src/validation/diagnosticExamples.ts
|
|
@@ -194924,6 +195180,12 @@ var DIAGNOSTIC_EXAMPLES = [
|
|
|
194924
195180
|
code: "function-guards-an-open-collection" /* FunctionGuardsAnOpenCollection */,
|
|
194925
195181
|
examples: ["cloud-members-can-edit-my-records"],
|
|
194926
195182
|
why: `the records only a function creates, with the collection's create rule "nobody": the function runs as the system`
|
|
195183
|
+
},
|
|
195184
|
+
// P112-OWN018-FANOUT (ruled 2026-10-27 "Teach it and warn") — the warning carries the chained shape.
|
|
195185
|
+
{
|
|
195186
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
195187
|
+
examples: ["score-then-move-on"],
|
|
195188
|
+
why: "the press evaluating the gate that reads, and that gate's Done evaluating the gate that moves on, so the score is decided first"
|
|
194927
195189
|
}
|
|
194928
195190
|
];
|
|
194929
195191
|
|
|
@@ -196510,6 +196772,156 @@ var functionNeverReruns = {
|
|
|
196510
196772
|
}
|
|
196511
196773
|
};
|
|
196512
196774
|
|
|
196775
|
+
// ../noodl-editor/src/editor/src/validation/rules/functionRunOnChangeUnknown.ts
|
|
196776
|
+
var RUN_ON_CHANGE_PREFIX4 = "runOnChange-";
|
|
196777
|
+
var FUNCTION_INPUT_PREFIX = "in-";
|
|
196778
|
+
var functionRunOnChangeUnknown = {
|
|
196779
|
+
code: "function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */,
|
|
196780
|
+
description: "A Function has a Run On Value Change setting under a name it does not have (its boxes are runOnChange-in-<input>).",
|
|
196781
|
+
defaultEnabled: true,
|
|
196782
|
+
run(ctx) {
|
|
196783
|
+
const out = [];
|
|
196784
|
+
for (const { component } of ctx.components) {
|
|
196785
|
+
for (const node of component.nodes) {
|
|
196786
|
+
if (node.type !== FUNCTION_NODE_TYPE || !node.parameters) continue;
|
|
196787
|
+
const guessed = Object.keys(node.parameters).filter(
|
|
196788
|
+
(key) => key.startsWith(RUN_ON_CHANGE_PREFIX4) && !key.startsWith(RUN_ON_CHANGE_PREFIX4 + FUNCTION_INPUT_PREFIX)
|
|
196789
|
+
);
|
|
196790
|
+
if (guessed.length === 0) continue;
|
|
196791
|
+
const inputs = scriptPortsForNode(node).filter((p) => p.plug === "input" && p.name.startsWith(FUNCTION_INPUT_PREFIX)).map((p) => p.name.slice(FUNCTION_INPUT_PREFIX.length));
|
|
196792
|
+
const name2 = node.label ? `"${node.label}"` : "This Function";
|
|
196793
|
+
for (const key of guessed) {
|
|
196794
|
+
const wanted = key.slice(RUN_ON_CHANGE_PREFIX4.length);
|
|
196795
|
+
const input2 = inputs.includes(wanted) ? wanted : nearest(wanted, inputs);
|
|
196796
|
+
const right = input2 !== void 0 ? `${RUN_ON_CHANGE_PREFIX4}${FUNCTION_INPUT_PREFIX}${input2}` : void 0;
|
|
196797
|
+
out.push({
|
|
196798
|
+
code: "function-run-on-change-unknown" /* FunctionRunOnChangeUnknown */,
|
|
196799
|
+
severity: "warning",
|
|
196800
|
+
message: `${name2} has no setting "${key}", so nothing reads it` + (input2 === wanted ? ` and a new "${wanted}" still runs it. ` : ". ") + `A Function's Run On Value Change box for an input is "runOnChange-in-<input>"` + (right ? input2 === wanted ? `: write "${right}".` : `; its script reads no "${wanted}" (the nearest input is "${input2}": "${right}").` : `, and its script reads no inputs.`),
|
|
196801
|
+
location: {
|
|
196802
|
+
component: component.name,
|
|
196803
|
+
nodeId: node.id,
|
|
196804
|
+
nodeType: node.type,
|
|
196805
|
+
nodeLabel: node.label,
|
|
196806
|
+
port: key,
|
|
196807
|
+
plug: "input"
|
|
196808
|
+
},
|
|
196809
|
+
...right ? { suggestion: `${right}: ${JSON.stringify(node.parameters[key])}` } : {}
|
|
196810
|
+
});
|
|
196811
|
+
}
|
|
196812
|
+
}
|
|
196813
|
+
}
|
|
196814
|
+
return out;
|
|
196815
|
+
}
|
|
196816
|
+
};
|
|
196817
|
+
|
|
196818
|
+
// ../noodl-editor/src/editor/src/validation/rules/conditionReadsASiblingsChange.ts
|
|
196819
|
+
var CONDITION = "Condition";
|
|
196820
|
+
var conditionReadsASiblingsChange = {
|
|
196821
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
196822
|
+
description: "Two Conditions evaluated by one signal, where one's On True or On False changes what the other reads.",
|
|
196823
|
+
defaultEnabled: true,
|
|
196824
|
+
run(ctx) {
|
|
196825
|
+
const out = [];
|
|
196826
|
+
const { catalog } = ctx;
|
|
196827
|
+
for (const { component, nodeById } of ctx.components) {
|
|
196828
|
+
let exclusiveStates = function(a, b) {
|
|
196829
|
+
const stateOf = (gate) => {
|
|
196830
|
+
const c = (incoming.get(gate) ?? []).find((w) => w.toProperty === "condition");
|
|
196831
|
+
return c && nodeById.get(c.fromId)?.type === "States" && c.fromProperty.startsWith("at-") ? c : void 0;
|
|
196832
|
+
};
|
|
196833
|
+
const sa = stateOf(a);
|
|
196834
|
+
const sb = stateOf(b);
|
|
196835
|
+
return !!sa && !!sb && sa.fromId === sb.fromId && sa.fromProperty !== sb.fromProperty;
|
|
196836
|
+
}, feedingValues = function(gate) {
|
|
196837
|
+
const seen = /* @__PURE__ */ new Set();
|
|
196838
|
+
const stack = (incoming.get(gate) ?? []).filter((c) => c.toProperty === "condition").map((c) => c.fromId);
|
|
196839
|
+
while (stack.length > 0) {
|
|
196840
|
+
const id = stack.pop();
|
|
196841
|
+
if (id === gate || seen.has(id)) continue;
|
|
196842
|
+
seen.add(id);
|
|
196843
|
+
if (!passesThrough(id)) continue;
|
|
196844
|
+
const type = nodeById.get(id)?.type;
|
|
196845
|
+
for (const c of incoming.get(id) ?? []) {
|
|
196846
|
+
if (type && catalog.getPort(type, "input", c.toProperty)?.isSignal) continue;
|
|
196847
|
+
stack.push(c.fromId);
|
|
196848
|
+
}
|
|
196849
|
+
}
|
|
196850
|
+
return seen;
|
|
196851
|
+
}, reaches2 = function(gate, targets) {
|
|
196852
|
+
if (targets.size === 0) return false;
|
|
196853
|
+
const seen = /* @__PURE__ */ new Set([gate]);
|
|
196854
|
+
const stack = (outgoing.get(gate) ?? []).map((c) => c.toId);
|
|
196855
|
+
while (stack.length > 0) {
|
|
196856
|
+
const id = stack.pop();
|
|
196857
|
+
if (targets.has(id)) return true;
|
|
196858
|
+
if (seen.has(id)) continue;
|
|
196859
|
+
seen.add(id);
|
|
196860
|
+
if (!passesThrough(id)) continue;
|
|
196861
|
+
for (const c of outgoing.get(id) ?? []) stack.push(c.toId);
|
|
196862
|
+
}
|
|
196863
|
+
return false;
|
|
196864
|
+
};
|
|
196865
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
196866
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
196867
|
+
for (const c of component.connections) {
|
|
196868
|
+
(outgoing.get(c.fromId) ?? outgoing.set(c.fromId, []).get(c.fromId)).push(c);
|
|
196869
|
+
(incoming.get(c.toId) ?? incoming.set(c.toId, []).get(c.toId)).push(c);
|
|
196870
|
+
}
|
|
196871
|
+
const isCondition = (id) => nodeById.get(id)?.type === CONDITION;
|
|
196872
|
+
const passesThrough = (id) => {
|
|
196873
|
+
const type = nodeById.get(id)?.type;
|
|
196874
|
+
const known = type ? catalog.getNode(type) : void 0;
|
|
196875
|
+
return !!known && !known.isVisual;
|
|
196876
|
+
};
|
|
196877
|
+
const gatesBySource = /* @__PURE__ */ new Map();
|
|
196878
|
+
for (const c of component.connections) {
|
|
196879
|
+
if (c.toProperty !== "eval" || !isCondition(c.toId)) continue;
|
|
196880
|
+
const key = `${c.fromId}\0${c.fromProperty}`;
|
|
196881
|
+
const gates = gatesBySource.get(key) ?? [];
|
|
196882
|
+
if (!gates.includes(c.toId)) gates.push(c.toId);
|
|
196883
|
+
gatesBySource.set(key, gates);
|
|
196884
|
+
}
|
|
196885
|
+
const reported = /* @__PURE__ */ new Set();
|
|
196886
|
+
for (const [key, gates] of gatesBySource) {
|
|
196887
|
+
if (gates.length < 2) continue;
|
|
196888
|
+
const sourceId = key.split("\0")[0];
|
|
196889
|
+
for (const reader of gates) {
|
|
196890
|
+
if (reported.has(reader)) continue;
|
|
196891
|
+
const feeds = feedingValues(reader);
|
|
196892
|
+
for (const writer of gates) {
|
|
196893
|
+
if (writer === reader) continue;
|
|
196894
|
+
if (exclusiveStates(reader, writer)) continue;
|
|
196895
|
+
if (!reaches2(writer, feeds)) continue;
|
|
196896
|
+
reported.add(reader);
|
|
196897
|
+
const r = nodeById.get(reader);
|
|
196898
|
+
const w = nodeById.get(writer);
|
|
196899
|
+
const rName = r.label ? `"${r.label}"` : "This Condition";
|
|
196900
|
+
const wName = w.label ? `"${w.label}"` : "another Condition";
|
|
196901
|
+
const sName = nodeById.get(sourceId)?.label ? `"${nodeById.get(sourceId).label}"` : "the same signal";
|
|
196902
|
+
out.push({
|
|
196903
|
+
code: "condition-reads-a-siblings-change" /* ConditionReadsASiblingsChange */,
|
|
196904
|
+
severity: "warning",
|
|
196905
|
+
message: `${rName} and ${wName} are both evaluated by ${sName}, and what ${wName} does changes what ${rName} reads. ${rName} reads its Condition after that change, whichever order the two are wired in. If it should decide on the value from before, chain them: ${rName}'s Done into ${wName}'s Evaluate, instead of both from ${sName}.`,
|
|
196906
|
+
location: {
|
|
196907
|
+
component: component.name,
|
|
196908
|
+
nodeId: r.id,
|
|
196909
|
+
nodeType: r.type,
|
|
196910
|
+
nodeLabel: r.label,
|
|
196911
|
+
port: "eval",
|
|
196912
|
+
plug: "input"
|
|
196913
|
+
},
|
|
196914
|
+
suggestion: `${r.id}.done \u2192 ${w.id}.eval`
|
|
196915
|
+
});
|
|
196916
|
+
break;
|
|
196917
|
+
}
|
|
196918
|
+
}
|
|
196919
|
+
}
|
|
196920
|
+
}
|
|
196921
|
+
return out;
|
|
196922
|
+
}
|
|
196923
|
+
};
|
|
196924
|
+
|
|
196513
196925
|
// ../noodl-editor/src/editor/src/validation/rules/layoutFillTraps.ts
|
|
196514
196926
|
var GROUP = "Group";
|
|
196515
196927
|
var CONTENT_WIDTH = /* @__PURE__ */ new Set(["contentSize", "contentWidth"]);
|
|
@@ -196617,6 +197029,97 @@ var rowOfWordsFillsItsColumn = {
|
|
|
196617
197029
|
}
|
|
196618
197030
|
};
|
|
196619
197031
|
|
|
197032
|
+
// ../noodl-editor/src/editor/src/validation/rules/clickAreaHoldsAControl.ts
|
|
197033
|
+
var CONTROL_TYPES = /* @__PURE__ */ new Map([
|
|
197034
|
+
["net.noodl.controls.button", "Button"],
|
|
197035
|
+
["net.noodl.controls.textinput", "Text Input"],
|
|
197036
|
+
["net.noodl.controls.checkbox", "Checkbox"],
|
|
197037
|
+
["net.noodl.controls.radiobutton", "Radio Button"],
|
|
197038
|
+
["net.noodl.controls.options", "Dropdown"],
|
|
197039
|
+
["net.noodl.controls.range", "Slider"]
|
|
197040
|
+
]);
|
|
197041
|
+
var CLICK = "onClick";
|
|
197042
|
+
var COMPONENT_OUTPUTS = "Component Outputs";
|
|
197043
|
+
var clickWired = (component, node) => component.connections.some((c) => c.fromId === node.id && c.fromProperty === CLICK);
|
|
197044
|
+
function reaches(component, nodeById, node, port, used) {
|
|
197045
|
+
return component.connections.some((c) => {
|
|
197046
|
+
if (c.fromId !== node.id || c.fromProperty !== port) return false;
|
|
197047
|
+
if (nodeById.get(c.toId)?.type !== COMPONENT_OUTPUTS) return true;
|
|
197048
|
+
return used === "all" || used.has(c.toProperty);
|
|
197049
|
+
});
|
|
197050
|
+
}
|
|
197051
|
+
var quoted = (node) => node.label ? ` ("${node.label}")` : "";
|
|
197052
|
+
var clickAreaHoldsAControl = {
|
|
197053
|
+
code: "click-area-holds-a-control" /* ClickAreaHoldsAControl */,
|
|
197054
|
+
description: "An element whose Click is wired holds a control, so the keyboard reaches the control and not the element.",
|
|
197055
|
+
defaultEnabled: true,
|
|
197056
|
+
run(ctx) {
|
|
197057
|
+
const out = [];
|
|
197058
|
+
const byPath = new Map(ctx.components.map((c) => [refToPath(c.component.name), c]));
|
|
197059
|
+
const memo = /* @__PURE__ */ new Map();
|
|
197060
|
+
const componentHolds = (ref, used) => {
|
|
197061
|
+
const path8 = refToPath(ref);
|
|
197062
|
+
const key = `${path8}|${[...used].sort().join(",")}`;
|
|
197063
|
+
if (memo.has(key)) return memo.get(key) ?? null;
|
|
197064
|
+
memo.set(key, null);
|
|
197065
|
+
const entry = byPath.get(path8);
|
|
197066
|
+
let found = null;
|
|
197067
|
+
if (entry) {
|
|
197068
|
+
for (const node of entry.component.nodes) {
|
|
197069
|
+
found = held(node, entry.component, entry.nodeById, used);
|
|
197070
|
+
if (found) break;
|
|
197071
|
+
}
|
|
197072
|
+
}
|
|
197073
|
+
memo.set(key, found);
|
|
197074
|
+
return found;
|
|
197075
|
+
};
|
|
197076
|
+
function held(node, component, nodeById, used) {
|
|
197077
|
+
const control = CONTROL_TYPES.get(node.type);
|
|
197078
|
+
if (control) return `a ${control}${quoted(node)}`;
|
|
197079
|
+
if (reaches(component, nodeById, node, CLICK, used)) {
|
|
197080
|
+
return `${node.label ? `"${node.label}"` : "an element"}, which answers a click of its own`;
|
|
197081
|
+
}
|
|
197082
|
+
if (isComponentRef(node.type)) {
|
|
197083
|
+
const ports = new Set(component.connections.filter((c) => c.fromId === node.id).map((c) => c.fromProperty));
|
|
197084
|
+
const usedHere = new Set([...ports].filter((port) => reaches(component, nodeById, node, port, used)));
|
|
197085
|
+
const inside = componentHolds(node.type, usedHere);
|
|
197086
|
+
if (inside) return `the component ${node.label ? `"${node.label}"` : `"${refToPath(node.type)}"`}, which holds ${inside}`;
|
|
197087
|
+
}
|
|
197088
|
+
return null;
|
|
197089
|
+
}
|
|
197090
|
+
for (const { component, nodeById } of ctx.components) {
|
|
197091
|
+
for (const area of component.nodes) {
|
|
197092
|
+
if (CONTROL_TYPES.has(area.type) || !clickWired(component, area)) continue;
|
|
197093
|
+
if (!ctx.catalog.getNode(area.type)?.isVisual) continue;
|
|
197094
|
+
let inner = null;
|
|
197095
|
+
const stack = [...area.children ?? []];
|
|
197096
|
+
while (stack.length && !inner) {
|
|
197097
|
+
const node = nodeById.get(stack.shift());
|
|
197098
|
+
if (!node) continue;
|
|
197099
|
+
inner = held(node, component, nodeById, "all");
|
|
197100
|
+
stack.push(...node.children ?? []);
|
|
197101
|
+
}
|
|
197102
|
+
if (!inner) continue;
|
|
197103
|
+
const name2 = area.label ? `"${area.label}"` : `This ${ctx.catalog.getNode(area.type)?.displayName ?? area.type}`;
|
|
197104
|
+
out.push({
|
|
197105
|
+
code: "click-area-holds-a-control" /* ClickAreaHoldsAControl */,
|
|
197106
|
+
severity: "info",
|
|
197107
|
+
message: `${name2} answers a click. It holds ${inner}. The Tab key stops on what it holds instead. A person using a keyboard or a screen reader cannot press ${area.label ? name2 : "it"} itself. If its click does something nothing inside it does, give that its own Button.`,
|
|
197108
|
+
location: {
|
|
197109
|
+
component: component.name,
|
|
197110
|
+
nodeId: area.id,
|
|
197111
|
+
nodeType: area.type,
|
|
197112
|
+
...area.label ? { nodeLabel: area.label } : {},
|
|
197113
|
+
port: CLICK,
|
|
197114
|
+
plug: "output"
|
|
197115
|
+
}
|
|
197116
|
+
});
|
|
197117
|
+
}
|
|
197118
|
+
}
|
|
197119
|
+
return out;
|
|
197120
|
+
}
|
|
197121
|
+
};
|
|
197122
|
+
|
|
196620
197123
|
// ../noodl-editor/src/editor/src/validation/rules/index.ts
|
|
196621
197124
|
var ALL_RULES = [
|
|
196622
197125
|
duplicateNodeId,
|
|
@@ -196673,6 +197176,10 @@ var ALL_RULES = [
|
|
|
196673
197176
|
// arrangement ones.
|
|
196674
197177
|
functionScriptNotCompiled,
|
|
196675
197178
|
functionNeverReruns,
|
|
197179
|
+
functionRunOnChangeUnknown,
|
|
197180
|
+
// P112-OWN018-FANOUT (ruled 2026-10-27 "Teach it and warn") — beside the Function rules: what a graph's signals do in
|
|
197181
|
+
// what order, read from the wires. Two gates off one signal, one changing what the other reads.
|
|
197182
|
+
conditionReadsASiblingsChange,
|
|
196676
197183
|
repeatedSiblingSubtree,
|
|
196677
197184
|
// DEF-025 — beside `repeatedSiblingSubtree` because both read the visual
|
|
196678
197185
|
// tree's arrangement rather than wires: that one asks whether siblings
|
|
@@ -196686,6 +197193,9 @@ var ALL_RULES = [
|
|
|
196686
197193
|
// against the shipped corpus before they were written — this one is 0 hits
|
|
196687
197194
|
// on all ten templates.
|
|
196688
197195
|
dialogWithoutName,
|
|
197196
|
+
// P121-TWO003-NESTEDBUTTON (ruled 2026-10-27) — beside the two above: what a keyboard and a screen reader reach, read
|
|
197197
|
+
// from the arrangement. An info, live, so the Problems panel and an agent's write both say it.
|
|
197198
|
+
clickAreaHoldsAControl,
|
|
196689
197199
|
// P112-OWN020-PILLWRAP / FILLGROW (ruled 2026-10-26: warn and teach) — a node with no size setting fills its parent,
|
|
196690
197200
|
// read from the arrangement like the two above, and live so a person sees it while editing.
|
|
196691
197201
|
contentRowSplitsItsChildren,
|
|
@@ -197464,6 +197974,7 @@ function validationWarningLines(state2) {
|
|
|
197464
197974
|
if (state2.kind !== "ok" || !state2.report) return [];
|
|
197465
197975
|
return state2.report.diagnostics.filter((d) => d.severity === "warning").map((d) => `${d.code} in ${d.component}${d.nodeLabel || d.nodeId ? ` (${d.nodeLabel ?? d.nodeId})` : ""}: ${d.message}`);
|
|
197466
197976
|
}
|
|
197977
|
+
var BACKEND_PREFIX = "/__nodegx/backend";
|
|
197467
197978
|
var MIME = {
|
|
197468
197979
|
".html": "text/html; charset=utf-8",
|
|
197469
197980
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -197577,6 +198088,10 @@ var PreviewServer = class {
|
|
|
197577
198088
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
197578
198089
|
const pathname = decodeURIComponent(url.pathname);
|
|
197579
198090
|
if (pathname === "/__preview/events") return this.serveEvents(req, res);
|
|
198091
|
+
const backendPort = this.sharedBackendPort();
|
|
198092
|
+
if (backendPort && (pathname === BACKEND_PREFIX || pathname.startsWith(BACKEND_PREFIX + "/"))) {
|
|
198093
|
+
return this.proxyToBackend(req, res, backendPort);
|
|
198094
|
+
}
|
|
197580
198095
|
if (pathname === "/__preview/state") {
|
|
197581
198096
|
const status = {
|
|
197582
198097
|
state: this.state,
|
|
@@ -197600,7 +198115,11 @@ var PreviewServer = class {
|
|
|
197600
198115
|
if (!bundle) return this.notFound(res);
|
|
197601
198116
|
return this.sendDynamic(res, bundle, "application/json; charset=utf-8");
|
|
197602
198117
|
}
|
|
197603
|
-
if (pathname === "/nodegx-config.json")
|
|
198118
|
+
if (pathname === "/nodegx-config.json") {
|
|
198119
|
+
const origin = backendPort ? pageOrigin(req) : null;
|
|
198120
|
+
const config = origin ? { cloudservices: { endpoint: origin + BACKEND_PREFIX } } : {};
|
|
198121
|
+
return this.sendDynamic(res, JSON.stringify(config), "application/json; charset=utf-8");
|
|
198122
|
+
}
|
|
197604
198123
|
const asset = pathname.slice(1);
|
|
197605
198124
|
if (DEPLOY_ASSETS.includes(asset)) {
|
|
197606
198125
|
return this.sendFile(req, res, deployAssetPath(asset));
|
|
@@ -197651,6 +198170,46 @@ var PreviewServer = class {
|
|
|
197651
198170
|
this.clients.delete(res);
|
|
197652
198171
|
});
|
|
197653
198172
|
}
|
|
198173
|
+
/** The backend port a SHARED preview carries, else undefined. */
|
|
198174
|
+
sharedBackendPort() {
|
|
198175
|
+
return this.access.shared && this.options.backendPort ? this.options.backendPort : void 0;
|
|
198176
|
+
}
|
|
198177
|
+
/**
|
|
198178
|
+
* P112-OWN023-PHONEPREVIEW — one request on to the backend on loopback, streamed both ways (its realtime channel is a
|
|
198179
|
+
* long `text/event-stream`). The backend is told where the page reached it (`Host`, `X-Forwarded-Prefix`), so a
|
|
198180
|
+
* File's url it answers with leads back through here; the preview's own token is not passed on.
|
|
198181
|
+
*/
|
|
198182
|
+
proxyToBackend(req, res, port) {
|
|
198183
|
+
const raw = req.url ?? "/";
|
|
198184
|
+
const rest2 = raw.slice(BACKEND_PREFIX.length) || "/";
|
|
198185
|
+
const headers = { ...req.headers };
|
|
198186
|
+
delete headers[TOKEN_HEADER];
|
|
198187
|
+
const cookie = withoutCookie(req.headers.cookie, TOKEN_COOKIE);
|
|
198188
|
+
if (cookie) headers.cookie = cookie;
|
|
198189
|
+
else delete headers.cookie;
|
|
198190
|
+
headers["x-forwarded-prefix"] = BACKEND_PREFIX;
|
|
198191
|
+
headers["x-forwarded-proto"] = "http";
|
|
198192
|
+
const peer = req.socket.remoteAddress;
|
|
198193
|
+
if (peer) headers["x-forwarded-for"] = peer;
|
|
198194
|
+
const upstream = http.request(
|
|
198195
|
+
{ host: "127.0.0.1", port, method: req.method, path: rest2.startsWith("/") ? rest2 : "/" + rest2, headers },
|
|
198196
|
+
(answer) => {
|
|
198197
|
+
res.writeHead(answer.statusCode ?? 502, answer.headers);
|
|
198198
|
+
answer.pipe(res);
|
|
198199
|
+
}
|
|
198200
|
+
);
|
|
198201
|
+
upstream.on("error", (err) => {
|
|
198202
|
+
if (res.headersSent) {
|
|
198203
|
+
res.destroy(err);
|
|
198204
|
+
return;
|
|
198205
|
+
}
|
|
198206
|
+
res.writeHead(502, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store" });
|
|
198207
|
+
res.end(`The app's backend on this computer (port ${port}) did not answer: ${err.message}
|
|
198208
|
+
`);
|
|
198209
|
+
});
|
|
198210
|
+
res.on("close", () => upstream.destroy());
|
|
198211
|
+
req.pipe(upstream);
|
|
198212
|
+
}
|
|
197654
198213
|
sendJson(res, body) {
|
|
197655
198214
|
this.sendDynamic(res, JSON.stringify(body, null, 2), "application/json; charset=utf-8");
|
|
197656
198215
|
}
|
|
@@ -197695,6 +198254,15 @@ var PreviewServer = class {
|
|
|
197695
198254
|
res.end("Not found");
|
|
197696
198255
|
}
|
|
197697
198256
|
};
|
|
198257
|
+
function pageOrigin(req) {
|
|
198258
|
+
const host = String(req.headers.host || "");
|
|
198259
|
+
return /^[A-Za-z0-9.\-[\]:]+$/.test(host) ? `http://${host}` : null;
|
|
198260
|
+
}
|
|
198261
|
+
function withoutCookie(header, name2) {
|
|
198262
|
+
if (!header) return void 0;
|
|
198263
|
+
const kept = header.split(";").map((part) => part.trim()).filter((part) => part && part.split("=")[0].trim() !== name2);
|
|
198264
|
+
return kept.length ? kept.join("; ") : void 0;
|
|
198265
|
+
}
|
|
197698
198266
|
|
|
197699
198267
|
// src/watcher.ts
|
|
197700
198268
|
var import_chokidar = __toESM(require("chokidar"));
|
|
@@ -197747,6 +198315,7 @@ Options:
|
|
|
197747
198315
|
--port <n> Port to listen on (default 8575, 0 for any free port).
|
|
197748
198316
|
--host <addr> Address to bind (default 127.0.0.1). Any address but loopback also turns
|
|
197749
198317
|
on the token: the printed URL carries it, and without it nothing is served.
|
|
198318
|
+
A phone on the same Wi-Fi then reaches the app's own backend through it too.
|
|
197750
198319
|
--token <t> Use this token rather than minting one.
|
|
197751
198320
|
--debounce <ms> Quiet period before rebuilding after a change (default 120).
|
|
197752
198321
|
--no-watch Render once and serve; do not watch for changes.
|
|
@@ -197860,7 +198429,8 @@ async function main() {
|
|
|
197860
198429
|
log(`noodl-preview \u2014 ${dir} (${format}), ${types} node types`);
|
|
197861
198430
|
const shared = args.host !== "127.0.0.1" && args.host !== "localhost";
|
|
197862
198431
|
const access = resolveAccess({ share: shared, host: args.host, token: args.token });
|
|
197863
|
-
const
|
|
198432
|
+
const backendPort = format === "v2" ? namedBackend(dir)?.port : void 0;
|
|
198433
|
+
const server = new PreviewServer({ projectDir: dir, port: args.port, host: args.host, access, backendPort });
|
|
197864
198434
|
let port;
|
|
197865
198435
|
try {
|
|
197866
198436
|
port = await server.listen();
|