@nodegx/preview 0.2.5 → 0.2.6
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 +1225 -656
- package/dist/backend/vm/provision-app.sh +41 -0
- package/dist/browser/noodl-graph-target.js +95 -95
- package/dist/browser/noodl-preview-in-page.js +23 -20
- package/dist/external/deploy/noodl.deploy.js +1 -1
- package/dist/nodegx-deploy.cjs +1000 -581
- package/dist/nodegx-deploy.inputs.json +5 -0
- package/dist/noodl-preview.cjs +820 -531
- package/dist/noodl-preview.inputs.json +4 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/nodegx-deploy.cjs
CHANGED
|
@@ -18277,6 +18277,27 @@ var init_run_on_value_change = __esm({
|
|
|
18277
18277
|
}
|
|
18278
18278
|
});
|
|
18279
18279
|
|
|
18280
|
+
// ../noodl-runtime/src/tracecause.ts
|
|
18281
|
+
function currentTraceCause(context) {
|
|
18282
|
+
const c = context;
|
|
18283
|
+
return c && c.traceEnabled ? c._currentCause || 0 : 0;
|
|
18284
|
+
}
|
|
18285
|
+
function withTraceCause(context, cause, fn) {
|
|
18286
|
+
const c = context;
|
|
18287
|
+
if (!cause || !c || !c.traceEnabled) return fn();
|
|
18288
|
+
const previous = c._currentCause;
|
|
18289
|
+
c._currentCause = cause;
|
|
18290
|
+
try {
|
|
18291
|
+
return fn();
|
|
18292
|
+
} finally {
|
|
18293
|
+
c._currentCause = previous;
|
|
18294
|
+
}
|
|
18295
|
+
}
|
|
18296
|
+
var init_tracecause = __esm({
|
|
18297
|
+
"../noodl-runtime/src/tracecause.ts"() {
|
|
18298
|
+
}
|
|
18299
|
+
});
|
|
18300
|
+
|
|
18280
18301
|
// ../noodl-runtime/src/outputproperty.ts
|
|
18281
18302
|
var require_outputproperty = __commonJS({
|
|
18282
18303
|
"../noodl-runtime/src/outputproperty.ts"(exports2, module2) {
|
|
@@ -18452,6 +18473,7 @@ var require_node2 = __commonJS({
|
|
|
18452
18473
|
init_diagnostics();
|
|
18453
18474
|
init_outcome();
|
|
18454
18475
|
init_run_on_value_change();
|
|
18476
|
+
init_tracecause();
|
|
18455
18477
|
var OutputProperty = require_outputproperty();
|
|
18456
18478
|
var NAN_INPUT_KEY = "node/nan-input/";
|
|
18457
18479
|
var SIGNAL_PULSE = Object.freeze({ __noodlSignalPulse: true });
|
|
@@ -18964,6 +18986,8 @@ var require_node2 = __commonJS({
|
|
|
18964
18986
|
});
|
|
18965
18987
|
Node17.prototype.beginOutcome = function(inputData) {
|
|
18966
18988
|
const token = { reported: void 0 };
|
|
18989
|
+
const cause = currentTraceCause(this.context);
|
|
18990
|
+
if (cause) token.cause = cause;
|
|
18967
18991
|
const scope = this.nodeScope;
|
|
18968
18992
|
const runContext = scope && scope.runContext;
|
|
18969
18993
|
if (runContext && runContext.beginStep) {
|
|
@@ -18999,6 +19023,10 @@ var require_node2 = __commonJS({
|
|
|
18999
19023
|
return;
|
|
19000
19024
|
}
|
|
19001
19025
|
token.reported = outcome;
|
|
19026
|
+
const traceContext = this.context;
|
|
19027
|
+
const tokenCause = token.cause;
|
|
19028
|
+
const causeBefore = traceContext ? traceContext._currentCause : 0;
|
|
19029
|
+
if (tokenCause && traceContext && traceContext.traceEnabled) traceContext._currentCause = tokenCause;
|
|
19002
19030
|
this._raisingForOutcome = true;
|
|
19003
19031
|
try {
|
|
19004
19032
|
if (outcome === "failure" && !(options && options.raise === false)) {
|
|
@@ -19028,6 +19056,7 @@ var require_node2 = __commonJS({
|
|
|
19028
19056
|
}
|
|
19029
19057
|
} finally {
|
|
19030
19058
|
this._raisingForOutcome = false;
|
|
19059
|
+
if (tokenCause && traceContext && traceContext.traceEnabled) traceContext._currentCause = causeBefore;
|
|
19031
19060
|
}
|
|
19032
19061
|
const step = token.step;
|
|
19033
19062
|
if (step !== void 0 && step !== null) {
|
|
@@ -19123,7 +19152,12 @@ var require_node2 = __commonJS({
|
|
|
19123
19152
|
this.flagDirty();
|
|
19124
19153
|
};
|
|
19125
19154
|
Node17.prototype.scheduleAfterInputsHaveUpdated = function(callback) {
|
|
19126
|
-
this.
|
|
19155
|
+
const cause = currentTraceCause(this.context);
|
|
19156
|
+
this._afterInputsHaveUpdatedCallbacks.push(
|
|
19157
|
+
cause ? function() {
|
|
19158
|
+
return withTraceCause(this.context, cause, () => callback.call(this));
|
|
19159
|
+
} : callback
|
|
19160
|
+
);
|
|
19127
19161
|
this.flagDirty();
|
|
19128
19162
|
};
|
|
19129
19163
|
Node17.prototype.setNodeModel = function(nodeModel) {
|
|
@@ -19226,6 +19260,60 @@ var require_node2 = __commonJS({
|
|
|
19226
19260
|
}
|
|
19227
19261
|
});
|
|
19228
19262
|
|
|
19263
|
+
// ../nodegx-project-contract/renamed-ports.ts
|
|
19264
|
+
function renamedOutput(typeName4, port5) {
|
|
19265
|
+
const renames = own.call(RENAMED_OUTPUTS, typeName4) ? RENAMED_OUTPUTS[typeName4] : void 0;
|
|
19266
|
+
return renames && own.call(renames, port5) ? renames[port5] : void 0;
|
|
19267
|
+
}
|
|
19268
|
+
var RENAMED_OUTPUTS, own;
|
|
19269
|
+
var init_renamed_ports = __esm({
|
|
19270
|
+
"../nodegx-project-contract/renamed-ports.ts"() {
|
|
19271
|
+
"use strict";
|
|
19272
|
+
RENAMED_OUTPUTS = {
|
|
19273
|
+
AddDbModelRelation: { relationAdded: "done" },
|
|
19274
|
+
Boolean: { stored: "completed" },
|
|
19275
|
+
CloudFunction2: { success: "done" },
|
|
19276
|
+
CollectionClear: { modified: "done" },
|
|
19277
|
+
CollectionInsert: { modified: "done" },
|
|
19278
|
+
CollectionNew: { created: "done" },
|
|
19279
|
+
CollectionRemove: { modified: "done" },
|
|
19280
|
+
Color: { stored: "completed" },
|
|
19281
|
+
DeleteDbModelProperties: { deleted: "done" },
|
|
19282
|
+
"Event Sender": { sent: "done" },
|
|
19283
|
+
NavigationClosePopup: { success: "done" },
|
|
19284
|
+
NewDbModelProperties: { created: "done" },
|
|
19285
|
+
NewModel: { created: "done" },
|
|
19286
|
+
Number: { stored: "completed" },
|
|
19287
|
+
"Open File Picker": { success: "done", cancelled: "unchanged" },
|
|
19288
|
+
PageStackNavigate: { navigated: "done" },
|
|
19289
|
+
PageStackNavigateBack: { success: "done" },
|
|
19290
|
+
PageStackNavigateToPath: { success: "done" },
|
|
19291
|
+
RemoveDbModelRelation: { relationRemoved: "done" },
|
|
19292
|
+
RouterNavigate: { navigated: "done" },
|
|
19293
|
+
RunTasks: { success: "done" },
|
|
19294
|
+
SetDbModelProperties: { stored: "done" },
|
|
19295
|
+
SetModelProperties: { stored: "done" },
|
|
19296
|
+
"Sign File URL": { success: "done" },
|
|
19297
|
+
String: { stored: "completed" },
|
|
19298
|
+
"Unique Id": { generated: "done" },
|
|
19299
|
+
"Upload File": { success: "done" },
|
|
19300
|
+
"net.noodl.HTTP": { success: "done" },
|
|
19301
|
+
"net.noodl.SetComponentObjectProperties": { stored: "done" },
|
|
19302
|
+
"net.noodl.SetParentComponentObjectProperties": { stored: "done" },
|
|
19303
|
+
"net.noodl.externallink": { success: "done" },
|
|
19304
|
+
"net.noodl.user.LogIn": { success: "done" },
|
|
19305
|
+
"net.noodl.user.LogOut": { success: "done" },
|
|
19306
|
+
"net.noodl.user.RequestMagicLink": { success: "done" },
|
|
19307
|
+
"net.noodl.user.SetUserProperties": { success: "done" },
|
|
19308
|
+
"net.noodl.user.SignInWith": { success: "done" },
|
|
19309
|
+
"net.noodl.user.SignUp": { success: "done" },
|
|
19310
|
+
"noodl.cloud.response": { sent: "done" },
|
|
19311
|
+
"noodl.cloud.sendemail": { sent: "done", failed: "failure" }
|
|
19312
|
+
};
|
|
19313
|
+
own = Object.prototype.hasOwnProperty;
|
|
19314
|
+
}
|
|
19315
|
+
});
|
|
19316
|
+
|
|
19229
19317
|
// ../noodl-runtime/src/guid.ts
|
|
19230
19318
|
var require_guid = __commonJS({
|
|
19231
19319
|
"../noodl-runtime/src/guid.ts"(exports2, module2) {
|
|
@@ -19243,6 +19331,7 @@ var require_guid = __commonJS({
|
|
|
19243
19331
|
var require_nodescope = __commonJS({
|
|
19244
19332
|
"../noodl-runtime/src/nodescope.ts"(exports2, module2) {
|
|
19245
19333
|
"use strict";
|
|
19334
|
+
init_renamed_ports();
|
|
19246
19335
|
var guid5 = require_guid();
|
|
19247
19336
|
function hasNode(map2, id) {
|
|
19248
19337
|
return Object.prototype.hasOwnProperty.call(map2, id);
|
|
@@ -19275,11 +19364,15 @@ var require_nodescope = __commonJS({
|
|
|
19275
19364
|
var sourceNode = this.getNodeWithId(connectionData.sourceId), targetNode = this.getNodeWithId(connectionData.targetId);
|
|
19276
19365
|
targetNode.registerInputIfNeeded(connectionData.targetPort);
|
|
19277
19366
|
sourceNode.registerOutputIfNeeded(connectionData.sourcePort);
|
|
19278
|
-
targetNode.connectInput(connectionData.targetPort, sourceNode, connectionData.sourcePort);
|
|
19367
|
+
targetNode.connectInput(connectionData.targetPort, sourceNode, sourcePortOf(sourceNode, connectionData.sourcePort));
|
|
19279
19368
|
} catch (e) {
|
|
19280
19369
|
console.error(e.message);
|
|
19281
19370
|
}
|
|
19282
19371
|
};
|
|
19372
|
+
function sourcePortOf(sourceNode, port5) {
|
|
19373
|
+
if (sourceNode.hasOutput(port5)) return port5;
|
|
19374
|
+
return renamedOutput(sourceNode.name, port5) ?? port5;
|
|
19375
|
+
}
|
|
19283
19376
|
NodeScope.prototype.setNodeParameters = function(node, nodeModel) {
|
|
19284
19377
|
const variant = this.context.variants.getVariant(nodeModel.type, nodeModel.variant);
|
|
19285
19378
|
if (variant) {
|
|
@@ -19287,10 +19380,10 @@ var require_nodescope = __commonJS({
|
|
|
19287
19380
|
} else {
|
|
19288
19381
|
const parameters = nodeModel.parameters;
|
|
19289
19382
|
var inputNames = Object.keys(parameters);
|
|
19290
|
-
const
|
|
19383
|
+
const RUN_ON_CHANGE_PREFIX4 = "runOnChange-";
|
|
19291
19384
|
const metadata = this.context.nodeRegister.hasNode(node.name) ? this.context.nodeRegister.getNodeMetadata(node.name) : void 0;
|
|
19292
19385
|
const priorityOf = function(name2) {
|
|
19293
|
-
if (name2.indexOf(
|
|
19386
|
+
if (name2.indexOf(RUN_ON_CHANGE_PREFIX4) === 0) return Number.MAX_SAFE_INTEGER;
|
|
19294
19387
|
const input2 = metadata ? metadata.inputs[name2] : void 0;
|
|
19295
19388
|
return input2 ? input2.inputPriority : 0;
|
|
19296
19389
|
};
|
|
@@ -19464,7 +19557,9 @@ var require_nodescope = __commonJS({
|
|
|
19464
19557
|
};
|
|
19465
19558
|
NodeScope.prototype.removeConnection = function(connectionModel) {
|
|
19466
19559
|
var targetNode = this.getNodeWithId(connectionModel.targetId);
|
|
19467
|
-
|
|
19560
|
+
const sourceNode = this.hasNodeWithId(connectionModel.sourceId) ? this.getNodeWithId(connectionModel.sourceId) : void 0;
|
|
19561
|
+
const sourcePort = sourceNode ? sourcePortOf(sourceNode, connectionModel.sourcePort) : connectionModel.sourcePort;
|
|
19562
|
+
targetNode.removeInputConnection(connectionModel.targetPort, connectionModel.sourceId, sourcePort);
|
|
19468
19563
|
};
|
|
19469
19564
|
NodeScope.prototype.setComponentModel = async function(componentModel) {
|
|
19470
19565
|
this.componentModel = componentModel;
|
|
@@ -27078,7 +27173,9 @@ var init_function_ports = __esm({
|
|
|
27078
27173
|
INPUT_PATTERNS = [/Inputs\.([A-Za-z0-9_]+)/g, /Inputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]/g];
|
|
27079
27174
|
SIGNAL_PATTERNS = [
|
|
27080
27175
|
/Outputs\.([A-Za-z0-9_]+)\s*\(\s*\)/g,
|
|
27081
|
-
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\(\s*\)/g
|
|
27176
|
+
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\(\s*\)/g,
|
|
27177
|
+
/Outputs\.([A-Za-z0-9_]+)\.send\s*\(\s*\)/g,
|
|
27178
|
+
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\.send\s*\(\s*\)/g
|
|
27082
27179
|
];
|
|
27083
27180
|
OUTPUT_PATTERNS = [/Outputs\.([A-Za-z0-9_]+)/g, /Outputs\s*\[\s*"([^"]*)"\s*\]/g];
|
|
27084
27181
|
REGEX_AFTER = /* @__PURE__ */ new Set(["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "+", "-", "*", "%", "<", ">", "~", "^"]);
|
|
@@ -27095,6 +27192,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27095
27192
|
init_functionDiagnostics();
|
|
27096
27193
|
init_scriptfiles();
|
|
27097
27194
|
init_function_ports();
|
|
27195
|
+
init_tracecause();
|
|
27098
27196
|
var Node17 = require_node2();
|
|
27099
27197
|
var JavascriptNodeParser4 = require_javascriptnodeparser();
|
|
27100
27198
|
var { logJavaScriptNodeError: logJavaScriptNodeError2, thrownMessage: thrownMessage3 } = (init_utils(), __toCommonJS(utils_exports));
|
|
@@ -27464,11 +27562,12 @@ var require_simplejavascript = __commonJS({
|
|
|
27464
27562
|
}
|
|
27465
27563
|
const inputs = this._internal.inputValues;
|
|
27466
27564
|
const outputs2 = this._internal.outputValuesProxy;
|
|
27565
|
+
const runCause = currentTraceCause(this.context);
|
|
27467
27566
|
placingSignalPorts(this).forEach((key) => {
|
|
27468
27567
|
const _sendSignal = () => {
|
|
27469
27568
|
this._internal.outputWrites.add(key.substring("out-".length));
|
|
27470
27569
|
publishHeldWrites(this);
|
|
27471
|
-
if (this.hasOutput(key)) this.sendSignalOnOutput(key);
|
|
27570
|
+
if (this.hasOutput(key)) withTraceCause(this.context, runCause, () => this.sendSignalOnOutput(key));
|
|
27472
27571
|
};
|
|
27473
27572
|
const signalValue = _sendSignal;
|
|
27474
27573
|
signalValue.send = _sendSignal;
|
|
@@ -27493,7 +27592,7 @@ var require_simplejavascript = __commonJS({
|
|
|
27493
27592
|
this._internal.lastError = "";
|
|
27494
27593
|
this.flagOutputDirty("error");
|
|
27495
27594
|
}
|
|
27496
|
-
this.sendSignalOnOutput("success");
|
|
27595
|
+
withTraceCause(this.context, runCause, () => this.sendSignalOnOutput("success"));
|
|
27497
27596
|
reportOutcomes(this, tokens, "done");
|
|
27498
27597
|
}
|
|
27499
27598
|
} catch (e) {
|
|
@@ -35613,6 +35712,466 @@ var init_TokenLifecycle = __esm({
|
|
|
35613
35712
|
}
|
|
35614
35713
|
});
|
|
35615
35714
|
|
|
35715
|
+
// ../noodl-runtime/src/api/backends/ParseAuthAdapter.ts
|
|
35716
|
+
function pageSays(response, phrase) {
|
|
35717
|
+
return typeof response === "string" && response.indexOf(phrase) !== -1;
|
|
35718
|
+
}
|
|
35719
|
+
function htmlPageError(error, invalidPhrase, invalidMessage, fallback) {
|
|
35720
|
+
if (typeof error !== "string" || error.indexOf("<") === -1) return error || fallback;
|
|
35721
|
+
return error.indexOf(invalidPhrase) !== -1 ? invalidMessage : fallback;
|
|
35722
|
+
}
|
|
35723
|
+
function sessionTokenOf(session) {
|
|
35724
|
+
return session ? session.sessionToken : void 0;
|
|
35725
|
+
}
|
|
35726
|
+
var guid, HANDOFF_PARAM, AUTH_ERROR_PARAM, SESSION_MOVED_MESSAGE, ParseAuthAdapter;
|
|
35727
|
+
var init_ParseAuthAdapter = __esm({
|
|
35728
|
+
"../noodl-runtime/src/api/backends/ParseAuthAdapter.ts"() {
|
|
35729
|
+
init_AuthEvents();
|
|
35730
|
+
init_SessionStore();
|
|
35731
|
+
init_TokenLifecycle();
|
|
35732
|
+
guid = require_guid();
|
|
35733
|
+
HANDOFF_PARAM = "nodegx_auth";
|
|
35734
|
+
AUTH_ERROR_PARAM = "nodegx_auth_error";
|
|
35735
|
+
SESSION_MOVED_MESSAGE = "Someone signed in or out while this was being checked, so its answer was not used. Check again.";
|
|
35736
|
+
ParseAuthAdapter = class extends AuthEvents {
|
|
35737
|
+
constructor(options) {
|
|
35738
|
+
super();
|
|
35739
|
+
/** BAK-004: the state of a sign-in returning from a provider or a magic link. */
|
|
35740
|
+
this.oauthReturn = { inProgress: false };
|
|
35741
|
+
/**
|
|
35742
|
+
* One store and one controller per backend, keyed by application id.
|
|
35743
|
+
*
|
|
35744
|
+
* Per handle rather than per adapter because the contract's whole point is
|
|
35745
|
+
* that a project may have more than one backend, and two backends have two
|
|
35746
|
+
* sessions — the phase decided explicitly that they are never shared.
|
|
35747
|
+
*/
|
|
35748
|
+
this.stores = /* @__PURE__ */ new Map();
|
|
35749
|
+
this.controllers = /* @__PURE__ */ new Map();
|
|
35750
|
+
this.serializeObject = options.serializeObject;
|
|
35751
|
+
this.createStore = options.createStore || parseSessionStore;
|
|
35752
|
+
this.lifecycle = options.lifecycle || { kind: "eternal" };
|
|
35753
|
+
}
|
|
35754
|
+
// ── Session storage ──────────────────────────────────────────────────────
|
|
35755
|
+
sessionStore(handle) {
|
|
35756
|
+
const appId = handle.publicToken;
|
|
35757
|
+
const cacheKey = String(appId);
|
|
35758
|
+
let store2 = this.stores.get(cacheKey);
|
|
35759
|
+
if (store2 === void 0) {
|
|
35760
|
+
store2 = this.createStore(appId);
|
|
35761
|
+
this.stores.set(cacheKey, store2);
|
|
35762
|
+
}
|
|
35763
|
+
return store2;
|
|
35764
|
+
}
|
|
35765
|
+
/**
|
|
35766
|
+
* The lifecycle controller for this backend.
|
|
35767
|
+
*
|
|
35768
|
+
* On Parse this is an `eternal` controller: it arms no timer, subscribes to no
|
|
35769
|
+
* storage events, and its gate is a synchronous pass-through. It is on the
|
|
35770
|
+
* request path anyway, so that BCN-004 adds a `performRefresh` rather than a
|
|
35771
|
+
* call site.
|
|
35772
|
+
*/
|
|
35773
|
+
lifecycleController(handle) {
|
|
35774
|
+
const cacheKey = String(handle.publicToken);
|
|
35775
|
+
let controller = this.controllers.get(cacheKey);
|
|
35776
|
+
if (controller === void 0) {
|
|
35777
|
+
controller = new TokenLifecycleController({
|
|
35778
|
+
lifecycle: this.lifecycle,
|
|
35779
|
+
store: this.sessionStore(handle),
|
|
35780
|
+
onSessionLost: (reason) => {
|
|
35781
|
+
this.emitAuthEvent("sessionChanged", void 0);
|
|
35782
|
+
this.emitAuthEvent("sessionLost", reason);
|
|
35783
|
+
},
|
|
35784
|
+
onSessionClearedElsewhere: () => {
|
|
35785
|
+
this.emitAuthEvent("sessionChanged", void 0);
|
|
35786
|
+
this.emitAuthEvent("loggedOut");
|
|
35787
|
+
}
|
|
35788
|
+
});
|
|
35789
|
+
controller.start();
|
|
35790
|
+
this.controllers.set(cacheKey, controller);
|
|
35791
|
+
}
|
|
35792
|
+
return controller;
|
|
35793
|
+
}
|
|
35794
|
+
/** The stored session, or `undefined`. Not a contract method. */
|
|
35795
|
+
getCurrentUser(handle) {
|
|
35796
|
+
return this.sessionStore(handle).read();
|
|
35797
|
+
}
|
|
35798
|
+
setSession(handle, session) {
|
|
35799
|
+
this.sessionStore(handle).write(session);
|
|
35800
|
+
this.lifecycleController(handle).sessionChanged();
|
|
35801
|
+
this.emitAuthEvent("sessionChanged", session);
|
|
35802
|
+
}
|
|
35803
|
+
clearSession(handle) {
|
|
35804
|
+
this.sessionStore(handle).clear();
|
|
35805
|
+
this.lifecycleController(handle).sessionChanged();
|
|
35806
|
+
this.emitAuthEvent("sessionChanged", void 0);
|
|
35807
|
+
}
|
|
35808
|
+
// ── The wire ─────────────────────────────────────────────────────────────
|
|
35809
|
+
/**
|
|
35810
|
+
* The Parse auth seam. Moved from `userservice.ts:198` with one substitution:
|
|
35811
|
+
* `this.endpoint`/`this.appId` became `handle.url`/`handle.publicToken`.
|
|
35812
|
+
*
|
|
35813
|
+
* It is deliberately *not* `ParseWireAdapter._makeRequest`. The two differ in
|
|
35814
|
+
* three ways that all matter: this one sends `X-Parse-Installation-Id`, never
|
|
35815
|
+
* sends a master key, and hands its error callback Parse's `code` (the `209`
|
|
35816
|
+
* the session check turns on). Merging them would be a rewrite in a task
|
|
35817
|
+
* whose value is that nothing was rewritten.
|
|
35818
|
+
*/
|
|
35819
|
+
_makeRequest(handle, path10, options) {
|
|
35820
|
+
if (!handle.url) {
|
|
35821
|
+
if (options.error) {
|
|
35822
|
+
options.error({ error: "No active cloud service", status: 0 });
|
|
35823
|
+
}
|
|
35824
|
+
return;
|
|
35825
|
+
}
|
|
35826
|
+
const store2 = this.sessionStore(handle);
|
|
35827
|
+
this.lifecycleController(handle).withSession((session) => {
|
|
35828
|
+
const xhr = new XMLHttpRequest();
|
|
35829
|
+
xhr.onreadystatechange = function() {
|
|
35830
|
+
if (xhr.readyState === 4) {
|
|
35831
|
+
let json2;
|
|
35832
|
+
try {
|
|
35833
|
+
json2 = JSON.parse(xhr.response);
|
|
35834
|
+
} catch (e) {
|
|
35835
|
+
}
|
|
35836
|
+
if (xhr.status === 200 || xhr.status === 201) {
|
|
35837
|
+
options.success(json2 || xhr.response);
|
|
35838
|
+
} else options.error(json2 || { error: xhr.responseText, status: xhr.status });
|
|
35839
|
+
}
|
|
35840
|
+
};
|
|
35841
|
+
xhr.open(options.method || "GET", handle.url + path10, true);
|
|
35842
|
+
xhr.setRequestHeader("X-Parse-Application-Id", handle.publicToken);
|
|
35843
|
+
const iidKey = parseInstallationIdKey(handle.publicToken);
|
|
35844
|
+
let _iid = store2.readKey(iidKey);
|
|
35845
|
+
if (_iid === void 0) {
|
|
35846
|
+
_iid = guid();
|
|
35847
|
+
store2.writeKey(iidKey, _iid);
|
|
35848
|
+
}
|
|
35849
|
+
xhr.setRequestHeader("X-Parse-Installation-Id", _iid);
|
|
35850
|
+
if (options.sessionToken) xhr.setRequestHeader("X-Parse-Session-Token", options.sessionToken);
|
|
35851
|
+
else if (session !== void 0) {
|
|
35852
|
+
xhr.setRequestHeader("X-Parse-Session-Token", session.sessionToken);
|
|
35853
|
+
}
|
|
35854
|
+
xhr.setRequestHeader("Content-Type", "application/json");
|
|
35855
|
+
xhr.send(JSON.stringify(options.content));
|
|
35856
|
+
});
|
|
35857
|
+
}
|
|
35858
|
+
// ── The ten ──────────────────────────────────────────────────────────────
|
|
35859
|
+
logIn(handle, options) {
|
|
35860
|
+
this._makeRequest(handle, "/login", {
|
|
35861
|
+
method: "POST",
|
|
35862
|
+
content: {
|
|
35863
|
+
username: options.username,
|
|
35864
|
+
password: options.password,
|
|
35865
|
+
_method: "GET"
|
|
35866
|
+
},
|
|
35867
|
+
success: (response) => {
|
|
35868
|
+
this.setSession(handle, response);
|
|
35869
|
+
options.success(response);
|
|
35870
|
+
this.emitAuthEvent("loggedIn", response);
|
|
35871
|
+
},
|
|
35872
|
+
error: (e) => {
|
|
35873
|
+
options.error(e.error);
|
|
35874
|
+
}
|
|
35875
|
+
});
|
|
35876
|
+
}
|
|
35877
|
+
logOut(handle, options) {
|
|
35878
|
+
this._makeRequest(handle, "/logout", {
|
|
35879
|
+
method: "POST",
|
|
35880
|
+
content: {},
|
|
35881
|
+
success: () => {
|
|
35882
|
+
this.clearSession(handle);
|
|
35883
|
+
options.success();
|
|
35884
|
+
this.emitAuthEvent("loggedOut");
|
|
35885
|
+
},
|
|
35886
|
+
error: (e) => {
|
|
35887
|
+
options.error(e.error);
|
|
35888
|
+
}
|
|
35889
|
+
});
|
|
35890
|
+
}
|
|
35891
|
+
signUp(handle, options) {
|
|
35892
|
+
const additionalUserProps = options.properties ? this.serializeObject({ ...options.properties }, "_User") : {};
|
|
35893
|
+
this._makeRequest(handle, "/users", {
|
|
35894
|
+
method: "POST",
|
|
35895
|
+
content: Object.assign({}, additionalUserProps, {
|
|
35896
|
+
username: options.username,
|
|
35897
|
+
password: options.password,
|
|
35898
|
+
email: options.email
|
|
35899
|
+
}),
|
|
35900
|
+
success: (response) => {
|
|
35901
|
+
const identity2 = { username: options.username };
|
|
35902
|
+
if (options.email !== void 0) identity2.email = options.email;
|
|
35903
|
+
const _cu = Object.assign(response, identity2, options.properties);
|
|
35904
|
+
if (_cu.emailVerified === void 0) _cu.emailVerified = false;
|
|
35905
|
+
this.setSession(handle, _cu);
|
|
35906
|
+
options.success(response);
|
|
35907
|
+
this.emitAuthEvent("loggedIn", _cu);
|
|
35908
|
+
},
|
|
35909
|
+
error: (e) => {
|
|
35910
|
+
options.error(e.error);
|
|
35911
|
+
}
|
|
35912
|
+
});
|
|
35913
|
+
}
|
|
35914
|
+
fetchCurrentUser(handle, options) {
|
|
35915
|
+
const asked = sessionTokenOf(this.getCurrentUser(handle));
|
|
35916
|
+
this._makeRequest(handle, "/users/me", {
|
|
35917
|
+
method: "GET",
|
|
35918
|
+
sessionToken: options.sessionToken,
|
|
35919
|
+
success: (response) => {
|
|
35920
|
+
if (sessionTokenOf(this.getCurrentUser(handle)) !== asked) {
|
|
35921
|
+
options.error(SESSION_MOVED_MESSAGE);
|
|
35922
|
+
return;
|
|
35923
|
+
}
|
|
35924
|
+
this.setSession(handle, response);
|
|
35925
|
+
this.emitAuthEvent("sessionGained", response);
|
|
35926
|
+
options.success(response);
|
|
35927
|
+
},
|
|
35928
|
+
error: (e) => {
|
|
35929
|
+
if (sessionTokenOf(this.getCurrentUser(handle)) !== asked) {
|
|
35930
|
+
options.error(SESSION_MOVED_MESSAGE);
|
|
35931
|
+
return;
|
|
35932
|
+
}
|
|
35933
|
+
if (e.code === 209) {
|
|
35934
|
+
this.sessionStore(handle).clear();
|
|
35935
|
+
this.emitAuthEvent("sessionLost");
|
|
35936
|
+
}
|
|
35937
|
+
options.error(e.error);
|
|
35938
|
+
}
|
|
35939
|
+
});
|
|
35940
|
+
}
|
|
35941
|
+
verifyEmail(handle, options) {
|
|
35942
|
+
this._makeRequest(
|
|
35943
|
+
handle,
|
|
35944
|
+
"/apps/" + handle.publicToken + "/verify_email?username=" + options.username + "&token=" + options.token,
|
|
35945
|
+
{
|
|
35946
|
+
method: "GET",
|
|
35947
|
+
// This endpoint answers with an HTML page rather than JSON, so the outcome has to be
|
|
35948
|
+
// read out of the page's text.
|
|
35949
|
+
success: (response) => {
|
|
35950
|
+
if (pageSays(response, "Successfully verified your email")) {
|
|
35951
|
+
options.success();
|
|
35952
|
+
} else if (pageSays(response, "Invalid Verification Link")) {
|
|
35953
|
+
options.error("Invalid verification token");
|
|
35954
|
+
} else {
|
|
35955
|
+
options.error("Failed to verify email");
|
|
35956
|
+
}
|
|
35957
|
+
},
|
|
35958
|
+
error: (e) => {
|
|
35959
|
+
options.error(htmlPageError(e.error, "Invalid Verification Link", "Invalid verification token", "Failed to verify email"));
|
|
35960
|
+
}
|
|
35961
|
+
}
|
|
35962
|
+
);
|
|
35963
|
+
}
|
|
35964
|
+
sendEmailVerification(handle, options) {
|
|
35965
|
+
this._makeRequest(handle, "/verificationEmailRequest", {
|
|
35966
|
+
method: "POST",
|
|
35967
|
+
content: { email: options.email },
|
|
35968
|
+
success: () => {
|
|
35969
|
+
options.success();
|
|
35970
|
+
},
|
|
35971
|
+
error: (e) => {
|
|
35972
|
+
options.error(e.error);
|
|
35973
|
+
}
|
|
35974
|
+
});
|
|
35975
|
+
}
|
|
35976
|
+
resetPassword(handle, options) {
|
|
35977
|
+
this._makeRequest(handle, "/apps/" + handle.publicToken + "/request_password_reset", {
|
|
35978
|
+
method: "POST",
|
|
35979
|
+
content: {
|
|
35980
|
+
username: options.username,
|
|
35981
|
+
token: options.token,
|
|
35982
|
+
new_password: options.newPassword
|
|
35983
|
+
},
|
|
35984
|
+
success: (response) => {
|
|
35985
|
+
if (pageSays(response, "Password successfully reset") || pageSays(response, "Successfully updated your password")) {
|
|
35986
|
+
options.success();
|
|
35987
|
+
} else if (pageSays(response, "Invalid Link")) {
|
|
35988
|
+
options.error("Invalid or expired reset link");
|
|
35989
|
+
} else {
|
|
35990
|
+
options.error("Failed to reset password");
|
|
35991
|
+
}
|
|
35992
|
+
},
|
|
35993
|
+
error: (e) => {
|
|
35994
|
+
options.error(htmlPageError(e.error, "Invalid Link", "Invalid or expired reset link", "Failed to reset password"));
|
|
35995
|
+
}
|
|
35996
|
+
});
|
|
35997
|
+
}
|
|
35998
|
+
requestPasswordReset(handle, options) {
|
|
35999
|
+
this._makeRequest(handle, "/requestPasswordReset", {
|
|
36000
|
+
method: "POST",
|
|
36001
|
+
content: { email: options.email },
|
|
36002
|
+
success: () => {
|
|
36003
|
+
options.success();
|
|
36004
|
+
},
|
|
36005
|
+
error: (e) => {
|
|
36006
|
+
options.error(e.error);
|
|
36007
|
+
}
|
|
36008
|
+
});
|
|
36009
|
+
}
|
|
36010
|
+
/**
|
|
36011
|
+
* Send the browser to a provider. This NAVIGATES AWAY: nothing after it runs,
|
|
36012
|
+
* and the result arrives on a later page load through {@link consumeAuthReturn}.
|
|
36013
|
+
*
|
|
36014
|
+
* `redirect` defaults to the current page (minus any leftover sign-in
|
|
36015
|
+
* parameters), so the user comes back where they were. The backend refuses
|
|
36016
|
+
* any target that is neither same-origin nor on its `redirectAllowList` — if
|
|
36017
|
+
* sign-in dead-ends with "origin not allowed", that list is the fix.
|
|
36018
|
+
*/
|
|
36019
|
+
signInWithProvider(handle, options) {
|
|
36020
|
+
if (!handle.url) {
|
|
36021
|
+
if (options.error) options.error("No active cloud service");
|
|
36022
|
+
return;
|
|
36023
|
+
}
|
|
36024
|
+
if (!options.provider) {
|
|
36025
|
+
if (options.error) options.error("Sign In With: no provider was set.");
|
|
36026
|
+
return;
|
|
36027
|
+
}
|
|
36028
|
+
const redirect = options.redirect || this._currentUrlWithoutAuthParams();
|
|
36029
|
+
const url = `${handle.url}/oauth/${encodeURIComponent(options.provider)}/start?redirect=${encodeURIComponent(redirect)}`;
|
|
36030
|
+
window.location.href = url;
|
|
36031
|
+
}
|
|
36032
|
+
/**
|
|
36033
|
+
* Ask the backend to email a one-click sign-in link.
|
|
36034
|
+
*
|
|
36035
|
+
* Succeeds identically for a known and an unknown address — the endpoint is
|
|
36036
|
+
* anonymous and anti-enumerating by design, so "success" here means "the
|
|
36037
|
+
* request was accepted", never "an account exists". The node's help text says
|
|
36038
|
+
* so, because a UI that claims "check your inbox" only for real accounts
|
|
36039
|
+
* re-creates the oracle the endpoint removed.
|
|
36040
|
+
*/
|
|
36041
|
+
requestMagicLink(handle, options) {
|
|
36042
|
+
this._makeRequest(handle, "/auth/magic-link", {
|
|
36043
|
+
method: "POST",
|
|
36044
|
+
content: { email: options.email, redirect: options.redirect || this._currentUrlWithoutAuthParams() },
|
|
36045
|
+
success: () => options.success(),
|
|
36046
|
+
error: (e) => options.error(e.error)
|
|
36047
|
+
});
|
|
36048
|
+
}
|
|
36049
|
+
// ── Not contract, and deliberately so ────────────────────────────────────
|
|
36050
|
+
/**
|
|
36051
|
+
* Write user-profile fields onto the signed-in user.
|
|
36052
|
+
*
|
|
36053
|
+
* Not one of the ten. It is the `Set User Properties` node's whole
|
|
36054
|
+
* implementation, and BCN-006's Desired State §3 makes it schema-driven across
|
|
36055
|
+
* five backends — which is step 6, not this pass. It moves with the wire
|
|
36056
|
+
* because leaving it behind would have left a second `_makeRequest` in
|
|
36057
|
+
* `userservice.ts`, and the entire claim of this move is that there is one.
|
|
36058
|
+
*/
|
|
36059
|
+
setUserProperties(handle, options) {
|
|
36060
|
+
const _cu = this.getCurrentUser(handle);
|
|
36061
|
+
if (_cu === void 0) {
|
|
36062
|
+
options.error("Nobody is signed in.");
|
|
36063
|
+
return;
|
|
36064
|
+
}
|
|
36065
|
+
{
|
|
36066
|
+
const propsToSave = this.serializeObject({ ...options.properties }, "_User");
|
|
36067
|
+
const _content = Object.assign(
|
|
36068
|
+
{},
|
|
36069
|
+
{ email: options.email, username: options.username },
|
|
36070
|
+
propsToSave
|
|
36071
|
+
);
|
|
36072
|
+
delete _content.emailVerified;
|
|
36073
|
+
delete _content.createdAt;
|
|
36074
|
+
delete _content.updatedAt;
|
|
36075
|
+
this._makeRequest(handle, "/users/" + _cu.objectId, {
|
|
36076
|
+
method: "PUT",
|
|
36077
|
+
content: _content,
|
|
36078
|
+
success: (response) => {
|
|
36079
|
+
Object.assign(_cu, _content);
|
|
36080
|
+
this.setSession(handle, _cu);
|
|
36081
|
+
options.success(response);
|
|
36082
|
+
},
|
|
36083
|
+
error: (e) => {
|
|
36084
|
+
options.error(e.error);
|
|
36085
|
+
}
|
|
36086
|
+
});
|
|
36087
|
+
}
|
|
36088
|
+
}
|
|
36089
|
+
/** The sign-in methods this backend offers — for rendering a set of buttons. */
|
|
36090
|
+
listAuthProviders(handle, options) {
|
|
36091
|
+
this._makeRequest(handle, "/auth/providers", {
|
|
36092
|
+
method: "GET",
|
|
36093
|
+
success: (response) => options.success(response),
|
|
36094
|
+
error: (e) => options.error(e.error)
|
|
36095
|
+
});
|
|
36096
|
+
}
|
|
36097
|
+
// ── BAK-004 — the provider return leg ────────────────────────────────────
|
|
36098
|
+
/**
|
|
36099
|
+
* Look for a sign-in result in the current URL and act on it.
|
|
36100
|
+
*
|
|
36101
|
+
* Returns true when this page load IS a sign-in return, so the caller can skip
|
|
36102
|
+
* its ordinary stale-session validation.
|
|
36103
|
+
*
|
|
36104
|
+
* **Per backend, not contract.** BCN-006 step 5 is four different redirect
|
|
36105
|
+
* shapes — Parse `authData`, Directus SSO, Supabase, PocketBase
|
|
36106
|
+
* `auth-with-oauth2` — and this is the first of the four. What they have in
|
|
36107
|
+
* common is only the *outcome*: a session, established. That is where the
|
|
36108
|
+
* contract's surface is, and it is `signInWithProvider` plus a `loggedIn`.
|
|
36109
|
+
*/
|
|
36110
|
+
consumeAuthReturn(handle) {
|
|
36111
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
36112
|
+
let params;
|
|
36113
|
+
try {
|
|
36114
|
+
params = new URLSearchParams(window.location.search);
|
|
36115
|
+
} catch (e) {
|
|
36116
|
+
return false;
|
|
36117
|
+
}
|
|
36118
|
+
const code = params.get(HANDOFF_PARAM);
|
|
36119
|
+
const error = params.get(AUTH_ERROR_PARAM);
|
|
36120
|
+
if (!code && !error) return false;
|
|
36121
|
+
params.delete(HANDOFF_PARAM);
|
|
36122
|
+
params.delete(AUTH_ERROR_PARAM);
|
|
36123
|
+
this._stripAuthParamsFromUrl(params);
|
|
36124
|
+
if (error) {
|
|
36125
|
+
this.oauthReturn = { inProgress: false, succeeded: false, error };
|
|
36126
|
+
setTimeout(() => this.emitAuthEvent("oauthReturn", this.oauthReturn), 0);
|
|
36127
|
+
return true;
|
|
36128
|
+
}
|
|
36129
|
+
this.oauthReturn = { inProgress: true };
|
|
36130
|
+
this._makeRequest(handle, "/oauth/exchange", {
|
|
36131
|
+
method: "POST",
|
|
36132
|
+
content: { code },
|
|
36133
|
+
success: (response) => {
|
|
36134
|
+
this.setSession(handle, response);
|
|
36135
|
+
this.oauthReturn = {
|
|
36136
|
+
inProgress: false,
|
|
36137
|
+
succeeded: true,
|
|
36138
|
+
outcome: response.authOutcome,
|
|
36139
|
+
notice: response.authNotice || void 0
|
|
36140
|
+
};
|
|
36141
|
+
this.emitAuthEvent("oauthReturn", this.oauthReturn);
|
|
36142
|
+
this.emitAuthEvent("loggedIn", response);
|
|
36143
|
+
},
|
|
36144
|
+
error: (e) => {
|
|
36145
|
+
this.oauthReturn = {
|
|
36146
|
+
inProgress: false,
|
|
36147
|
+
succeeded: false,
|
|
36148
|
+
error: e && e.error || "Sign-in could not be completed."
|
|
36149
|
+
};
|
|
36150
|
+
this.emitAuthEvent("oauthReturn", this.oauthReturn);
|
|
36151
|
+
}
|
|
36152
|
+
});
|
|
36153
|
+
return true;
|
|
36154
|
+
}
|
|
36155
|
+
/** Rewrite the address bar without the sign-in parameters, keeping everything else. */
|
|
36156
|
+
_stripAuthParamsFromUrl(remaining) {
|
|
36157
|
+
if (!window.history || typeof window.history.replaceState !== "function") return;
|
|
36158
|
+
const query2 = remaining.toString();
|
|
36159
|
+
const cleaned = window.location.pathname + (query2 ? `?${query2}` : "") + window.location.hash;
|
|
36160
|
+
try {
|
|
36161
|
+
window.history.replaceState(window.history.state, "", cleaned);
|
|
36162
|
+
} catch (e) {
|
|
36163
|
+
}
|
|
36164
|
+
}
|
|
36165
|
+
_currentUrlWithoutAuthParams() {
|
|
36166
|
+
const url = new URL(window.location.href);
|
|
36167
|
+
url.searchParams.delete(HANDOFF_PARAM);
|
|
36168
|
+
url.searchParams.delete(AUTH_ERROR_PARAM);
|
|
36169
|
+
return url.toString();
|
|
36170
|
+
}
|
|
36171
|
+
};
|
|
36172
|
+
}
|
|
36173
|
+
});
|
|
36174
|
+
|
|
35616
36175
|
// ../noodl-runtime/src/api/backends/RestAuthAdapter.ts
|
|
35617
36176
|
function restAuthProfileFor(type) {
|
|
35618
36177
|
return PROFILES[type];
|
|
@@ -35704,6 +36263,7 @@ var init_RestAuthAdapter = __esm({
|
|
|
35704
36263
|
"../noodl-runtime/src/api/backends/RestAuthAdapter.ts"() {
|
|
35705
36264
|
init_src();
|
|
35706
36265
|
init_AuthEvents();
|
|
36266
|
+
init_ParseAuthAdapter();
|
|
35707
36267
|
init_SessionStore();
|
|
35708
36268
|
init_TokenLifecycle();
|
|
35709
36269
|
DEFAULT_POCKETBASE_USER_COLLECTION = "users";
|
|
@@ -36269,12 +36829,20 @@ var init_RestAuthAdapter = __esm({
|
|
|
36269
36829
|
{ method, path: this.path(profile, profile.me), token, body: method === "POST" ? {} : void 0 },
|
|
36270
36830
|
{
|
|
36271
36831
|
ok: (body) => {
|
|
36832
|
+
if (sessionTokenOf(this.getCurrentUser(handle)) !== sessionTokenOf(stored)) {
|
|
36833
|
+
options.error(SESSION_MOVED_MESSAGE);
|
|
36834
|
+
return;
|
|
36835
|
+
}
|
|
36272
36836
|
const session = this.readSession(profile, body, Object.assign({}, stored, { sessionToken: token })) ?? Object.assign({}, stored, this.readUser(profile, body), { sessionToken: token });
|
|
36273
36837
|
this.setSession(handle, session);
|
|
36274
36838
|
this.emitAuthEvent("sessionGained", session);
|
|
36275
36839
|
options.success(session);
|
|
36276
36840
|
},
|
|
36277
36841
|
fail: (error) => {
|
|
36842
|
+
if (sessionTokenOf(this.getCurrentUser(handle)) !== sessionTokenOf(stored)) {
|
|
36843
|
+
options.error(SESSION_MOVED_MESSAGE);
|
|
36844
|
+
return;
|
|
36845
|
+
}
|
|
36278
36846
|
if (error.status === 401 || error.status === 403) {
|
|
36279
36847
|
this.sessionStore(handle).clear();
|
|
36280
36848
|
this.emitAuthEvent("sessionLost", error.error);
|
|
@@ -38775,8 +39343,9 @@ function schemaFor(collectionName2) {
|
|
|
38775
39343
|
try {
|
|
38776
39344
|
collection = CloudStore._collections[collectionName2];
|
|
38777
39345
|
} catch (e) {
|
|
38778
|
-
|
|
39346
|
+
collection = void 0;
|
|
38779
39347
|
}
|
|
39348
|
+
if (!collection) collection = hostCollection(collectionName2);
|
|
38780
39349
|
if (!collection) return void 0;
|
|
38781
39350
|
if (collection.schema) return { collection: collectionName2, properties: collection.schema.properties };
|
|
38782
39351
|
if (Array.isArray(collection.columns)) {
|
|
@@ -38790,6 +39359,15 @@ function schemaFor(collectionName2) {
|
|
|
38790
39359
|
}
|
|
38791
39360
|
return void 0;
|
|
38792
39361
|
}
|
|
39362
|
+
function hostCollection(collectionName2) {
|
|
39363
|
+
if (typeof _noodl_cloud_runtime_version === "undefined" || typeof _noodl_cloudservices === "undefined") return void 0;
|
|
39364
|
+
if (!_noodl_cloudservices || typeof _noodl_cloudservices.collections !== "function") return void 0;
|
|
39365
|
+
try {
|
|
39366
|
+
return (_noodl_cloudservices.collections() || []).find((c) => c && c.name === collectionName2);
|
|
39367
|
+
} catch (e) {
|
|
39368
|
+
return void 0;
|
|
39369
|
+
}
|
|
39370
|
+
}
|
|
38793
39371
|
function backendType() {
|
|
38794
39372
|
try {
|
|
38795
39373
|
const handle = CloudStore.instance && CloudStore.instance._handle && CloudStore.instance._handle();
|
|
@@ -70138,7 +70716,7 @@ function syncDataAttributesOnElement(node) {
|
|
|
70138
70716
|
node._appliedDataAttributes = Object.keys(want);
|
|
70139
70717
|
}
|
|
70140
70718
|
function claimDataAttributeInputs(node) {
|
|
70141
|
-
const
|
|
70719
|
+
const own2 = node.registerInputIfNeeded;
|
|
70142
70720
|
node.registerInputIfNeeded = function(name2) {
|
|
70143
70721
|
if (!this.hasInput(name2) && DATA_ATTRIBUTE_NAME.test(name2)) {
|
|
70144
70722
|
this.registerInput(name2, {
|
|
@@ -70150,7 +70728,7 @@ function claimDataAttributeInputs(node) {
|
|
|
70150
70728
|
});
|
|
70151
70729
|
return;
|
|
70152
70730
|
}
|
|
70153
|
-
return
|
|
70731
|
+
return own2 ? own2.call(this, name2) : void 0;
|
|
70154
70732
|
};
|
|
70155
70733
|
}
|
|
70156
70734
|
function scrollNodeIntoView(node, align) {
|
|
@@ -71700,7 +72278,7 @@ var init_Checkbox2 = __esm({
|
|
|
71700
72278
|
});
|
|
71701
72279
|
|
|
71702
72280
|
// ../noodl-viewer-react/src/guid.ts
|
|
71703
|
-
function
|
|
72281
|
+
function guid2() {
|
|
71704
72282
|
function s4() {
|
|
71705
72283
|
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
|
|
71706
72284
|
}
|
|
@@ -71709,7 +72287,7 @@ function guid() {
|
|
|
71709
72287
|
var guid_default;
|
|
71710
72288
|
var init_guid = __esm({
|
|
71711
72289
|
"../noodl-viewer-react/src/guid.ts"() {
|
|
71712
|
-
guid_default =
|
|
72290
|
+
guid_default = guid2;
|
|
71713
72291
|
}
|
|
71714
72292
|
});
|
|
71715
72293
|
|
|
@@ -71929,6 +72507,17 @@ var init_checkbox = __esm({
|
|
|
71929
72507
|
});
|
|
71930
72508
|
|
|
71931
72509
|
// ../noodl-viewer-react/src/components/controls/Select/Select.tsx
|
|
72510
|
+
function domValueOf(item) {
|
|
72511
|
+
if (item.Value === void 0 || item.Value === null || typeof item.Value === "boolean") {
|
|
72512
|
+
const label = item.Label;
|
|
72513
|
+
return (label === void 0 || label === null || typeof label === "boolean" ? "" : String(label)).replace(/[ \t\n\f\r]+/g, " ").trim();
|
|
72514
|
+
}
|
|
72515
|
+
return String(item.Value);
|
|
72516
|
+
}
|
|
72517
|
+
function offersValue(items, value2) {
|
|
72518
|
+
if (!items || typeof value2 !== "string") return false;
|
|
72519
|
+
return items.some((item) => item !== null && item !== void 0 && domValueOf(item) === value2);
|
|
72520
|
+
}
|
|
71932
72521
|
function Select(props) {
|
|
71933
72522
|
const [value2, setValue] = (0, import_react6.useState)(props.value);
|
|
71934
72523
|
(0, import_react6.useEffect)(() => {
|
|
@@ -71942,7 +72531,7 @@ function Select(props) {
|
|
|
71942
72531
|
style2 = Object.assign({}, props.textStyle, style2);
|
|
71943
72532
|
style2.color = props.noodlNode.context.styles.resolveColor(style2.color);
|
|
71944
72533
|
}
|
|
71945
|
-
const selectedIndex = !props.items || value2 === void 0 ? -1 : props.items.findIndex((i2) => i2
|
|
72534
|
+
const selectedIndex = !props.items || value2 === void 0 ? -1 : props.items.findIndex((i2) => i2 !== null && i2 !== void 0 && domValueOf(i2) === value2);
|
|
71946
72535
|
const { height, ...otherStyles } = style2;
|
|
71947
72536
|
function _renderIcon() {
|
|
71948
72537
|
if (props.iconSourceType === "image" && props.iconImageSource !== void 0)
|
|
@@ -72050,7 +72639,7 @@ function Select(props) {
|
|
|
72050
72639
|
{
|
|
72051
72640
|
...inputProps,
|
|
72052
72641
|
disabled: !props.enabled,
|
|
72053
|
-
value:
|
|
72642
|
+
value: offersValue(props.items, value2) ? value2 : "",
|
|
72054
72643
|
...utils_default.controlEvents(props),
|
|
72055
72644
|
onChange: (e) => {
|
|
72056
72645
|
setValue(e.target.value);
|
|
@@ -72223,12 +72812,14 @@ var init_options = __esm({
|
|
|
72223
72812
|
this.props.id = "input-" + guid_default();
|
|
72224
72813
|
this.props.items = DEFAULT_ITEMS.map((item) => ({ ...item }));
|
|
72225
72814
|
this.props.value = this._internal.value = DEFAULT_ITEMS[0].Value;
|
|
72815
|
+
this._internal.valueIsSeed = true;
|
|
72226
72816
|
this.props.valueWrites = 0;
|
|
72227
72817
|
this.props.valueChanged = (value2) => {
|
|
72228
72818
|
const changed = this._internal.value !== value2;
|
|
72229
72819
|
this.props.value = this._internal.value = value2;
|
|
72230
72820
|
this.props.valueWrites++;
|
|
72231
72821
|
if (changed) {
|
|
72822
|
+
this._internal.valueIsSeed = false;
|
|
72232
72823
|
this.flagOutputDirty("value");
|
|
72233
72824
|
this.sendSignalOnOutput("onChange");
|
|
72234
72825
|
}
|
|
@@ -72281,6 +72872,16 @@ var init_options = __esm({
|
|
|
72281
72872
|
if (newValue && typeof newValue.on === "function") newValue.on("change", this._itemsChanged);
|
|
72282
72873
|
}
|
|
72283
72874
|
this.props.items = optionItems(this._internal.items);
|
|
72875
|
+
if (this._internal.valueIsSeed) {
|
|
72876
|
+
const offered = this.props.items;
|
|
72877
|
+
const seed = this._internal.value;
|
|
72878
|
+
if (!Array.isArray(offered) || !offered.some((item) => item && item.Value === seed)) {
|
|
72879
|
+
this._internal.valueIsSeed = false;
|
|
72880
|
+
this.props.value = this._internal.value = "";
|
|
72881
|
+
this.props.valueWrites++;
|
|
72882
|
+
this.flagOutputDirty("value");
|
|
72883
|
+
}
|
|
72884
|
+
}
|
|
72284
72885
|
this.forceUpdate();
|
|
72285
72886
|
}
|
|
72286
72887
|
},
|
|
@@ -72299,6 +72900,7 @@ var init_options = __esm({
|
|
|
72299
72900
|
}
|
|
72300
72901
|
const changed = value2 !== this._internal.value;
|
|
72301
72902
|
this.props.value = this._internal.value = value2;
|
|
72903
|
+
this._internal.valueIsSeed = false;
|
|
72302
72904
|
this.props.valueWrites++;
|
|
72303
72905
|
if (changed) {
|
|
72304
72906
|
this.forceUpdate();
|
|
@@ -78605,11 +79207,11 @@ var init_foreach = __esm({
|
|
|
78605
79207
|
const internal2 = this._internal;
|
|
78606
79208
|
const children = internal2.target.getChildren();
|
|
78607
79209
|
let at = children.indexOf(this) + 1;
|
|
78608
|
-
let
|
|
79210
|
+
let own2 = 0;
|
|
78609
79211
|
for (let i2 = at; i2 < children.length; i2++) {
|
|
78610
79212
|
if (internal2.itemNodes.indexOf(children[i2]) === -1) continue;
|
|
78611
|
-
if (
|
|
78612
|
-
|
|
79213
|
+
if (own2 === listIndex) return i2;
|
|
79214
|
+
own2++;
|
|
78613
79215
|
at = i2 + 1;
|
|
78614
79216
|
}
|
|
78615
79217
|
return at;
|
|
@@ -90999,8 +91601,8 @@ var init_timer = __esm({
|
|
|
90999
91601
|
const a = this._internal._animation;
|
|
91000
91602
|
if (a._isRunning !== true || this._internal.paused) return;
|
|
91001
91603
|
const left = a.durationLeft();
|
|
91002
|
-
const
|
|
91003
|
-
this._internal.paused = { left: typeof left === "number" && left >= 0 ? left : a.duration, ...
|
|
91604
|
+
const own2 = this._internal.resumed || { duration: a.duration, delay: a.delay };
|
|
91605
|
+
this._internal.paused = { left: typeof left === "number" && left >= 0 ? left : a.duration, ...own2 };
|
|
91004
91606
|
a.stop();
|
|
91005
91607
|
},
|
|
91006
91608
|
_onPageShown: function() {
|
|
@@ -95191,6 +95793,7 @@ __export(cloudfunction2_exports, {
|
|
|
95191
95793
|
default: () => cloudfunction2_default
|
|
95192
95794
|
});
|
|
95193
95795
|
function _makeRequest(path10, options) {
|
|
95796
|
+
options = holdOptionsForWrite(options);
|
|
95194
95797
|
const xhr = new XMLHttpRequest();
|
|
95195
95798
|
xhr.onreadystatechange = function() {
|
|
95196
95799
|
if (xhr.readyState === 4) {
|
|
@@ -95235,6 +95838,7 @@ var init_cloudfunction2 = __esm({
|
|
|
95235
95838
|
"../noodl-viewer-react/src/nodes/std-library/data/cloudfunction2.ts"() {
|
|
95236
95839
|
import_runtime10 = __toESM(require_noodl_runtime());
|
|
95237
95840
|
init_SessionStore();
|
|
95841
|
+
init_pressguard();
|
|
95238
95842
|
init_outcome();
|
|
95239
95843
|
CALL_ERROR_CODE = "cloud-function/call-failed";
|
|
95240
95844
|
CloudFunctionNode = {
|
|
@@ -97873,453 +98477,6 @@ var init_router_navigate = __esm({
|
|
|
97873
98477
|
}
|
|
97874
98478
|
});
|
|
97875
98479
|
|
|
97876
|
-
// ../noodl-runtime/src/api/backends/ParseAuthAdapter.ts
|
|
97877
|
-
function pageSays(response, phrase) {
|
|
97878
|
-
return typeof response === "string" && response.indexOf(phrase) !== -1;
|
|
97879
|
-
}
|
|
97880
|
-
function htmlPageError(error, invalidPhrase, invalidMessage, fallback) {
|
|
97881
|
-
if (typeof error !== "string" || error.indexOf("<") === -1) return error || fallback;
|
|
97882
|
-
return error.indexOf(invalidPhrase) !== -1 ? invalidMessage : fallback;
|
|
97883
|
-
}
|
|
97884
|
-
var guid2, HANDOFF_PARAM, AUTH_ERROR_PARAM, ParseAuthAdapter;
|
|
97885
|
-
var init_ParseAuthAdapter = __esm({
|
|
97886
|
-
"../noodl-runtime/src/api/backends/ParseAuthAdapter.ts"() {
|
|
97887
|
-
init_AuthEvents();
|
|
97888
|
-
init_SessionStore();
|
|
97889
|
-
init_TokenLifecycle();
|
|
97890
|
-
guid2 = require_guid();
|
|
97891
|
-
HANDOFF_PARAM = "nodegx_auth";
|
|
97892
|
-
AUTH_ERROR_PARAM = "nodegx_auth_error";
|
|
97893
|
-
ParseAuthAdapter = class extends AuthEvents {
|
|
97894
|
-
constructor(options) {
|
|
97895
|
-
super();
|
|
97896
|
-
/** BAK-004: the state of a sign-in returning from a provider or a magic link. */
|
|
97897
|
-
this.oauthReturn = { inProgress: false };
|
|
97898
|
-
/**
|
|
97899
|
-
* One store and one controller per backend, keyed by application id.
|
|
97900
|
-
*
|
|
97901
|
-
* Per handle rather than per adapter because the contract's whole point is
|
|
97902
|
-
* that a project may have more than one backend, and two backends have two
|
|
97903
|
-
* sessions — the phase decided explicitly that they are never shared.
|
|
97904
|
-
*/
|
|
97905
|
-
this.stores = /* @__PURE__ */ new Map();
|
|
97906
|
-
this.controllers = /* @__PURE__ */ new Map();
|
|
97907
|
-
this.serializeObject = options.serializeObject;
|
|
97908
|
-
this.createStore = options.createStore || parseSessionStore;
|
|
97909
|
-
this.lifecycle = options.lifecycle || { kind: "eternal" };
|
|
97910
|
-
}
|
|
97911
|
-
// ── Session storage ──────────────────────────────────────────────────────
|
|
97912
|
-
sessionStore(handle) {
|
|
97913
|
-
const appId = handle.publicToken;
|
|
97914
|
-
const cacheKey = String(appId);
|
|
97915
|
-
let store2 = this.stores.get(cacheKey);
|
|
97916
|
-
if (store2 === void 0) {
|
|
97917
|
-
store2 = this.createStore(appId);
|
|
97918
|
-
this.stores.set(cacheKey, store2);
|
|
97919
|
-
}
|
|
97920
|
-
return store2;
|
|
97921
|
-
}
|
|
97922
|
-
/**
|
|
97923
|
-
* The lifecycle controller for this backend.
|
|
97924
|
-
*
|
|
97925
|
-
* On Parse this is an `eternal` controller: it arms no timer, subscribes to no
|
|
97926
|
-
* storage events, and its gate is a synchronous pass-through. It is on the
|
|
97927
|
-
* request path anyway, so that BCN-004 adds a `performRefresh` rather than a
|
|
97928
|
-
* call site.
|
|
97929
|
-
*/
|
|
97930
|
-
lifecycleController(handle) {
|
|
97931
|
-
const cacheKey = String(handle.publicToken);
|
|
97932
|
-
let controller = this.controllers.get(cacheKey);
|
|
97933
|
-
if (controller === void 0) {
|
|
97934
|
-
controller = new TokenLifecycleController({
|
|
97935
|
-
lifecycle: this.lifecycle,
|
|
97936
|
-
store: this.sessionStore(handle),
|
|
97937
|
-
onSessionLost: (reason) => {
|
|
97938
|
-
this.emitAuthEvent("sessionChanged", void 0);
|
|
97939
|
-
this.emitAuthEvent("sessionLost", reason);
|
|
97940
|
-
},
|
|
97941
|
-
onSessionClearedElsewhere: () => {
|
|
97942
|
-
this.emitAuthEvent("sessionChanged", void 0);
|
|
97943
|
-
this.emitAuthEvent("loggedOut");
|
|
97944
|
-
}
|
|
97945
|
-
});
|
|
97946
|
-
controller.start();
|
|
97947
|
-
this.controllers.set(cacheKey, controller);
|
|
97948
|
-
}
|
|
97949
|
-
return controller;
|
|
97950
|
-
}
|
|
97951
|
-
/** The stored session, or `undefined`. Not a contract method. */
|
|
97952
|
-
getCurrentUser(handle) {
|
|
97953
|
-
return this.sessionStore(handle).read();
|
|
97954
|
-
}
|
|
97955
|
-
setSession(handle, session) {
|
|
97956
|
-
this.sessionStore(handle).write(session);
|
|
97957
|
-
this.lifecycleController(handle).sessionChanged();
|
|
97958
|
-
this.emitAuthEvent("sessionChanged", session);
|
|
97959
|
-
}
|
|
97960
|
-
clearSession(handle) {
|
|
97961
|
-
this.sessionStore(handle).clear();
|
|
97962
|
-
this.lifecycleController(handle).sessionChanged();
|
|
97963
|
-
this.emitAuthEvent("sessionChanged", void 0);
|
|
97964
|
-
}
|
|
97965
|
-
// ── The wire ─────────────────────────────────────────────────────────────
|
|
97966
|
-
/**
|
|
97967
|
-
* The Parse auth seam. Moved from `userservice.ts:198` with one substitution:
|
|
97968
|
-
* `this.endpoint`/`this.appId` became `handle.url`/`handle.publicToken`.
|
|
97969
|
-
*
|
|
97970
|
-
* It is deliberately *not* `ParseWireAdapter._makeRequest`. The two differ in
|
|
97971
|
-
* three ways that all matter: this one sends `X-Parse-Installation-Id`, never
|
|
97972
|
-
* sends a master key, and hands its error callback Parse's `code` (the `209`
|
|
97973
|
-
* the session check turns on). Merging them would be a rewrite in a task
|
|
97974
|
-
* whose value is that nothing was rewritten.
|
|
97975
|
-
*/
|
|
97976
|
-
_makeRequest(handle, path10, options) {
|
|
97977
|
-
if (!handle.url) {
|
|
97978
|
-
if (options.error) {
|
|
97979
|
-
options.error({ error: "No active cloud service", status: 0 });
|
|
97980
|
-
}
|
|
97981
|
-
return;
|
|
97982
|
-
}
|
|
97983
|
-
const store2 = this.sessionStore(handle);
|
|
97984
|
-
this.lifecycleController(handle).withSession((session) => {
|
|
97985
|
-
const xhr = new XMLHttpRequest();
|
|
97986
|
-
xhr.onreadystatechange = function() {
|
|
97987
|
-
if (xhr.readyState === 4) {
|
|
97988
|
-
let json2;
|
|
97989
|
-
try {
|
|
97990
|
-
json2 = JSON.parse(xhr.response);
|
|
97991
|
-
} catch (e) {
|
|
97992
|
-
}
|
|
97993
|
-
if (xhr.status === 200 || xhr.status === 201) {
|
|
97994
|
-
options.success(json2 || xhr.response);
|
|
97995
|
-
} else options.error(json2 || { error: xhr.responseText, status: xhr.status });
|
|
97996
|
-
}
|
|
97997
|
-
};
|
|
97998
|
-
xhr.open(options.method || "GET", handle.url + path10, true);
|
|
97999
|
-
xhr.setRequestHeader("X-Parse-Application-Id", handle.publicToken);
|
|
98000
|
-
const iidKey = parseInstallationIdKey(handle.publicToken);
|
|
98001
|
-
let _iid = store2.readKey(iidKey);
|
|
98002
|
-
if (_iid === void 0) {
|
|
98003
|
-
_iid = guid2();
|
|
98004
|
-
store2.writeKey(iidKey, _iid);
|
|
98005
|
-
}
|
|
98006
|
-
xhr.setRequestHeader("X-Parse-Installation-Id", _iid);
|
|
98007
|
-
if (options.sessionToken) xhr.setRequestHeader("X-Parse-Session-Token", options.sessionToken);
|
|
98008
|
-
else if (session !== void 0) {
|
|
98009
|
-
xhr.setRequestHeader("X-Parse-Session-Token", session.sessionToken);
|
|
98010
|
-
}
|
|
98011
|
-
xhr.setRequestHeader("Content-Type", "application/json");
|
|
98012
|
-
xhr.send(JSON.stringify(options.content));
|
|
98013
|
-
});
|
|
98014
|
-
}
|
|
98015
|
-
// ── The ten ──────────────────────────────────────────────────────────────
|
|
98016
|
-
logIn(handle, options) {
|
|
98017
|
-
this._makeRequest(handle, "/login", {
|
|
98018
|
-
method: "POST",
|
|
98019
|
-
content: {
|
|
98020
|
-
username: options.username,
|
|
98021
|
-
password: options.password,
|
|
98022
|
-
_method: "GET"
|
|
98023
|
-
},
|
|
98024
|
-
success: (response) => {
|
|
98025
|
-
this.setSession(handle, response);
|
|
98026
|
-
options.success(response);
|
|
98027
|
-
this.emitAuthEvent("loggedIn", response);
|
|
98028
|
-
},
|
|
98029
|
-
error: (e) => {
|
|
98030
|
-
options.error(e.error);
|
|
98031
|
-
}
|
|
98032
|
-
});
|
|
98033
|
-
}
|
|
98034
|
-
logOut(handle, options) {
|
|
98035
|
-
this._makeRequest(handle, "/logout", {
|
|
98036
|
-
method: "POST",
|
|
98037
|
-
content: {},
|
|
98038
|
-
success: () => {
|
|
98039
|
-
this.clearSession(handle);
|
|
98040
|
-
options.success();
|
|
98041
|
-
this.emitAuthEvent("loggedOut");
|
|
98042
|
-
},
|
|
98043
|
-
error: (e) => {
|
|
98044
|
-
options.error(e.error);
|
|
98045
|
-
}
|
|
98046
|
-
});
|
|
98047
|
-
}
|
|
98048
|
-
signUp(handle, options) {
|
|
98049
|
-
const additionalUserProps = options.properties ? this.serializeObject({ ...options.properties }, "_User") : {};
|
|
98050
|
-
this._makeRequest(handle, "/users", {
|
|
98051
|
-
method: "POST",
|
|
98052
|
-
content: Object.assign({}, additionalUserProps, {
|
|
98053
|
-
username: options.username,
|
|
98054
|
-
password: options.password,
|
|
98055
|
-
email: options.email
|
|
98056
|
-
}),
|
|
98057
|
-
success: (response) => {
|
|
98058
|
-
const identity2 = { username: options.username };
|
|
98059
|
-
if (options.email !== void 0) identity2.email = options.email;
|
|
98060
|
-
const _cu = Object.assign(response, identity2, options.properties);
|
|
98061
|
-
if (_cu.emailVerified === void 0) _cu.emailVerified = false;
|
|
98062
|
-
this.setSession(handle, _cu);
|
|
98063
|
-
options.success(response);
|
|
98064
|
-
this.emitAuthEvent("loggedIn", _cu);
|
|
98065
|
-
},
|
|
98066
|
-
error: (e) => {
|
|
98067
|
-
options.error(e.error);
|
|
98068
|
-
}
|
|
98069
|
-
});
|
|
98070
|
-
}
|
|
98071
|
-
fetchCurrentUser(handle, options) {
|
|
98072
|
-
this._makeRequest(handle, "/users/me", {
|
|
98073
|
-
method: "GET",
|
|
98074
|
-
sessionToken: options.sessionToken,
|
|
98075
|
-
success: (response) => {
|
|
98076
|
-
this.setSession(handle, response);
|
|
98077
|
-
this.emitAuthEvent("sessionGained", response);
|
|
98078
|
-
options.success(response);
|
|
98079
|
-
},
|
|
98080
|
-
error: (e) => {
|
|
98081
|
-
if (e.code === 209) {
|
|
98082
|
-
this.sessionStore(handle).clear();
|
|
98083
|
-
this.emitAuthEvent("sessionLost");
|
|
98084
|
-
}
|
|
98085
|
-
options.error(e.error);
|
|
98086
|
-
}
|
|
98087
|
-
});
|
|
98088
|
-
}
|
|
98089
|
-
verifyEmail(handle, options) {
|
|
98090
|
-
this._makeRequest(
|
|
98091
|
-
handle,
|
|
98092
|
-
"/apps/" + handle.publicToken + "/verify_email?username=" + options.username + "&token=" + options.token,
|
|
98093
|
-
{
|
|
98094
|
-
method: "GET",
|
|
98095
|
-
// This endpoint answers with an HTML page rather than JSON, so the outcome has to be
|
|
98096
|
-
// read out of the page's text.
|
|
98097
|
-
success: (response) => {
|
|
98098
|
-
if (pageSays(response, "Successfully verified your email")) {
|
|
98099
|
-
options.success();
|
|
98100
|
-
} else if (pageSays(response, "Invalid Verification Link")) {
|
|
98101
|
-
options.error("Invalid verification token");
|
|
98102
|
-
} else {
|
|
98103
|
-
options.error("Failed to verify email");
|
|
98104
|
-
}
|
|
98105
|
-
},
|
|
98106
|
-
error: (e) => {
|
|
98107
|
-
options.error(htmlPageError(e.error, "Invalid Verification Link", "Invalid verification token", "Failed to verify email"));
|
|
98108
|
-
}
|
|
98109
|
-
}
|
|
98110
|
-
);
|
|
98111
|
-
}
|
|
98112
|
-
sendEmailVerification(handle, options) {
|
|
98113
|
-
this._makeRequest(handle, "/verificationEmailRequest", {
|
|
98114
|
-
method: "POST",
|
|
98115
|
-
content: { email: options.email },
|
|
98116
|
-
success: () => {
|
|
98117
|
-
options.success();
|
|
98118
|
-
},
|
|
98119
|
-
error: (e) => {
|
|
98120
|
-
options.error(e.error);
|
|
98121
|
-
}
|
|
98122
|
-
});
|
|
98123
|
-
}
|
|
98124
|
-
resetPassword(handle, options) {
|
|
98125
|
-
this._makeRequest(handle, "/apps/" + handle.publicToken + "/request_password_reset", {
|
|
98126
|
-
method: "POST",
|
|
98127
|
-
content: {
|
|
98128
|
-
username: options.username,
|
|
98129
|
-
token: options.token,
|
|
98130
|
-
new_password: options.newPassword
|
|
98131
|
-
},
|
|
98132
|
-
success: (response) => {
|
|
98133
|
-
if (pageSays(response, "Password successfully reset") || pageSays(response, "Successfully updated your password")) {
|
|
98134
|
-
options.success();
|
|
98135
|
-
} else if (pageSays(response, "Invalid Link")) {
|
|
98136
|
-
options.error("Invalid or expired reset link");
|
|
98137
|
-
} else {
|
|
98138
|
-
options.error("Failed to reset password");
|
|
98139
|
-
}
|
|
98140
|
-
},
|
|
98141
|
-
error: (e) => {
|
|
98142
|
-
options.error(htmlPageError(e.error, "Invalid Link", "Invalid or expired reset link", "Failed to reset password"));
|
|
98143
|
-
}
|
|
98144
|
-
});
|
|
98145
|
-
}
|
|
98146
|
-
requestPasswordReset(handle, options) {
|
|
98147
|
-
this._makeRequest(handle, "/requestPasswordReset", {
|
|
98148
|
-
method: "POST",
|
|
98149
|
-
content: { email: options.email },
|
|
98150
|
-
success: () => {
|
|
98151
|
-
options.success();
|
|
98152
|
-
},
|
|
98153
|
-
error: (e) => {
|
|
98154
|
-
options.error(e.error);
|
|
98155
|
-
}
|
|
98156
|
-
});
|
|
98157
|
-
}
|
|
98158
|
-
/**
|
|
98159
|
-
* Send the browser to a provider. This NAVIGATES AWAY: nothing after it runs,
|
|
98160
|
-
* and the result arrives on a later page load through {@link consumeAuthReturn}.
|
|
98161
|
-
*
|
|
98162
|
-
* `redirect` defaults to the current page (minus any leftover sign-in
|
|
98163
|
-
* parameters), so the user comes back where they were. The backend refuses
|
|
98164
|
-
* any target that is neither same-origin nor on its `redirectAllowList` — if
|
|
98165
|
-
* sign-in dead-ends with "origin not allowed", that list is the fix.
|
|
98166
|
-
*/
|
|
98167
|
-
signInWithProvider(handle, options) {
|
|
98168
|
-
if (!handle.url) {
|
|
98169
|
-
if (options.error) options.error("No active cloud service");
|
|
98170
|
-
return;
|
|
98171
|
-
}
|
|
98172
|
-
if (!options.provider) {
|
|
98173
|
-
if (options.error) options.error("Sign In With: no provider was set.");
|
|
98174
|
-
return;
|
|
98175
|
-
}
|
|
98176
|
-
const redirect = options.redirect || this._currentUrlWithoutAuthParams();
|
|
98177
|
-
const url = `${handle.url}/oauth/${encodeURIComponent(options.provider)}/start?redirect=${encodeURIComponent(redirect)}`;
|
|
98178
|
-
window.location.href = url;
|
|
98179
|
-
}
|
|
98180
|
-
/**
|
|
98181
|
-
* Ask the backend to email a one-click sign-in link.
|
|
98182
|
-
*
|
|
98183
|
-
* Succeeds identically for a known and an unknown address — the endpoint is
|
|
98184
|
-
* anonymous and anti-enumerating by design, so "success" here means "the
|
|
98185
|
-
* request was accepted", never "an account exists". The node's help text says
|
|
98186
|
-
* so, because a UI that claims "check your inbox" only for real accounts
|
|
98187
|
-
* re-creates the oracle the endpoint removed.
|
|
98188
|
-
*/
|
|
98189
|
-
requestMagicLink(handle, options) {
|
|
98190
|
-
this._makeRequest(handle, "/auth/magic-link", {
|
|
98191
|
-
method: "POST",
|
|
98192
|
-
content: { email: options.email, redirect: options.redirect || this._currentUrlWithoutAuthParams() },
|
|
98193
|
-
success: () => options.success(),
|
|
98194
|
-
error: (e) => options.error(e.error)
|
|
98195
|
-
});
|
|
98196
|
-
}
|
|
98197
|
-
// ── Not contract, and deliberately so ────────────────────────────────────
|
|
98198
|
-
/**
|
|
98199
|
-
* Write user-profile fields onto the signed-in user.
|
|
98200
|
-
*
|
|
98201
|
-
* Not one of the ten. It is the `Set User Properties` node's whole
|
|
98202
|
-
* implementation, and BCN-006's Desired State §3 makes it schema-driven across
|
|
98203
|
-
* five backends — which is step 6, not this pass. It moves with the wire
|
|
98204
|
-
* because leaving it behind would have left a second `_makeRequest` in
|
|
98205
|
-
* `userservice.ts`, and the entire claim of this move is that there is one.
|
|
98206
|
-
*/
|
|
98207
|
-
setUserProperties(handle, options) {
|
|
98208
|
-
const _cu = this.getCurrentUser(handle);
|
|
98209
|
-
if (_cu === void 0) {
|
|
98210
|
-
options.error("Nobody is signed in.");
|
|
98211
|
-
return;
|
|
98212
|
-
}
|
|
98213
|
-
{
|
|
98214
|
-
const propsToSave = this.serializeObject({ ...options.properties }, "_User");
|
|
98215
|
-
const _content = Object.assign(
|
|
98216
|
-
{},
|
|
98217
|
-
{ email: options.email, username: options.username },
|
|
98218
|
-
propsToSave
|
|
98219
|
-
);
|
|
98220
|
-
delete _content.emailVerified;
|
|
98221
|
-
delete _content.createdAt;
|
|
98222
|
-
delete _content.updatedAt;
|
|
98223
|
-
this._makeRequest(handle, "/users/" + _cu.objectId, {
|
|
98224
|
-
method: "PUT",
|
|
98225
|
-
content: _content,
|
|
98226
|
-
success: (response) => {
|
|
98227
|
-
Object.assign(_cu, _content);
|
|
98228
|
-
this.setSession(handle, _cu);
|
|
98229
|
-
options.success(response);
|
|
98230
|
-
},
|
|
98231
|
-
error: (e) => {
|
|
98232
|
-
options.error(e.error);
|
|
98233
|
-
}
|
|
98234
|
-
});
|
|
98235
|
-
}
|
|
98236
|
-
}
|
|
98237
|
-
/** The sign-in methods this backend offers — for rendering a set of buttons. */
|
|
98238
|
-
listAuthProviders(handle, options) {
|
|
98239
|
-
this._makeRequest(handle, "/auth/providers", {
|
|
98240
|
-
method: "GET",
|
|
98241
|
-
success: (response) => options.success(response),
|
|
98242
|
-
error: (e) => options.error(e.error)
|
|
98243
|
-
});
|
|
98244
|
-
}
|
|
98245
|
-
// ── BAK-004 — the provider return leg ────────────────────────────────────
|
|
98246
|
-
/**
|
|
98247
|
-
* Look for a sign-in result in the current URL and act on it.
|
|
98248
|
-
*
|
|
98249
|
-
* Returns true when this page load IS a sign-in return, so the caller can skip
|
|
98250
|
-
* its ordinary stale-session validation.
|
|
98251
|
-
*
|
|
98252
|
-
* **Per backend, not contract.** BCN-006 step 5 is four different redirect
|
|
98253
|
-
* shapes — Parse `authData`, Directus SSO, Supabase, PocketBase
|
|
98254
|
-
* `auth-with-oauth2` — and this is the first of the four. What they have in
|
|
98255
|
-
* common is only the *outcome*: a session, established. That is where the
|
|
98256
|
-
* contract's surface is, and it is `signInWithProvider` plus a `loggedIn`.
|
|
98257
|
-
*/
|
|
98258
|
-
consumeAuthReturn(handle) {
|
|
98259
|
-
if (typeof window === "undefined" || !window.location) return false;
|
|
98260
|
-
let params;
|
|
98261
|
-
try {
|
|
98262
|
-
params = new URLSearchParams(window.location.search);
|
|
98263
|
-
} catch (e) {
|
|
98264
|
-
return false;
|
|
98265
|
-
}
|
|
98266
|
-
const code = params.get(HANDOFF_PARAM);
|
|
98267
|
-
const error = params.get(AUTH_ERROR_PARAM);
|
|
98268
|
-
if (!code && !error) return false;
|
|
98269
|
-
params.delete(HANDOFF_PARAM);
|
|
98270
|
-
params.delete(AUTH_ERROR_PARAM);
|
|
98271
|
-
this._stripAuthParamsFromUrl(params);
|
|
98272
|
-
if (error) {
|
|
98273
|
-
this.oauthReturn = { inProgress: false, succeeded: false, error };
|
|
98274
|
-
setTimeout(() => this.emitAuthEvent("oauthReturn", this.oauthReturn), 0);
|
|
98275
|
-
return true;
|
|
98276
|
-
}
|
|
98277
|
-
this.oauthReturn = { inProgress: true };
|
|
98278
|
-
this._makeRequest(handle, "/oauth/exchange", {
|
|
98279
|
-
method: "POST",
|
|
98280
|
-
content: { code },
|
|
98281
|
-
success: (response) => {
|
|
98282
|
-
this.setSession(handle, response);
|
|
98283
|
-
this.oauthReturn = {
|
|
98284
|
-
inProgress: false,
|
|
98285
|
-
succeeded: true,
|
|
98286
|
-
outcome: response.authOutcome,
|
|
98287
|
-
notice: response.authNotice || void 0
|
|
98288
|
-
};
|
|
98289
|
-
this.emitAuthEvent("oauthReturn", this.oauthReturn);
|
|
98290
|
-
this.emitAuthEvent("loggedIn", response);
|
|
98291
|
-
},
|
|
98292
|
-
error: (e) => {
|
|
98293
|
-
this.oauthReturn = {
|
|
98294
|
-
inProgress: false,
|
|
98295
|
-
succeeded: false,
|
|
98296
|
-
error: e && e.error || "Sign-in could not be completed."
|
|
98297
|
-
};
|
|
98298
|
-
this.emitAuthEvent("oauthReturn", this.oauthReturn);
|
|
98299
|
-
}
|
|
98300
|
-
});
|
|
98301
|
-
return true;
|
|
98302
|
-
}
|
|
98303
|
-
/** Rewrite the address bar without the sign-in parameters, keeping everything else. */
|
|
98304
|
-
_stripAuthParamsFromUrl(remaining) {
|
|
98305
|
-
if (!window.history || typeof window.history.replaceState !== "function") return;
|
|
98306
|
-
const query2 = remaining.toString();
|
|
98307
|
-
const cleaned = window.location.pathname + (query2 ? `?${query2}` : "") + window.location.hash;
|
|
98308
|
-
try {
|
|
98309
|
-
window.history.replaceState(window.history.state, "", cleaned);
|
|
98310
|
-
} catch (e) {
|
|
98311
|
-
}
|
|
98312
|
-
}
|
|
98313
|
-
_currentUrlWithoutAuthParams() {
|
|
98314
|
-
const url = new URL(window.location.href);
|
|
98315
|
-
url.searchParams.delete(HANDOFF_PARAM);
|
|
98316
|
-
url.searchParams.delete(AUTH_ERROR_PARAM);
|
|
98317
|
-
return url.toString();
|
|
98318
|
-
}
|
|
98319
|
-
};
|
|
98320
|
-
}
|
|
98321
|
-
});
|
|
98322
|
-
|
|
98323
98480
|
// ../noodl-viewer-react/src/nodes/std-library/user/userservice.ts
|
|
98324
98481
|
var import_events2, import_runtime14, import_cloudstore2, UserService, _instance, userservice_default;
|
|
98325
98482
|
var init_userservice = __esm({
|
|
@@ -98865,44 +99022,20 @@ var signup_exports = {};
|
|
|
98865
99022
|
__export(signup_exports, {
|
|
98866
99023
|
default: () => signup_default
|
|
98867
99024
|
});
|
|
98868
|
-
function updatePorts6(nodeId,
|
|
98869
|
-
const
|
|
98870
|
-
|
|
98871
|
-
|
|
98872
|
-
|
|
98873
|
-
|
|
98874
|
-
const _ignoreKeys = ["authData", "password", "username", "createdAt", "updatedAt", "emailVerified", "email", "ACL"];
|
|
98875
|
-
for (const key in props) {
|
|
98876
|
-
if (_ignoreKeys.indexOf(key) !== -1) continue;
|
|
98877
|
-
const p = props[key];
|
|
98878
|
-
if (ports.find((_p) => _p.name === key)) continue;
|
|
98879
|
-
if (p.type === "Relation") {
|
|
98880
|
-
} else {
|
|
98881
|
-
const _typeMap = {
|
|
98882
|
-
String: "string",
|
|
98883
|
-
Boolean: "boolean",
|
|
98884
|
-
Number: "number",
|
|
98885
|
-
Date: "date"
|
|
98886
|
-
};
|
|
98887
|
-
ports.push({
|
|
98888
|
-
type: {
|
|
98889
|
-
name: _typeMap[p.type] ? _typeMap[p.type] : "*"
|
|
98890
|
-
},
|
|
98891
|
-
plug: "input",
|
|
98892
|
-
group: "Properties",
|
|
98893
|
-
name: "prop-" + key,
|
|
98894
|
-
displayName: key
|
|
98895
|
-
});
|
|
98896
|
-
}
|
|
98897
|
-
}
|
|
98898
|
-
}
|
|
98899
|
-
}
|
|
98900
|
-
editorConnection.sendDynamicPorts(nodeId, ports);
|
|
99025
|
+
function updatePorts6(nodeId, editorConnection, graphModel) {
|
|
99026
|
+
const ctx = userSchemaContext(graphModel, {});
|
|
99027
|
+
const ignore = isParseWireContext(ctx) ? USER_INPUT_IGNORE_PARSE_BROWSER : USER_INPUT_IGNORE_REST;
|
|
99028
|
+
sendSchemaPorts(editorConnection, nodeId, userPropertyPorts(ctx, { plug: "input", ignore }), {
|
|
99029
|
+
staticPorts: staticPortNames(SignUpNodeDefinition)
|
|
99030
|
+
});
|
|
98901
99031
|
}
|
|
98902
99032
|
var SIGN_UP_ERROR_CODE, SignUpNodeDefinition, SignUpModule, signup_default;
|
|
98903
99033
|
var init_signup = __esm({
|
|
98904
99034
|
"../noodl-viewer-react/src/nodes/std-library/user/signup.ts"() {
|
|
98905
99035
|
"use strict";
|
|
99036
|
+
init_record_ports();
|
|
99037
|
+
init_schema_ports();
|
|
99038
|
+
init_user_ports();
|
|
98906
99039
|
init_outcome();
|
|
98907
99040
|
init_userservice();
|
|
98908
99041
|
SIGN_UP_ERROR_CODE = "user/sign-up-failed";
|
|
@@ -99040,23 +99173,11 @@ var init_signup = __esm({
|
|
|
99040
99173
|
return;
|
|
99041
99174
|
}
|
|
99042
99175
|
function _managePortsForNode(node) {
|
|
99043
|
-
updatePorts6(
|
|
99044
|
-
|
|
99045
|
-
|
|
99046
|
-
|
|
99047
|
-
|
|
99048
|
-
);
|
|
99049
|
-
node.on("parameterUpdated", function() {
|
|
99050
|
-
updatePorts6(
|
|
99051
|
-
node.id,
|
|
99052
|
-
node.parameters,
|
|
99053
|
-
editorConnection,
|
|
99054
|
-
graphModel.getMetaData("systemCollections")
|
|
99055
|
-
);
|
|
99056
|
-
});
|
|
99057
|
-
graphModel.on("metadataChanged.systemCollections", function(data) {
|
|
99058
|
-
updatePorts6(node.id, node.parameters, editorConnection, data);
|
|
99059
|
-
});
|
|
99176
|
+
const rebuild = () => updatePorts6(node.id, editorConnection, graphModel);
|
|
99177
|
+
rebuild();
|
|
99178
|
+
graphModel.on("metadataChanged.systemCollections", rebuild);
|
|
99179
|
+
graphModel.on("metadataChanged.dbCollections", rebuild);
|
|
99180
|
+
graphModel.on("metadataChanged.backendServices", rebuild);
|
|
99060
99181
|
}
|
|
99061
99182
|
graphModel.on("editorImportComplete", () => {
|
|
99062
99183
|
graphModel.on("nodeAdded.net.noodl.user.SignUp", function(node) {
|
|
@@ -100237,6 +100358,7 @@ __export(cloudfunction_exports, {
|
|
|
100237
100358
|
default: () => cloudfunction_default
|
|
100238
100359
|
});
|
|
100239
100360
|
function _makeRequest2(path10, options) {
|
|
100361
|
+
options = holdOptionsForWrite(options);
|
|
100240
100362
|
const xhr = new XMLHttpRequest();
|
|
100241
100363
|
xhr.onreadystatechange = function() {
|
|
100242
100364
|
if (xhr.readyState === 4) {
|
|
@@ -100265,6 +100387,7 @@ var init_cloudfunction = __esm({
|
|
|
100265
100387
|
import_runtime15 = __toESM(require_noodl_runtime());
|
|
100266
100388
|
import_cloudstore3 = __toESM(require_cloudstore());
|
|
100267
100389
|
init_SessionStore();
|
|
100390
|
+
init_pressguard();
|
|
100268
100391
|
CloudFunctionNode2 = {
|
|
100269
100392
|
name: "Cloud Function",
|
|
100270
100393
|
category: "Cloud Services",
|
|
@@ -167354,11 +167477,12 @@ var node_catalog_default = {
|
|
|
167354
167477
|
],
|
|
167355
167478
|
items: [
|
|
167356
167479
|
"nothing handed (undefined): the list it has stays",
|
|
167480
|
+
"the value is still the placed node's seed and the list does not offer it: the list is kept, and the value is cleared to '' and sent",
|
|
167357
167481
|
"otherwise: the list is kept as handed and drawn at the next paint; the value stays, so a list handed again keeps the pick"
|
|
167358
167482
|
],
|
|
167359
167483
|
value: [
|
|
167360
167484
|
"null: ignored",
|
|
167361
|
-
"the value it already holds, as text: nothing",
|
|
167485
|
+
"the value it already holds, as text: nothing (it is no longer the seed)",
|
|
167362
167486
|
"a change: kept as text, Value follows, and the list selects it at the next paint if it names an option \u2014 Changed does not fire (only a person's pick fires it)"
|
|
167363
167487
|
],
|
|
167364
167488
|
mounted: [
|
|
@@ -179739,7 +179863,7 @@ var node_catalog_default = {
|
|
|
179739
179863
|
group: "Request",
|
|
179740
179864
|
plug: "output",
|
|
179741
179865
|
type: {
|
|
179742
|
-
name: "
|
|
179866
|
+
name: "string"
|
|
179743
179867
|
},
|
|
179744
179868
|
isSignal: false,
|
|
179745
179869
|
description: "Id of the user the session token resolved to, and blank for an unauthenticated request"
|
|
@@ -180965,8 +181089,8 @@ var CatalogIndex = class _CatalogIndex {
|
|
|
180965
181089
|
suggestPort(typeName4, plug, unknown, kind) {
|
|
180966
181090
|
const names2 = this.portNames(typeName4, plug);
|
|
180967
181091
|
const candidates = kind ? names2.filter((name2) => {
|
|
180968
|
-
const
|
|
180969
|
-
return
|
|
181092
|
+
const own2 = this.portKind(typeName4, plug, name2);
|
|
181093
|
+
return own2 === void 0 || own2 === kind;
|
|
180970
181094
|
}) : names2;
|
|
180971
181095
|
return nearest(unknown, candidates);
|
|
180972
181096
|
}
|
|
@@ -196603,7 +196727,8 @@ function coerce(type, value2) {
|
|
|
196603
196727
|
}
|
|
196604
196728
|
}
|
|
196605
196729
|
}
|
|
196606
|
-
function applyRequestContract(specs2,
|
|
196730
|
+
function applyRequestContract(specs2, parsed2) {
|
|
196731
|
+
const body = parsed2 !== null && typeof parsed2 === "object" && !Array.isArray(parsed2) ? parsed2 : {};
|
|
196607
196732
|
const values2 = { ...body };
|
|
196608
196733
|
const errors = [];
|
|
196609
196734
|
for (const spec of specs2) {
|
|
@@ -196723,7 +196848,21 @@ for (const invocation of invocations) {
|
|
|
196723
196848
|
HOSTS3.push({ invocation: invocations[0], cloudServices: { ...CLOUD, runId: "run-1" } });
|
|
196724
196849
|
var CloudRequest = defineNode({
|
|
196725
196850
|
type: "noodl.cloud.request",
|
|
196726
|
-
version:
|
|
196851
|
+
version: 3,
|
|
196852
|
+
changes: [
|
|
196853
|
+
{
|
|
196854
|
+
version: 2,
|
|
196855
|
+
says: "A body that is not a JSON object, sent to a function whose parameters declare a contract, names no fields: a Required one refuses the request with the 400 naming it, and with none Required the function runs. It threw a TypeError reading a name off null (a 500)",
|
|
196856
|
+
row: "P107-S97-NULLBODY (the Beta sweep s5)",
|
|
196857
|
+
migration: { none: "Nothing a project stores changes: only a caller sending a body that is not an object to a function with a contract gets a different answer" }
|
|
196858
|
+
},
|
|
196859
|
+
{
|
|
196860
|
+
version: 3,
|
|
196861
|
+
says: "User Id is text (it carries the id of the person the session token resolved to). It was declared a flag, so a wire into a Number read the id as NaN and the canvas offered it to a signal input",
|
|
196862
|
+
row: "P107-S97-USERIDBOOLEAN (the Beta sweep s6)",
|
|
196863
|
+
migration: { none: "No saved wire stops connecting: measured 106 wires from User Id in 13,839 project files, 70 into script inputs (any type), 35 into text ports, 1 into a record field, none into a signal" }
|
|
196864
|
+
}
|
|
196865
|
+
],
|
|
196727
196866
|
source: "packages/noodl-viewer-cloud/src/nodes/cloud/request.ts; packages/noodl-viewer-cloud/src/nodes/cloud/requestContract.ts; packages/noodl-viewer-cloud/src/nodes/cloud/requestOrigin.ts; packages/noodl-viewer-cloud/src/services/userservice.js",
|
|
196728
196867
|
needs: ["host", "network", "random"],
|
|
196729
196868
|
worldPool: {
|
|
@@ -196758,7 +196897,7 @@ var CloudRequest = defineNode({
|
|
|
196758
196897
|
// :120-128 — its getter, `!!`
|
|
196759
196898
|
auth: { type: "boolean", from: (s) => !!s.authenticated, displayName: "Authenticated", group: "Request", description: "Whether the request carried a session token that resolved to a user" },
|
|
196760
196899
|
// :129-137 — declared `boolean`, carries the id
|
|
196761
|
-
userId: { type: "
|
|
196900
|
+
userId: { type: "string", from: (s) => s.authUserId, displayName: "User Id", group: "Request", description: "Id of the user the session token resolved to, and blank for an unauthenticated request" },
|
|
196762
196901
|
// :142-151
|
|
196763
196902
|
origin: { type: "string", from: (s) => s.origin, displayName: "Origin", group: "Request", description: "The address the calling app is served from \u2014 its Origin header, or this backend\u2019s own host when the caller sent none. Blank when neither is known (a workflow step has no caller). Caller-supplied like every header: right for links sent back to whoever called, not a proof of where the request came from." }
|
|
196764
196903
|
}
|
|
@@ -199092,7 +199231,7 @@ var Model27 = defineNode({
|
|
|
199092
199231
|
// :250-272
|
|
199093
199232
|
modelId: (s, value2, i2, w) => {
|
|
199094
199233
|
let v = value2;
|
|
199095
|
-
const
|
|
199234
|
+
const own2 = [];
|
|
199096
199235
|
if (w.registry.isRecord(v)) v = v.getId();
|
|
199097
199236
|
else if (typeof v === "object" && v !== null) {
|
|
199098
199237
|
const data = v;
|
|
@@ -199105,20 +199244,20 @@ var Model27 = defineNode({
|
|
|
199105
199244
|
if (key === "id" || before3[key] === data[key]) continue;
|
|
199106
199245
|
if (s.properties.includes(key)) {
|
|
199107
199246
|
w.send("prop-" + key, s);
|
|
199108
|
-
|
|
199247
|
+
own2.push({ derived: "changed-" + key });
|
|
199109
199248
|
}
|
|
199110
|
-
|
|
199249
|
+
own2.push("changed");
|
|
199111
199250
|
}
|
|
199112
199251
|
}
|
|
199113
199252
|
}
|
|
199114
199253
|
const previous = s.modelId;
|
|
199115
199254
|
if (valueDidChange2(previous, v) && i2["runOnChange-modelId"]) {
|
|
199116
199255
|
const st = { ...s, modelId: v };
|
|
199117
|
-
const pulses = [...
|
|
199256
|
+
const pulses = [...own2];
|
|
199118
199257
|
setModelID(st, w, v, pulses);
|
|
199119
199258
|
return { says: "a new Id, ticked under Run On Value Change: bound to the object it names now (made if none has it) and Fetched fires \u2014 an empty Id unbinds, silently; an object handed writes its keys onto that object first", set: { modelId: v, bound: st.bound, jobs: st.jobs, storeScheduled: st.storeScheduled }, send: [], pulses };
|
|
199120
199259
|
}
|
|
199121
|
-
return { says: "otherwise (the same Id, or unticked): Id is sent and the binding stays; an object handed still writes its keys onto the object it names", set: { modelId: v }, send: ["id"], pulses:
|
|
199260
|
+
return { says: "otherwise (the same Id, or unticked): Id is sent and the binding stays; an object handed still writes its keys onto the object it names", set: { modelId: v }, send: ["id"], pulses: own2 };
|
|
199122
199261
|
},
|
|
199123
199262
|
// :274-280 — `set: function () {}`
|
|
199124
199263
|
properties: () => ({ says: "always: nothing while it runs; each name makes an input, an output and its own Changed", send: [] }),
|
|
@@ -199179,9 +199318,9 @@ var Model27 = defineNode({
|
|
|
199179
199318
|
const model2 = w.registry.model(st.bound);
|
|
199180
199319
|
for (const key of Object.keys(st.dirty)) {
|
|
199181
199320
|
const value2 = st.inputValues[key];
|
|
199182
|
-
const
|
|
199321
|
+
const own2 = key.indexOf(".") === -1 && model2.get(key) !== value2;
|
|
199183
199322
|
model2.set(key, value2, { resolve: true });
|
|
199184
|
-
if (
|
|
199323
|
+
if (own2 && i2["runOnChange-object"]) {
|
|
199185
199324
|
if (st.properties.includes(key)) {
|
|
199186
199325
|
w.send("prop-" + key, st);
|
|
199187
199326
|
pulses.push({ derived: "changed-" + key });
|
|
@@ -199219,8 +199358,8 @@ var Model27 = defineNode({
|
|
|
199219
199358
|
change: (s, i2, e) => {
|
|
199220
199359
|
if (e.kind !== "model" || e.id !== s.bound || !i2["runOnChange-object"]) return { send: [] };
|
|
199221
199360
|
const key = String(e.name);
|
|
199222
|
-
const
|
|
199223
|
-
return { sendDerived:
|
|
199361
|
+
const own2 = s.properties.includes(key);
|
|
199362
|
+
return { sendDerived: own2 ? ["prop-" + key] : [], emitDerived: own2 ? ["changed-" + key] : [], emit: ["changed"], send: [] };
|
|
199224
199363
|
}
|
|
199225
199364
|
}
|
|
199226
199365
|
}
|
|
@@ -201821,7 +201960,9 @@ var Dropdown = defineNode({
|
|
|
201821
201960
|
// v8 (P107 R21, P107-S88-OPTIONSCAST): a word in a list on Items is an option, labelled and valued by the word — v7 drew an option with no text and the value ''
|
|
201822
201961
|
// v9 (NSP-019 R35): a Focus that finds the node off the page shows as its warning until a Focus reaches it, and an Icon Source that is not an icon shows as one until an icon or nothing arrives (`diagnostics`)
|
|
201823
201962
|
// v10 (NSP-019 R38): a wired Width in a row, or a wired Height in a column, that is a percent shows as its warning (wired-grow.ts)
|
|
201824
|
-
|
|
201963
|
+
// v11 (P121-TWO005-DROPDOWNSTALE): Items that do not offer the placed node's seed ("Option 1") clear it to '' and send Value
|
|
201964
|
+
// v12 (BETA-S7-DROPDOWNFIRSTPICK): with the value in no option, the element keeps its hidden empty option selected, so every option is a pick
|
|
201965
|
+
version: 12,
|
|
201825
201966
|
// NSP-010 — v7 is the spec correcting itself, not the runtime changing
|
|
201826
201967
|
changes: [
|
|
201827
201968
|
{
|
|
@@ -201841,7 +201982,23 @@ var Dropdown = defineNode({
|
|
|
201841
201982
|
}
|
|
201842
201983
|
},
|
|
201843
201984
|
focusAndIconSourceChange(9),
|
|
201844
|
-
growChange(10)
|
|
201985
|
+
growChange(10),
|
|
201986
|
+
{
|
|
201987
|
+
version: 11,
|
|
201988
|
+
says: `A Dropdown nobody has given a Value or a pick holds the first default option, "Option 1", so a placed node shows a real option. When Items arrive and do not offer it, Value now becomes '' (empty) and is sent, as the list shows its placeholder. Before, Value went on saying "Option 1", an option the list did not have, and a form that checked whether anything was chosen sent it. A Value somebody wrote, or a person's pick, is never changed`,
|
|
201989
|
+
row: "P121-TWO005-DROPDOWNSTALE",
|
|
201990
|
+
migration: {
|
|
201991
|
+
none: "Only a value the screen never showed changes: the placeholder was already drawn, and the community template, which met it, already set Value to '' itself. No stored project changes"
|
|
201992
|
+
}
|
|
201993
|
+
},
|
|
201994
|
+
{
|
|
201995
|
+
version: 12,
|
|
201996
|
+
says: "While the Dropdown shows its placeholder because its value is in no option, choosing the first option now chooses it: Value and Changed. Before, the list underneath had already selected that first option (the browser does when the selected option leaves the list, or when options arrive in an empty one), so choosing it did nothing and the placeholder stayed. Every other option was already a pick",
|
|
201997
|
+
row: "BETA-S7-DROPDOWNFIRSTPICK",
|
|
201998
|
+
migration: {
|
|
201999
|
+
none: "A pick that did nothing now does what the person asked; nothing an app could rely on, and no stored project changes"
|
|
202000
|
+
}
|
|
202001
|
+
}
|
|
201845
202002
|
],
|
|
201846
202003
|
diagnostics: { ...focusDiagnostics(controlFocusNotMounted("dropdown")), ...iconSourceDiagnostics(), ...wiredGrowDiagnostic },
|
|
201847
202004
|
source: "packages/noodl-viewer-react/src/nodes/controls/options.ts",
|
|
@@ -201864,7 +202021,8 @@ var Dropdown = defineNode({
|
|
|
201864
202021
|
focusMissed: false,
|
|
201865
202022
|
value: "Option 1",
|
|
201866
202023
|
items: DEFAULT_ITEMS2,
|
|
201867
|
-
domSelected: ""
|
|
202024
|
+
domSelected: "",
|
|
202025
|
+
seeded: true
|
|
201868
202026
|
},
|
|
201869
202027
|
// utils.ts :402-411 `outcomeOutputs({ done, unchanged })` (addFocusActions)
|
|
201870
202028
|
outcomes: ["done", "unchanged"],
|
|
@@ -202086,7 +202244,12 @@ var Dropdown = defineNode({
|
|
|
202086
202244
|
},
|
|
202087
202245
|
// options.ts :158-171 — `undefined` is no opinion; anything else is kept as handed, drawn at the next paint (`painted`); the
|
|
202088
202246
|
// node's value stays (R14: a list handed again keeps the pick — the runtime's effect took the author's Value back until P109 ISL-031, row D34)
|
|
202089
|
-
items: (s, v) =>
|
|
202247
|
+
items: (s, v) => {
|
|
202248
|
+
if (v === void 0) return { says: "nothing handed (undefined): the list it has stays", send: [] };
|
|
202249
|
+
if (s.seeded && !offersValue2(v, s.value))
|
|
202250
|
+
return { says: "the value is still the placed node's seed and the list does not offer it: the list is kept, and the value is cleared to '' and sent", set: { items: v, value: "", seeded: false }, send: ["value"] };
|
|
202251
|
+
return { says: "otherwise: the list is kept as handed and drawn at the next paint; the value stays, so a list handed again keeps the pick", set: { items: v }, send: [] };
|
|
202252
|
+
},
|
|
202090
202253
|
// options.ts :181-200 — `null` has no `toString` and is ignored (:182-184); what is not text is made text; a change sends
|
|
202091
202254
|
// Value and never Changed, and the selection follows it at the next paint (R14: the last word wins — the runtime's effect
|
|
202092
202255
|
// keyed on the author's Value React last rendered, so a write back to it after a pick left the pick on the screen, row C71,
|
|
@@ -202094,8 +202257,8 @@ var Dropdown = defineNode({
|
|
|
202094
202257
|
value: (s, v) => {
|
|
202095
202258
|
if (v === null) return { says: "null: ignored", send: [] };
|
|
202096
202259
|
const value2 = v === void 0 ? void 0 : String(v);
|
|
202097
|
-
if (value2 === s.value) return { says: "the value it already holds, as text: nothing", send: [] };
|
|
202098
|
-
return { says: "a change: kept as text, Value follows, and the list selects it at the next paint if it names an option \u2014 Changed does not fire (only a person's pick fires it)", set: { value: value2 }, send: ["value"] };
|
|
202260
|
+
if (value2 === s.value) return { says: "the value it already holds, as text: nothing (it is no longer the seed)", set: { seeded: false }, send: [] };
|
|
202261
|
+
return { says: "a change: kept as text, Value follows, and the list selects it at the next paint if it names an option \u2014 Changed does not fire (only a person's pick fires it)", set: { value: value2, seeded: false }, send: ["value"] };
|
|
202099
202262
|
},
|
|
202100
202263
|
// react-component-node.ts :2310-2320
|
|
202101
202264
|
mounted: (_s, v) => ({ says: "always: the list is put on the page (Did Mount), or taken off it (Will Unmount), at the next paint", set: { mounted: !!v }, send: [] }),
|
|
@@ -202157,7 +202320,7 @@ var Dropdown = defineNode({
|
|
|
202157
202320
|
if (!option || option.disabled) return NONE4;
|
|
202158
202321
|
const focus = p.elementFocused ? NONE4 : focusTaken2();
|
|
202159
202322
|
if (p.domSelected === picked) return focus;
|
|
202160
|
-
return join3(focus, { set: { value: picked, domSelected: picked }, send: ["value"], emit: ["onChange"] });
|
|
202323
|
+
return join3(focus, { set: { value: picked, domSelected: picked, seeded: false }, send: ["value"], emit: ["onChange"] });
|
|
202161
202324
|
}, "keyboard")
|
|
202162
202325
|
}
|
|
202163
202326
|
}
|
|
@@ -202176,6 +202339,12 @@ function readStoredItems(stored) {
|
|
|
202176
202339
|
function editorItems(items) {
|
|
202177
202340
|
return typeof items === "string" ? readStoredItems(items) : itemRows(items);
|
|
202178
202341
|
}
|
|
202342
|
+
function offersValue2(items, value2) {
|
|
202343
|
+
if (!items) return false;
|
|
202344
|
+
const rows = typeof items === "string" ? readStoredItems(items) : Array.isArray(items) ? items : [];
|
|
202345
|
+
const words = rows.some((item) => item === null || item === void 0 || typeof item !== "object");
|
|
202346
|
+
return rows.filter((item) => item !== null && item !== void 0).map((item) => words && typeof item !== "object" ? { Value: String(item) } : item).some((item) => typeof item === "object" && item.Value === value2);
|
|
202347
|
+
}
|
|
202179
202348
|
function itemRows(source2) {
|
|
202180
202349
|
if (!Array.isArray(source2)) return [];
|
|
202181
202350
|
const out = [];
|
|
@@ -202221,12 +202390,7 @@ function painted4(s) {
|
|
|
202221
202390
|
const value2 = s.value;
|
|
202222
202391
|
const options = optionsOf(s.items);
|
|
202223
202392
|
const inList = value2 !== void 0 && options.some((o) => o.value === value2);
|
|
202224
|
-
const
|
|
202225
|
-
let dom = s.domSelected;
|
|
202226
|
-
if (fresh) dom = inList ? value2 : "";
|
|
202227
|
-
else if (inList) dom = value2;
|
|
202228
|
-
else if (dom !== "" && dom !== null && !options.some((o) => o.value === dom)) dom = firstEnabled;
|
|
202229
|
-
else if (dom === null) dom = firstEnabled;
|
|
202393
|
+
const dom = inList ? value2 : "";
|
|
202230
202394
|
if (dom !== s.domSelected) set.domSelected = dom;
|
|
202231
202395
|
return { set, send: [], emit: emit2 };
|
|
202232
202396
|
}
|
|
@@ -207776,7 +207940,7 @@ function copy(w, src) {
|
|
|
207776
207940
|
return hearingRecords(w, () => copyInto(w, src));
|
|
207777
207941
|
}
|
|
207778
207942
|
function copyInto(w, src) {
|
|
207779
|
-
const
|
|
207943
|
+
const own2 = w.st.own;
|
|
207780
207944
|
const source2 = src || [];
|
|
207781
207945
|
let empty2 = 0;
|
|
207782
207946
|
for (let i2 = 0; i2 < source2.length; i2++) if (source2[i2] === null || source2[i2] === void 0) empty2++;
|
|
@@ -207792,13 +207956,13 @@ function copyInto(w, src) {
|
|
|
207792
207956
|
src = Array.prototype.filter.call(source2, (item) => item !== null && item !== void 0);
|
|
207793
207957
|
}
|
|
207794
207958
|
const ops = [];
|
|
207795
|
-
const list2 =
|
|
207959
|
+
const list2 = own2;
|
|
207796
207960
|
const add = (a) => void ops.push({ kind: "add", rec: a.item, index: a.index });
|
|
207797
207961
|
const remove = (a) => void ops.push({ kind: "remove", rec: a.item });
|
|
207798
207962
|
list2.on("add", add);
|
|
207799
207963
|
list2.on("remove", remove);
|
|
207800
207964
|
try {
|
|
207801
|
-
|
|
207965
|
+
own2.set(src);
|
|
207802
207966
|
} finally {
|
|
207803
207967
|
list2.off("add", add);
|
|
207804
207968
|
list2.off("remove", remove);
|
|
@@ -207966,11 +208130,11 @@ function addItem(w, rec, _index) {
|
|
|
207966
208130
|
}
|
|
207967
208131
|
function childIndexFor(rows, listIndex) {
|
|
207968
208132
|
let at = 0;
|
|
207969
|
-
let
|
|
208133
|
+
let own2 = 0;
|
|
207970
208134
|
for (let i2 = 0; i2 < rows.length; i2++) {
|
|
207971
208135
|
if (rows[i2].leaving) continue;
|
|
207972
|
-
if (
|
|
207973
|
-
|
|
208136
|
+
if (own2 === listIndex) return i2;
|
|
208137
|
+
own2 += 1;
|
|
207974
208138
|
at = i2 + 1;
|
|
207975
208139
|
}
|
|
207976
208140
|
return at;
|
|
@@ -209331,13 +209495,13 @@ function merge(a, b) {
|
|
|
209331
209495
|
function then(s, first2, next2) {
|
|
209332
209496
|
return merge(first2, next2({ ...s, ...first2.set }));
|
|
209333
209497
|
}
|
|
209334
|
-
function select(s, v,
|
|
209498
|
+
function select(s, v, own2) {
|
|
209335
209499
|
if (!s.inGroup || s.gValue === v) return NONE14;
|
|
209336
209500
|
const set = { gValue: v };
|
|
209337
209501
|
if (!s.onPage) return { set, send: [], emit: [] };
|
|
209338
209502
|
const c = v === s.value;
|
|
209339
209503
|
if (c === s.checked) return { set, send: [], emit: [] };
|
|
209340
|
-
return { set: { ...set, checked: c }, send: ["checked"], emit:
|
|
209504
|
+
return { set: { ...set, checked: c }, send: ["checked"], emit: own2 || !c ? ["onChange"] : [] };
|
|
209341
209505
|
}
|
|
209342
209506
|
function painted14(s) {
|
|
209343
209507
|
const set = { ...s.pageEnabled === s.enabled ? {} : { pageEnabled: s.enabled }, ...s.pageVisible === s.visible ? {} : { pageVisible: s.visible } };
|
|
@@ -212670,7 +212834,8 @@ var LogIn = defineNode({
|
|
|
212670
212834
|
var SignUp = defineNode({
|
|
212671
212835
|
type: "net.noodl.user.SignUp",
|
|
212672
212836
|
// v2 (P107 s92, P107-S84-USERACL ruled R29): no `prop-ACL` input (signup.ts `_ignoreKeys` names ACL)
|
|
212673
|
-
|
|
212837
|
+
// v3 (P107-S102-SIGNUPBACKEND): the `prop-` inputs are the active backend's accounts table, not the Parse `_User` always
|
|
212838
|
+
version: 3,
|
|
212674
212839
|
changes: [
|
|
212675
212840
|
{
|
|
212676
212841
|
version: 2,
|
|
@@ -212679,6 +212844,14 @@ var SignUp = defineNode({
|
|
|
212679
212844
|
migration: {
|
|
212680
212845
|
none: `Ruled "Hide it, like the Record nodes" (R29). The reach measured on 2026-10-12 was 429 User-family nodes in 58 of Richard's projects and none wired to or from ACL, so no saved wire is dropped`
|
|
212681
212846
|
}
|
|
212847
|
+
},
|
|
212848
|
+
{
|
|
212849
|
+
version: 3,
|
|
212850
|
+
says: "The property inputs are the columns of the active backend's accounts table (directus_users on Directus, users on PocketBase, none on Supabase), as Set User Properties draws them, less the columns that backend owns. Before, Sign Up always offered the Parse _User's columns, so a Directus app was offered fields its users table does not have. On the Parse wire nothing moves",
|
|
212851
|
+
row: "P107-S102-SIGNUPBACKEND",
|
|
212852
|
+
migration: {
|
|
212853
|
+
none: "Measured in s102: 4 of 721 projects on Richard's machine have a REST backend and none uses Sign Up on one, so no saved wire is dropped. On the Parse wire the ignore list is the same seven names"
|
|
212854
|
+
}
|
|
212682
212855
|
}
|
|
212683
212856
|
],
|
|
212684
212857
|
source: "packages/noodl-viewer-react/src/nodes/std-library/user/signup.ts; userservice.ts; RestAuthAdapter.ts",
|
|
@@ -212706,12 +212879,15 @@ var SignUp = defineNode({
|
|
|
212706
212879
|
},
|
|
212707
212880
|
{
|
|
212708
212881
|
derived: {
|
|
212709
|
-
// NSP-020 s84 — what the editor draws (signup.ts `updatePorts
|
|
212710
|
-
//
|
|
212711
|
-
//
|
|
212882
|
+
// NSP-020 s84, P107-S102-SIGNUPBACKEND — what the editor draws (signup.ts `updatePorts`, user-ports.ts as Set User
|
|
212883
|
+
// Properties): one `prop-` per column of the ACTIVE backend's accounts class less the ones a browser cannot write
|
|
212884
|
+
// (`USER_INPUT_IGNORE_PARSE_BROWSER`; off the Parse wire `USER_INPUT_IGNORE_REST`) and every Relation. No Backend picker,
|
|
212885
|
+
// and a saved `backendId` is not read (the builder gives this type the active backend's schema). At play there is no
|
|
212886
|
+
// schema: a write registers each (:185-195)
|
|
212712
212887
|
inputs: (_params, context) => {
|
|
212713
212888
|
const out = {};
|
|
212714
|
-
|
|
212889
|
+
const ignore = onParseWire(context) ? USER_INPUT_IGNORE_PARSE_BROWSER2 : USER_INPUT_IGNORE_REST2;
|
|
212890
|
+
for (const [p, decl] of userFields(context, ignore)) out["prop-" + p] = { ...signUpProperty(p), ...recordFieldPort(decl) };
|
|
212715
212891
|
return out;
|
|
212716
212892
|
},
|
|
212717
212893
|
needs: ["schema"],
|
|
@@ -214244,10 +214420,10 @@ var WebSocketNode = defineNode({
|
|
|
214244
214420
|
run5.own = { pending: true };
|
|
214245
214421
|
const url = (run5.getConnection().cfg.url || "").trim();
|
|
214246
214422
|
run5.connect(false);
|
|
214247
|
-
const
|
|
214423
|
+
const own2 = run5.own.settled;
|
|
214248
214424
|
const p = run5.patch();
|
|
214249
|
-
if (
|
|
214250
|
-
const failed = { ...p, outcome:
|
|
214425
|
+
if (own2) {
|
|
214426
|
+
const failed = { ...p, outcome: own2.outcome, ...own2.error !== void 0 ? { error: own2.error } : {}, ...settles ? { settles } : {} };
|
|
214251
214427
|
if (!url)
|
|
214252
214428
|
return said(
|
|
214253
214429
|
"a blank URL: any socket held is closed quietly and the countdown and heartbeat stopped (a Connect still waiting: Unchanged), then it fails at once \u2014 Connection State error, Last Error `URL is required to connect`, Retry Count 0, the Status outputs sent, On Error; Failure (websocket/connect-failed)",
|
|
@@ -215290,9 +215466,9 @@ var ServerSentEvents = defineNode({
|
|
|
215290
215466
|
const settles = s.pendingConnect ? [{ outcome: "unchanged" }] : void 0;
|
|
215291
215467
|
run5.s.pendingConnect = false;
|
|
215292
215468
|
run5.doConnect(true);
|
|
215293
|
-
const
|
|
215469
|
+
const own2 = run5.own.settled;
|
|
215294
215470
|
const p = run5.patch();
|
|
215295
|
-
const patch =
|
|
215471
|
+
const patch = own2 ? { ...p, outcome: own2.outcome, ...own2.error !== void 0 ? { error: own2.error } : {}, ...settles ? { settles } : {} } : { ...p, outcome: "pending", ...settles ? { settles } : {} };
|
|
215296
215472
|
if (!s.url)
|
|
215297
215473
|
return said(
|
|
215298
215474
|
"no URL: a stream already open or retrying is dropped silently (its request abandoned, its timer cancelled; a Connect still waiting: Unchanged), then it fails for good \u2014 Last Error `No URL was set on the stream`, On Error, Connection State error (Connected false, Retry Count 0), On Close; Failure (sse/connect-failed)",
|
|
@@ -218623,7 +218799,9 @@ var Expression = defineNode({
|
|
|
218623
218799
|
var INPUT_PATTERNS2 = [/Inputs\.([A-Za-z0-9_]+)/g, /Inputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]/g];
|
|
218624
218800
|
var SIGNAL_PATTERNS2 = [
|
|
218625
218801
|
/Outputs\.([A-Za-z0-9_]+)\s*\(\s*\)/g,
|
|
218626
|
-
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\(\s*\)/g
|
|
218802
|
+
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\(\s*\)/g,
|
|
218803
|
+
/Outputs\.([A-Za-z0-9_]+)\.send\s*\(\s*\)/g,
|
|
218804
|
+
/Outputs\s*\[\s*(?:'([^'\n]*)'|"([^"\n]*)")\s*\]\.send\s*\(\s*\)/g
|
|
218627
218805
|
];
|
|
218628
218806
|
var OUTPUT_PATTERNS2 = [/Outputs\.([A-Za-z0-9_]+)/g, /Outputs\s*\[\s*"([^"]*)"\s*\]/g];
|
|
218629
218807
|
function collect2(source2, patterns, into, skip) {
|
|
@@ -218823,7 +219001,18 @@ var schedule3 = { scheduled: true };
|
|
|
218823
219001
|
var JavaScriptFunction = defineNode({
|
|
218824
219002
|
type: "JavaScriptFunction",
|
|
218825
219003
|
// v2 (P109 ISL-051, s33): Error cleared to blank text and sent by a run that works and by a new Script (row C146)
|
|
218826
|
-
|
|
219004
|
+
// v3 (P107-S83-SENDFORM): `Outputs.x.send()` names a signal port, as `Outputs.x()` does
|
|
219005
|
+
version: 3,
|
|
219006
|
+
changes: [
|
|
219007
|
+
{
|
|
219008
|
+
version: 3,
|
|
219009
|
+
says: "Code that fires a signal as `Outputs.done.send()` now makes `done` a signal port, and the call fires it (before, `done` was a value port, the call threw \"Cannot read properties of undefined (reading 'send')\", and the run reported Error and Failure every time)",
|
|
219010
|
+
row: "P107-S83-SENDFORM",
|
|
219011
|
+
migration: {
|
|
219012
|
+
none: "Each before was a throw at every run: no value ever reached a `done` read as a value, so a wire from it carried nothing, and the same wire now carries the signal the code meant"
|
|
219013
|
+
}
|
|
219014
|
+
}
|
|
219015
|
+
],
|
|
218827
219016
|
source: "packages/noodl-runtime/src/nodes/std-library/simplejavascript.ts",
|
|
218828
219017
|
contract: "ports from the code and the written lists; when the code runs (the Script \u2014 only while Run is not wired, P107-C155 \u2014, Run, a changed ticked input; once a frame); Inputs, this and Noodl as the code sees them; publish on change; signal outputs; Success and Done after an await; a throw, sync or async; a compile failure; no script \u2014 FUNCTION_FIXTURES, one per rule",
|
|
218829
219018
|
state: {
|
|
@@ -219834,12 +220023,12 @@ function run4(s, trigger, _world) {
|
|
|
219834
220023
|
}
|
|
219835
220024
|
function blockPorts(ws) {
|
|
219836
220025
|
const io = detectIO2(typeof ws === "string" ? ws : void 0);
|
|
219837
|
-
const
|
|
220026
|
+
const own2 = (list2) => (n) => list2.indexOf(n) === -1;
|
|
219838
220027
|
return {
|
|
219839
|
-
inputs: io.inputs.map((p) => p.name).filter(
|
|
219840
|
-
signalInputs: io.signalInputs.filter(
|
|
219841
|
-
outputs: io.outputs.map((p) => p.name).filter(
|
|
219842
|
-
signalOutputs: io.signalOutputs.filter(
|
|
220028
|
+
inputs: io.inputs.map((p) => p.name).filter(own2(RESERVED_INPUTS2)),
|
|
220029
|
+
signalInputs: io.signalInputs.filter(own2(RESERVED_INPUTS2)),
|
|
220030
|
+
outputs: io.outputs.map((p) => p.name).filter(own2(RESERVED_OUTPUTS4)),
|
|
220031
|
+
signalOutputs: io.signalOutputs.filter(own2(RESERVED_OUTPUTS4))
|
|
219843
220032
|
};
|
|
219844
220033
|
}
|
|
219845
220034
|
var typeOf = (ws, plug, name2) => typeOfPort2(detectIO2(typeof ws === "string" ? ws : void 0), plug, name2);
|
|
@@ -220123,7 +220312,7 @@ function portContextReader(project, options = {}) {
|
|
|
220123
220312
|
if (wired.length) context.wired = wired;
|
|
220124
220313
|
if (wiredOutputs.length) context.wiredOutputs = wiredOutputs;
|
|
220125
220314
|
if (backends.length) context.backends = backends.map((b) => ({ ...b }));
|
|
220126
|
-
const schema = schemaFor3(project.metadata, node.parameters ?? {});
|
|
220315
|
+
const schema = schemaFor3(project.metadata, NO_BACKEND_INPUT.includes(node.type) ? {} : node.parameters ?? {});
|
|
220127
220316
|
if (schema) context.schema = schema;
|
|
220128
220317
|
if (stacks.length) context.stacks = stacks;
|
|
220129
220318
|
if (routers.length) context.routers = routers;
|
|
@@ -220132,6 +220321,7 @@ function portContextReader(project, options = {}) {
|
|
|
220132
220321
|
return context;
|
|
220133
220322
|
};
|
|
220134
220323
|
}
|
|
220324
|
+
var NO_BACKEND_INPUT = ["net.noodl.user.SignUp"];
|
|
220135
220325
|
function unique(names2) {
|
|
220136
220326
|
return [...new Set(names2)];
|
|
220137
220327
|
}
|
|
@@ -231753,6 +231943,12 @@ var fs7 = __toESM(require("fs"));
|
|
|
231753
231943
|
var path8 = __toESM(require("path"));
|
|
231754
231944
|
|
|
231755
231945
|
// src/projectExport.ts
|
|
231946
|
+
var LOOPBACK = /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?(\/|$)/i;
|
|
231947
|
+
function loopbackBackendWarning(metadata) {
|
|
231948
|
+
const endpoint = metadata?.cloudservices?.endpoint;
|
|
231949
|
+
if (typeof endpoint !== "string" || !LOOPBACK.test(endpoint)) return void 0;
|
|
231950
|
+
return `This app talks to a backend on this computer (${endpoint}), and the deployed copy will ask each visitor's own computer for it, so its records, sign-in and functions will not work for anyone else. Put the backend on a server first (deploy_to_vm, or nodegx-deploy --full-stack, ships both), or point the project at a backend others can reach, then deploy again.`;
|
|
231951
|
+
}
|
|
231756
231952
|
function resolveRootNode(project) {
|
|
231757
231953
|
const components = project.getComponents();
|
|
231758
231954
|
const byName = (name2) => components.find((c) => c.name === name2);
|
|
@@ -231786,7 +231982,18 @@ function projectModelFromLegacy(legacy) {
|
|
|
231786
231982
|
var fs6 = __toESM(require("fs"));
|
|
231787
231983
|
var path7 = __toESM(require("path"));
|
|
231788
231984
|
init_scriptfiles();
|
|
231789
|
-
var readJson = (file) =>
|
|
231985
|
+
var readJson = (file) => {
|
|
231986
|
+
const text3 = fs6.readFileSync(file, "utf8");
|
|
231987
|
+
try {
|
|
231988
|
+
return JSON.parse(text3);
|
|
231989
|
+
} catch (e) {
|
|
231990
|
+
const why = text3.trim() === "" ? "the file is empty, so it is not valid JSON" : e instanceof Error ? e.message : String(e);
|
|
231991
|
+
throw Object.assign(new Error(`${file.split(path7.sep).join("/")}: ${why}`), { projectFile: file });
|
|
231992
|
+
}
|
|
231993
|
+
};
|
|
231994
|
+
function isProjectFileError(error) {
|
|
231995
|
+
return !!error && typeof error.projectFile === "string";
|
|
231996
|
+
}
|
|
231790
231997
|
function resolveTarget(target) {
|
|
231791
231998
|
const abs = path7.resolve(target);
|
|
231792
231999
|
if (!fs6.existsSync(abs)) throw new Error(`No such file or directory: ${abs}`);
|
|
@@ -231916,6 +232123,9 @@ function flatten3(roots) {
|
|
|
231916
232123
|
for (const root2 of roots) visit(root2, void 0);
|
|
231917
232124
|
return out;
|
|
231918
232125
|
}
|
|
232126
|
+
function recordedVisualRoots(list2) {
|
|
232127
|
+
return Array.isArray(list2) && list2.length > 0 ? { visualRoots: list2.filter((id) => typeof id === "string") } : {};
|
|
232128
|
+
}
|
|
231919
232129
|
function fromLegacyProject(project) {
|
|
231920
232130
|
const components = [];
|
|
231921
232131
|
for (const comp of project.components ?? []) {
|
|
@@ -231929,7 +232139,8 @@ function fromLegacyProject(project) {
|
|
|
231929
232139
|
components.push({
|
|
231930
232140
|
name: comp.name,
|
|
231931
232141
|
nodes: flatten3(graph.roots ?? []),
|
|
231932
|
-
connections
|
|
232142
|
+
connections,
|
|
232143
|
+
...recordedVisualRoots(graph.visualRoots)
|
|
231933
232144
|
});
|
|
231934
232145
|
}
|
|
231935
232146
|
return {
|
|
@@ -232677,6 +232888,7 @@ function cloudPortMeaning(type, plug) {
|
|
|
232677
232888
|
|
|
232678
232889
|
// ../noodl-editor/src/editor/src/validation/functionScriptUses.ts
|
|
232679
232890
|
init_scriptfiles();
|
|
232891
|
+
var FUNCTION_NODE_TYPE = "JavaScriptFunction";
|
|
232680
232892
|
|
|
232681
232893
|
// ../noodl-editor/src/editor/src/validation/functionPorts.ts
|
|
232682
232894
|
var SCRIPT_NODE_API_MEMBERS = [
|
|
@@ -232695,6 +232907,10 @@ var SCRIPT_DECLARATION = new RegExp(
|
|
|
232695
232907
|
)
|
|
232696
232908
|
);
|
|
232697
232909
|
|
|
232910
|
+
// ../noodl-editor/src/editor/src/validation/publicWriteDoor.ts
|
|
232911
|
+
var RECORD_WRITE_SCRIPT_MEMBERS = ["create", "save", "delete", "increment", "addRelation", "removeRelation"];
|
|
232912
|
+
var SCRIPT_WRITE_CALL = new RegExp(`\\bRecords\\s*\\.\\s*(${RECORD_WRITE_SCRIPT_MEMBERS.join("|")})\\s*\\(`);
|
|
232913
|
+
|
|
232698
232914
|
// ../noodl-editor/src/editor/src/validation/typedWordNotTranslated.ts
|
|
232699
232915
|
var HAS_LETTER = new RegExp("\\p{L}", "u");
|
|
232700
232916
|
|
|
@@ -232891,7 +233107,11 @@ var AUTHORED_BLOCKING_WARNINGS = /* @__PURE__ */ new Set([
|
|
|
232891
233107
|
// and **no `completed` wires at all**. `completed` is a rare port — authors
|
|
232892
233108
|
// reach for `done` — so the blast radius of making this blocking is small, and
|
|
232893
233109
|
// that is a measurement rather than a hope.
|
|
232894
|
-
"completed-commits-unchecked" /* CompletedCommitsUnchecked
|
|
233110
|
+
"completed-commits-unchecked" /* CompletedCommitsUnchecked */,
|
|
233111
|
+
// Bug P121-TWO003-SCRIPTCOMPILE — the runtime's own compile fails, so every Run fails. No benign
|
|
233112
|
+
// reading for a graph an agent just wrote; the write said nothing before, and the first person
|
|
233113
|
+
// to run the function got "That could not be done."
|
|
233114
|
+
"function-script-not-compiled" /* FunctionScriptNotCompiled */
|
|
232895
233115
|
]);
|
|
232896
233116
|
|
|
232897
233117
|
// ../noodl-editor/src/editor/src/validation/diagnosticExamples.ts
|
|
@@ -233020,6 +233240,17 @@ var DIAGNOSTIC_EXAMPLES = [
|
|
|
233020
233240
|
code: "function-trusts-a-field-the-caller-wrote" /* FunctionTrustsAFieldTheCallerWrote */,
|
|
233021
233241
|
examples: ["cloud-share-my-records-with-someone"],
|
|
233022
233242
|
why: "a cloud function that returns the records of the shares' owners, never a field the page wrote"
|
|
233243
|
+
},
|
|
233244
|
+
// P112 OWN-026 FIX-06 B — a group joined only through its owner's invitation, written only by a function.
|
|
233245
|
+
{
|
|
233246
|
+
code: "role-named-by-a-field-the-caller-wrote" /* RoleNamedByAFieldTheCallerWrote */,
|
|
233247
|
+
examples: ["cloud-members-can-edit-my-records"],
|
|
233248
|
+
why: "a cloud function that names the role from the invitation's owner, so an invitation only lets someone into its creator's group"
|
|
233249
|
+
},
|
|
233250
|
+
{
|
|
233251
|
+
code: "function-guards-an-open-collection" /* FunctionGuardsAnOpenCollection */,
|
|
233252
|
+
examples: ["cloud-members-can-edit-my-records"],
|
|
233253
|
+
why: `the records only a function creates, with the collection's create rule "nobody": the function runs as the system`
|
|
233023
233254
|
}
|
|
233024
233255
|
];
|
|
233025
233256
|
|
|
@@ -233210,6 +233441,9 @@ var unknownNodeType = {
|
|
|
233210
233441
|
}
|
|
233211
233442
|
};
|
|
233212
233443
|
|
|
233444
|
+
// ../noodl-editor/src/editor/src/validation/rules/nonexistentPort.ts
|
|
233445
|
+
init_renamed_ports();
|
|
233446
|
+
|
|
233213
233447
|
// ../noodl-editor/src/editor/src/views/panels/propertyeditor/propertyPanelTiers.ts
|
|
233214
233448
|
var ADVANCED_CSS_GROUPS = {
|
|
233215
233449
|
// ── plumbing ────────────────────────────────────────────────────────────────
|
|
@@ -233288,18 +233522,23 @@ function otherEndKind(catalog, nodeById, conn, plug) {
|
|
|
233288
233522
|
}
|
|
233289
233523
|
return portKindOfTypeName(other.instancePortTypes?.[port5]);
|
|
233290
233524
|
}
|
|
233525
|
+
function renamedPort(type, port5, plug) {
|
|
233526
|
+
const to = plug === "output" ? renamedOutput(type, port5) : void 0;
|
|
233527
|
+
return to ? { to, sentence: ` It was renamed "${to}" in August 2026: wire "${to}".` } : void 0;
|
|
233528
|
+
}
|
|
233291
233529
|
function missingDrawnPort(catalog, nodeById, conn, component, node, port5, plug, drawn3) {
|
|
233292
233530
|
const names2 = [...drawn3];
|
|
233293
233531
|
const tail = (name2) => name2.slice(name2.indexOf("-") + 1).toLowerCase();
|
|
233294
233532
|
const suffixed = names2.filter((name2) => tail(name2) === port5.toLowerCase() || tail(name2) === tail(port5));
|
|
233295
|
-
const
|
|
233533
|
+
const renamed = renamedPort(node.type, port5, plug);
|
|
233534
|
+
const suggestion = renamed && drawn3.has(renamed.to) ? renamed.to : suffixed.length > 0 ? nearest(port5, suffixed) ?? suffixed[0] : nearest(port5, names2);
|
|
233296
233535
|
const wireKind = otherEndKind(catalog, nodeById, conn, plug);
|
|
233297
233536
|
const fromProject = names2.filter((name2) => !catalog.hasPort(node.type, plug, name2)).sort();
|
|
233298
233537
|
const alternatives = [.../* @__PURE__ */ new Set([...fromProject, ...availableAlternatives(catalog, node.type, plug, wireKind).filter((name2) => drawn3.has(name2))])].slice(0, MAX_ALTERNATIVES3);
|
|
233299
233538
|
return {
|
|
233300
233539
|
code: "nonexistent-port" /* NonexistentPort */,
|
|
233301
233540
|
severity: "error",
|
|
233302
|
-
message: `${node.type} has no ${plug} named "${port5}" with its settings in this project
|
|
233541
|
+
message: `${node.type} has no ${plug} named "${port5}" with its settings in this project.` + (renamed && drawn3.has(renamed.to) ? renamed.sentence : ""),
|
|
233303
233542
|
location: {
|
|
233304
233543
|
component,
|
|
233305
233544
|
nodeId: node.id,
|
|
@@ -233379,12 +233618,13 @@ var nonexistentPort = {
|
|
|
233379
233618
|
}
|
|
233380
233619
|
const wireKind = otherEndKind(catalog, nodeById, conn, plug);
|
|
233381
233620
|
const prefixed = missingPrefix ? missingPrefix + port5 : void 0;
|
|
233382
|
-
const
|
|
233621
|
+
const renamed = renamedPort(node.type, port5, plug);
|
|
233622
|
+
const suggestion = renamed?.to ?? catalog.suggestPort(node.type, plug, port5, wireKind) ?? prefixed;
|
|
233383
233623
|
const alternatives = availableAlternatives(catalog, node.type, plug, wireKind);
|
|
233384
233624
|
out.push({
|
|
233385
233625
|
code: "nonexistent-port" /* NonexistentPort */,
|
|
233386
233626
|
severity: "error",
|
|
233387
|
-
message: `${node.type} has no ${plug} named "${port5}".` + (prefixed ? ` Its other ${plug}s exist only as "${missingPrefix}<name>", so a name without the prefix is never made and nothing goes through this wire. If "${port5}" is ${cloudPortMeaning(node.type, plug)}, wire "${prefixed}".` : ""),
|
|
233627
|
+
message: `${node.type} has no ${plug} named "${port5}".` + (renamed?.sentence ?? "") + (prefixed ? ` Its other ${plug}s exist only as "${missingPrefix}<name>", so a name without the prefix is never made and nothing goes through this wire. If "${port5}" is ${cloudPortMeaning(node.type, plug)}, wire "${prefixed}".` : ""),
|
|
233388
233628
|
location: {
|
|
233389
233629
|
component: component.name,
|
|
233390
233630
|
nodeId: node.id,
|
|
@@ -233587,6 +233827,7 @@ var detachedPageContent = {
|
|
|
233587
233827
|
};
|
|
233588
233828
|
|
|
233589
233829
|
// ../noodl-editor/src/editor/src/validation/rules/invalidHierarchy.ts
|
|
233830
|
+
var RECORDED_VISUAL_ROOT = { allowAsChild: true, category: "Visual" };
|
|
233590
233831
|
function hierarchyTyper(ctx) {
|
|
233591
233832
|
const byName = /* @__PURE__ */ new Map();
|
|
233592
233833
|
for (const c of ctx.project.components) {
|
|
@@ -233606,8 +233847,9 @@ function hierarchyTyper(ctx) {
|
|
|
233606
233847
|
const childIds = new Set(component.nodes.flatMap((n) => n.children));
|
|
233607
233848
|
const roots = component.nodes.filter((n) => n.parent === void 0 && !childIds.has(n.id));
|
|
233608
233849
|
const known = (n) => typeOf2(n.type);
|
|
233850
|
+
const rootType = (n) => known(n) ?? (component.visualRoots?.includes(n.id) ? RECORDED_VISUAL_ROOT : void 0);
|
|
233609
233851
|
const derived = componentHierarchyType(
|
|
233610
|
-
roots.map(
|
|
233852
|
+
roots.map(rootType).filter((t) => !!t),
|
|
233611
233853
|
component.nodes.map(known).filter((t) => !!t)
|
|
233612
233854
|
);
|
|
233613
233855
|
inFlight.delete(typeName4);
|
|
@@ -234478,6 +234720,123 @@ var settingsDiagnostic = {
|
|
|
234478
234720
|
}
|
|
234479
234721
|
};
|
|
234480
234722
|
|
|
234723
|
+
// ../noodl-editor/src/editor/src/validation/rules/functionScriptNotCompiled.ts
|
|
234724
|
+
var CODE_PREFIX3 = "const Script = (typeof Node !== 'undefined')?Node:undefined;\n";
|
|
234725
|
+
var PARAMS2 = ["Inputs", "Outputs", "Noodl", "Component", "Scripts"];
|
|
234726
|
+
function functionScriptCompileError(script2) {
|
|
234727
|
+
try {
|
|
234728
|
+
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
234729
|
+
}).constructor;
|
|
234730
|
+
new AsyncFunction(...PARAMS2, CODE_PREFIX3 + script2);
|
|
234731
|
+
return void 0;
|
|
234732
|
+
} catch (e) {
|
|
234733
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
234734
|
+
const line = syntaxErrorLine(script2);
|
|
234735
|
+
return line === void 0 ? { message } : { message, line };
|
|
234736
|
+
}
|
|
234737
|
+
}
|
|
234738
|
+
function syntaxErrorLine(script2) {
|
|
234739
|
+
const file = "nodegx-function-script";
|
|
234740
|
+
try {
|
|
234741
|
+
const vm = typeof require === "function" ? require("vm") : void 0;
|
|
234742
|
+
if (!vm?.Script) return void 0;
|
|
234743
|
+
new vm.Script(`(async function (${PARAMS2.join(", ")}) {
|
|
234744
|
+
${script2}
|
|
234745
|
+
})`, { filename: file });
|
|
234746
|
+
} catch (e) {
|
|
234747
|
+
const stack = e && typeof e === "object" ? e.stack : void 0;
|
|
234748
|
+
const match = typeof stack === "string" ? new RegExp(`${file}:(\\d+)`).exec(stack) : null;
|
|
234749
|
+
if (match) return Math.max(1, Number(match[1]) - 1);
|
|
234750
|
+
}
|
|
234751
|
+
return void 0;
|
|
234752
|
+
}
|
|
234753
|
+
var functionScriptNotCompiled = {
|
|
234754
|
+
code: "function-script-not-compiled" /* FunctionScriptNotCompiled */,
|
|
234755
|
+
description: "A Function's script does not compile, so the Function can never run.",
|
|
234756
|
+
defaultEnabled: true,
|
|
234757
|
+
run(ctx) {
|
|
234758
|
+
const out = [];
|
|
234759
|
+
for (const { component } of ctx.components) {
|
|
234760
|
+
for (const node of component.nodes) {
|
|
234761
|
+
if (node.type !== FUNCTION_NODE_TYPE) continue;
|
|
234762
|
+
const script2 = node.parameters?.functionScript;
|
|
234763
|
+
if (typeof script2 !== "string" || script2.trim() === "") continue;
|
|
234764
|
+
const error = functionScriptCompileError(script2);
|
|
234765
|
+
if (!error) continue;
|
|
234766
|
+
const name2 = node.label ? `"${node.label}"` : "This Function";
|
|
234767
|
+
out.push({
|
|
234768
|
+
code: "function-script-not-compiled" /* FunctionScriptNotCompiled */,
|
|
234769
|
+
severity: "warning",
|
|
234770
|
+
message: `${name2}'s script does not compile${error.line !== void 0 ? ` (line ${error.line})` : ""}: ${error.message}. It can never run: every Run fails with function/script-not-compiled. Fix the script; a regular expression or URL written inside a template literal loses its backslashes, so \`\\/\\/\` arrives as \`//\`, which starts a comment.`,
|
|
234771
|
+
location: {
|
|
234772
|
+
component: component.name,
|
|
234773
|
+
nodeId: node.id,
|
|
234774
|
+
nodeType: node.type,
|
|
234775
|
+
nodeLabel: node.label,
|
|
234776
|
+
port: "functionScript"
|
|
234777
|
+
}
|
|
234778
|
+
});
|
|
234779
|
+
}
|
|
234780
|
+
}
|
|
234781
|
+
return out;
|
|
234782
|
+
}
|
|
234783
|
+
};
|
|
234784
|
+
|
|
234785
|
+
// ../noodl-editor/src/editor/src/validation/rules/functionNeverReruns.ts
|
|
234786
|
+
var RUN_ON_CHANGE_PREFIX3 = "runOnChange-";
|
|
234787
|
+
var functionNeverReruns = {
|
|
234788
|
+
code: "function-never-reruns" /* FunctionNeverReruns */,
|
|
234789
|
+
description: "Nothing re-runs a node when its inputs change: its control signal is unwired and every wired input is set not to run on change.",
|
|
234790
|
+
defaultEnabled: true,
|
|
234791
|
+
run(ctx) {
|
|
234792
|
+
const out = [];
|
|
234793
|
+
const { catalog } = ctx;
|
|
234794
|
+
for (const { component } of ctx.components) {
|
|
234795
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
234796
|
+
for (const c of component.connections) {
|
|
234797
|
+
const list2 = incoming.get(c.toId);
|
|
234798
|
+
if (list2) list2.push(c);
|
|
234799
|
+
else incoming.set(c.toId, [c]);
|
|
234800
|
+
}
|
|
234801
|
+
for (const node of component.nodes) {
|
|
234802
|
+
const isFunction2 = node.type === FUNCTION_NODE_TYPE;
|
|
234803
|
+
if (!isFunction2 && !catalog.isRunOnValueChangeFamily(node.type)) continue;
|
|
234804
|
+
const inbound = incoming.get(node.id) ?? [];
|
|
234805
|
+
if (inbound.length === 0) continue;
|
|
234806
|
+
if (inbound.some((c) => catalog.getPort(node.type, "input", c.toProperty)?.isSignal)) continue;
|
|
234807
|
+
const governed = [
|
|
234808
|
+
...new Set(
|
|
234809
|
+
inbound.map((c) => c.toProperty).filter(
|
|
234810
|
+
(port5) => isFunction2 ? port5.startsWith("in-") : !!catalog.getPort(node.type, "input", RUN_ON_CHANGE_PREFIX3 + port5)
|
|
234811
|
+
)
|
|
234812
|
+
)
|
|
234813
|
+
];
|
|
234814
|
+
if (governed.length === 0) continue;
|
|
234815
|
+
const params = node.parameters ?? {};
|
|
234816
|
+
if (!governed.every((port5) => params[RUN_ON_CHANGE_PREFIX3 + port5] === false)) continue;
|
|
234817
|
+
const name2 = node.label ? `"${node.label}"` : isFunction2 ? "This Function" : "This node";
|
|
234818
|
+
const shown = governed.map((p) => `"${isFunction2 ? p.slice("in-".length) : p}"`).join(", ");
|
|
234819
|
+
out.push({
|
|
234820
|
+
code: "function-never-reruns" /* FunctionNeverReruns */,
|
|
234821
|
+
severity: "warning",
|
|
234822
|
+
message: `Nothing re-runs ${name2} when its inputs change: Run is not wired, and every wired input (${shown}) has Run On Value Change unticked. ` + (isFunction2 ? "It runs once when it loads, and a new value on any of them never runs it again. " : "") + "Wire Run to what should start it, or tick Run On Value Change on the input whose new value should run it.",
|
|
234823
|
+
location: {
|
|
234824
|
+
component: component.name,
|
|
234825
|
+
nodeId: node.id,
|
|
234826
|
+
nodeType: node.type,
|
|
234827
|
+
nodeLabel: node.label,
|
|
234828
|
+
port: RUN_ON_CHANGE_PREFIX3 + governed[0],
|
|
234829
|
+
plug: "input"
|
|
234830
|
+
},
|
|
234831
|
+
suggestion: `${RUN_ON_CHANGE_PREFIX3}${governed[0]}: true`,
|
|
234832
|
+
alternatives: governed.map((p) => `${RUN_ON_CHANGE_PREFIX3}${p}: true`)
|
|
234833
|
+
});
|
|
234834
|
+
}
|
|
234835
|
+
}
|
|
234836
|
+
return out;
|
|
234837
|
+
}
|
|
234838
|
+
};
|
|
234839
|
+
|
|
234481
234840
|
// ../noodl-editor/src/editor/src/validation/rules/index.ts
|
|
234482
234841
|
var ALL_RULES = [
|
|
234483
234842
|
duplicateNodeId,
|
|
@@ -234529,6 +234888,11 @@ var ALL_RULES = [
|
|
|
234529
234888
|
// the first fires on `prep` and never on the `RunTasks` carrying the defect,
|
|
234530
234889
|
// so this covers a hole in it rather than repeating it.
|
|
234531
234890
|
completedCommitsUnchecked,
|
|
234891
|
+
// P121 bugs SCRIPTCOMPILE / NEVERRUNS — a Function that can never run, and one that never
|
|
234892
|
+
// re-runs: breakage of the graph's behaviour, so beside the outcome rules and before the
|
|
234893
|
+
// arrangement ones.
|
|
234894
|
+
functionScriptNotCompiled,
|
|
234895
|
+
functionNeverReruns,
|
|
234532
234896
|
repeatedSiblingSubtree,
|
|
234533
234897
|
// DEF-025 — beside `repeatedSiblingSubtree` because both read the visual
|
|
234534
234898
|
// tree's arrangement rather than wires: that one asks whether siblings
|
|
@@ -235223,6 +235587,35 @@ function describeWireHealth(reading) {
|
|
|
235223
235587
|
return lines;
|
|
235224
235588
|
}
|
|
235225
235589
|
|
|
235590
|
+
// src/skeleton.ts
|
|
235591
|
+
var SKELETON_PLACEHOLDER_MARKER = " \u2014 nothing built yet. Review the plan in docs/ and start when you are ready.";
|
|
235592
|
+
function untouchedSkeletonPages(project) {
|
|
235593
|
+
const pages = [];
|
|
235594
|
+
for (const component of project.getComponents()) {
|
|
235595
|
+
const nodes = [];
|
|
235596
|
+
component.graph.forEachNode((node) => {
|
|
235597
|
+
nodes.push({ typename: node.typename, parameters: node.parameters ?? {} });
|
|
235598
|
+
});
|
|
235599
|
+
if (!nodes.some((n) => n.typename === "Page")) continue;
|
|
235600
|
+
if (!isUntouchedSkeletonPage(nodes)) return [];
|
|
235601
|
+
pages.push(component.name.replace(/^\/(#__page__\/)?/, ""));
|
|
235602
|
+
}
|
|
235603
|
+
return pages;
|
|
235604
|
+
}
|
|
235605
|
+
function isUntouchedSkeletonPage(nodes) {
|
|
235606
|
+
if (nodes.length !== 2) return false;
|
|
235607
|
+
const page = nodes.find((n) => n.typename === "Page");
|
|
235608
|
+
const text3 = nodes.find((n) => n.typename === "Text");
|
|
235609
|
+
if (!page || !text3) return false;
|
|
235610
|
+
const value2 = text3.parameters["text"];
|
|
235611
|
+
return typeof value2 === "string" && value2.endsWith(SKELETON_PLACEHOLDER_MARKER);
|
|
235612
|
+
}
|
|
235613
|
+
function skeletonWarning(pages) {
|
|
235614
|
+
if (!pages.length) return void 0;
|
|
235615
|
+
const named = pages.length === 1 ? `${pages[0]} is` : `${pages.join(", ")} are`;
|
|
235616
|
+
return `Nothing is built in this project yet: ${named} still the placeholder create_project wrote, whose sentence is addressed to the agent, and it is what every visitor will see. It was published as it is; build the app, then deploy again.`;
|
|
235617
|
+
}
|
|
235618
|
+
|
|
235226
235619
|
// src/deploy.ts
|
|
235227
235620
|
var DEPLOY_INDEX = path9.join(EXTERNAL_DIR, "deploy", "index.json");
|
|
235228
235621
|
function assertDeployRuntime() {
|
|
@@ -235294,6 +235687,11 @@ async function deployProject(options) {
|
|
|
235294
235687
|
} finally {
|
|
235295
235688
|
if (stage) fs9.rmSync(stage.stage, { recursive: true, force: true });
|
|
235296
235689
|
}
|
|
235690
|
+
const cloudservices = project.getMetaData("cloudservices");
|
|
235691
|
+
const loopback = fullStack ? void 0 : loopbackBackendWarning({ cloudservices });
|
|
235692
|
+
const localBackend = loopback ? { endpoint: String(cloudservices?.endpoint), warning: loopback } : void 0;
|
|
235693
|
+
const skeletonPages = untouchedSkeletonPages(project);
|
|
235694
|
+
const skeleton = skeletonWarning(skeletonPages);
|
|
235297
235695
|
const roots = readDeployedRoots(fullStack ? path9.join(options.outDir, "app") : options.outDir);
|
|
235298
235696
|
return {
|
|
235299
235697
|
outDir: options.outDir,
|
|
@@ -235312,10 +235710,16 @@ async function deployProject(options) {
|
|
|
235312
235710
|
blank: gradeRoots(roots),
|
|
235313
235711
|
engine,
|
|
235314
235712
|
wires,
|
|
235713
|
+
...localBackend ? { localBackend: localBackend.endpoint } : {},
|
|
235714
|
+
...skeleton ? { skeletonPages } : {},
|
|
235315
235715
|
// EXP-017 AC4 — a duplicate the copy step could not leave out reaches the person through the
|
|
235316
235716
|
// same channel every other deploy warning does, rather than only the editor's console.
|
|
235317
235717
|
warnings: [
|
|
235318
235718
|
...warnings,
|
|
235719
|
+
// P112-OWN023-DEPLOYLOOPBACK — a static site cannot reach a backend on the builder's machine. A full-stack
|
|
235720
|
+
// folder carries its backend, and the backend serves the app with its own address in place of this one.
|
|
235721
|
+
...localBackend ? [localBackend.warning] : [],
|
|
235722
|
+
...skeleton ? [skeleton] : [],
|
|
235319
235723
|
...describeWireHealth(wires),
|
|
235320
235724
|
...copyReport.duplicatesKept.map(
|
|
235321
235725
|
(kept) => `${kept.path} (${kept.bytes} B) is the same file as ${kept.keep} and both were published \u2014 ${kept.reason}. Point that reference at ${kept.keep} to stop shipping it twice.`
|
|
@@ -235380,9 +235784,24 @@ async function main() {
|
|
|
235380
235784
|
result2 = { ok: true, ...outcome };
|
|
235381
235785
|
} catch (error) {
|
|
235382
235786
|
const message = error instanceof Error ? error.message : String(error);
|
|
235383
|
-
result2 = error instanceof DevelopmentEngineError ? { ok: false, stage: "engine", message, engine: error.engine } : error instanceof FullStackTargetError ? { ok: false, stage: "target", message } : error instanceof FullStackInstallError ? { ok: false, stage: "runtime", message } :
|
|
235787
|
+
result2 = error instanceof DevelopmentEngineError ? { ok: false, stage: "engine", message, engine: error.engine } : error instanceof FullStackTargetError ? { ok: false, stage: "target", message } : error instanceof FullStackInstallError ? { ok: false, stage: "runtime", message } : (
|
|
235788
|
+
// P112-S13-DEPLOYBADJSON: a project file that is not JSON was filed under `write`.
|
|
235789
|
+
isProjectFileError(error) ? { ok: false, stage: "project", message } : { ok: false, stage: classify(message), message }
|
|
235790
|
+
);
|
|
235384
235791
|
}
|
|
235385
235792
|
realWrite(JSON.stringify(result2) + "\n");
|
|
235793
|
+
if (result2.ok && result2.localBackend) {
|
|
235794
|
+
const sentence = result2.warnings.find((w) => w.includes(result2.localBackend));
|
|
235795
|
+
if (sentence) process.stderr.write(`
|
|
235796
|
+
nodegx-deploy: ${sentence}
|
|
235797
|
+
`);
|
|
235798
|
+
}
|
|
235799
|
+
if (result2.ok && result2.skeletonPages) {
|
|
235800
|
+
const sentence = result2.warnings.find((w) => w.startsWith("Nothing is built in this project yet"));
|
|
235801
|
+
if (sentence) process.stderr.write(`
|
|
235802
|
+
nodegx-deploy: ${sentence}
|
|
235803
|
+
`);
|
|
235804
|
+
}
|
|
235386
235805
|
process.exitCode = 0;
|
|
235387
235806
|
}
|
|
235388
235807
|
main().catch((error) => {
|