@bonsae/nrg-runtime 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +471 -585
- package/package.json +1 -1
- package/resources/nrg.5f37e63a.js +20 -0
- package/resources/nrg.229bba2d.js +0 -13
package/index.cjs
CHANGED
|
@@ -36,8 +36,6 @@ __export(runtime_exports, {
|
|
|
36
36
|
Node: () => Node,
|
|
37
37
|
NrgError: () => NrgError,
|
|
38
38
|
SchemaType: () => SchemaType,
|
|
39
|
-
defineConfigNode: () => defineConfigNode,
|
|
40
|
-
defineIONode: () => defineIONode,
|
|
41
39
|
defineModule: () => defineModule,
|
|
42
40
|
defineSchema: () => defineSchema,
|
|
43
41
|
registerType: () => registerType,
|
|
@@ -46,9 +44,11 @@ __export(runtime_exports, {
|
|
|
46
44
|
module.exports = __toCommonJS(runtime_exports);
|
|
47
45
|
|
|
48
46
|
// src/sdk/lib/server/nodes/symbols.ts
|
|
49
|
-
var
|
|
47
|
+
var NRG_SETUP_CLOSE_HANDLER = Symbol.for("nrg.setupCloseHandler");
|
|
48
|
+
var NRG_SETUP_INPUT_HANDLER = Symbol.for("nrg.setupInputHandler");
|
|
50
49
|
var NRG_NODE = Symbol.for("nrg.node");
|
|
51
50
|
var NRG_CONFIG_NODE = Symbol.for("nrg.configNode");
|
|
51
|
+
var NRG_PORTS = Symbol.for("nrg.ports");
|
|
52
52
|
|
|
53
53
|
// src/sdk/lib/shared/errors.ts
|
|
54
54
|
var NrgError = class _NrgError extends Error {
|
|
@@ -65,7 +65,7 @@ function defineModule(definition) {
|
|
|
65
65
|
if (!NodeClass[NRG_NODE]) {
|
|
66
66
|
const name = NodeClass?.name || String(NodeClass);
|
|
67
67
|
throw new NrgError(
|
|
68
|
-
`defineModule: "${name}" is not an nrg node class \u2014 extend IONode/ConfigNode
|
|
68
|
+
`defineModule: "${name}" is not an nrg node class \u2014 extend IONode/ConfigNode.`
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -84,11 +84,6 @@ var Validator = class {
|
|
|
84
84
|
// Non-mutating instance (no coercion, no defaults): a pure predicate. Used for
|
|
85
85
|
// message/form validation so a validated msg/form is never silently rewritten.
|
|
86
86
|
pureAjv;
|
|
87
|
-
// `$id` → the schema object (and owner label) that first claimed it, recorded
|
|
88
|
-
// by reserveSchemaId() at node registration. AJV caches compiled validators by
|
|
89
|
-
// `$id`, so two *different* schemas sharing one `$id` is a silent bug — this
|
|
90
|
-
// registry makes the collision fail loudly.
|
|
91
|
-
registeredSchemaIds = /* @__PURE__ */ new Map();
|
|
92
87
|
constructor(options) {
|
|
93
88
|
const { customKeywords, customFormats, ...ajvOptions } = options || {};
|
|
94
89
|
this.ajv = this.buildAjv(ajvOptions, true, customKeywords, customFormats);
|
|
@@ -129,7 +124,6 @@ var Validator = class {
|
|
|
129
124
|
* Add custom keywords to the given ajv instance
|
|
130
125
|
*/
|
|
131
126
|
addCustomKeywords(ajv, keywords) {
|
|
132
|
-
if (!keywords) return;
|
|
133
127
|
keywords.forEach((keyword) => {
|
|
134
128
|
ajv.addKeyword(keyword);
|
|
135
129
|
});
|
|
@@ -138,7 +132,6 @@ var Validator = class {
|
|
|
138
132
|
* Add custom formats to the given ajv instance
|
|
139
133
|
*/
|
|
140
134
|
addCustomFormats(ajv, formats) {
|
|
141
|
-
if (!formats) return;
|
|
142
135
|
Object.entries(formats).forEach(([name, validator]) => {
|
|
143
136
|
if (validator instanceof RegExp) {
|
|
144
137
|
ajv.addFormat(name, validator);
|
|
@@ -147,13 +140,22 @@ var Validator = class {
|
|
|
147
140
|
}
|
|
148
141
|
});
|
|
149
142
|
}
|
|
143
|
+
// Object-keyed compile cache for `$id`-less schemas. AJV caches by `$id`; a
|
|
144
|
+
// schema without one (an ad-hoc `SchemaType.Object`, or a flow-author override
|
|
145
|
+
// a node memoizes and revalidates every message) would otherwise recompile —
|
|
146
|
+
// and accumulate — on every `compile()`. Keyed by the schema OBJECT so a reused
|
|
147
|
+
// instance compiles once; a WeakMap so the entry is collected with the schema.
|
|
148
|
+
// One per AJV (coercing vs pure) since each keeps its own compiled forms.
|
|
149
|
+
pureInlineCache = /* @__PURE__ */ new WeakMap();
|
|
150
|
+
mutateInlineCache = /* @__PURE__ */ new WeakMap();
|
|
150
151
|
/**
|
|
151
|
-
* Compile (and cache) a validator for a schema
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
152
|
+
* Compile (and cache) a validator for a schema. Schemas WITH an `$id` use AJV's
|
|
153
|
+
* own registry (`defineSchema` gives every schema a unique `$id`, so `getSchema`
|
|
154
|
+
* returns the previously compiled validator). Schemas WITHOUT one — a flow-author
|
|
155
|
+
* override, or an ad-hoc `SchemaType.Object` — are cached by object reference
|
|
156
|
+
* here, so a caller that reuses the same schema instance compiles it only once
|
|
157
|
+
* (no unbounded recompile/leak). We never write `$id` (no mutation of the
|
|
158
|
+
* caller's schema).
|
|
157
159
|
*
|
|
158
160
|
* @param schema - JSON Schema to validate against
|
|
159
161
|
* @param mutate - `true` (default) uses the coercing instance; `false` uses the
|
|
@@ -162,33 +164,16 @@ var Validator = class {
|
|
|
162
164
|
createValidator(schema, mutate = true) {
|
|
163
165
|
const ajv = mutate ? this.ajv : this.pureAjv;
|
|
164
166
|
if (schema.$id) {
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
+
const cached2 = ajv.getSchema(schema.$id);
|
|
168
|
+
if (cached2) return cached2;
|
|
169
|
+
return ajv.compile(schema);
|
|
167
170
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
* against the first. Reserving the same schema object again (a re-deploy, or
|
|
175
|
-
* one schema reused across roles) is idempotent. A schema without `$id` is a
|
|
176
|
-
* no-op here. Convention: `"<node-type>:<role>"`.
|
|
177
|
-
*
|
|
178
|
-
* @param schema - the schema being registered (its `$id` is the key)
|
|
179
|
-
* @param owner - human-readable origin (e.g. `"my-node.config"`) for the error
|
|
180
|
-
* @throws NrgError when `$id` is already held by a different schema object.
|
|
181
|
-
*/
|
|
182
|
-
reserveSchemaId(schema, owner) {
|
|
183
|
-
const id = schema.$id;
|
|
184
|
-
if (!id) return;
|
|
185
|
-
const existing = this.registeredSchemaIds.get(id);
|
|
186
|
-
if (existing && existing.schema !== schema) {
|
|
187
|
-
throw new NrgError(
|
|
188
|
-
`Duplicate schema $id "${id}": declared by both ${existing.owner} and ${owner}. $id is the AJV compile-cache key and must be unique \u2014 a collision makes one schema validate against another. Convention: "<node-type>:<role>".`
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
this.registeredSchemaIds.set(id, { schema, owner });
|
|
171
|
+
const cache = mutate ? this.mutateInlineCache : this.pureInlineCache;
|
|
172
|
+
const cached = cache.get(schema);
|
|
173
|
+
if (cached) return cached;
|
|
174
|
+
const compiled = ajv.compile(schema);
|
|
175
|
+
cache.set(schema, compiled);
|
|
176
|
+
return compiled;
|
|
192
177
|
}
|
|
193
178
|
/**
|
|
194
179
|
* Validate data against a schema and return a structured result
|
|
@@ -304,25 +289,25 @@ function initValidator(RED) {
|
|
|
304
289
|
}
|
|
305
290
|
|
|
306
291
|
// src/sdk/lib/server/api/assets.ts
|
|
307
|
-
var
|
|
308
|
-
var
|
|
309
|
-
var
|
|
310
|
-
var CLIENT_ASSET = true ? "nrg.
|
|
292
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
293
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
294
|
+
var import_node_module = require("node:module");
|
|
295
|
+
var CLIENT_ASSET = true ? "nrg.5f37e63a.js" : "nrg.js";
|
|
311
296
|
function serveFile(filePath) {
|
|
312
297
|
return (_req, res, next) => {
|
|
313
|
-
if (!
|
|
298
|
+
if (!import_node_fs.default.existsSync(filePath)) return next();
|
|
314
299
|
res.setHeader("Content-Type", "application/javascript");
|
|
315
|
-
|
|
300
|
+
import_node_fs.default.createReadStream(filePath).pipe(res);
|
|
316
301
|
};
|
|
317
302
|
}
|
|
318
303
|
function initAssetsRoutes(router) {
|
|
319
|
-
const resourcesDir =
|
|
320
|
-
if (!
|
|
321
|
-
const _require = (0,
|
|
304
|
+
const resourcesDir = import_node_path.default.resolve(__dirname, "./resources");
|
|
305
|
+
if (!import_node_fs.default.existsSync(resourcesDir)) return;
|
|
306
|
+
const _require = (0, import_node_module.createRequire)(import_node_path.default.join(__dirname, "package.json"));
|
|
322
307
|
const vueFile = process.env.NODE_ENV !== "production" ? _require.resolve("vue/dist/vue.esm-browser.js") : _require.resolve("vue/dist/vue.esm-browser.prod.js");
|
|
323
308
|
router.get(
|
|
324
309
|
`/nrg/assets/${CLIENT_ASSET}`,
|
|
325
|
-
serveFile(
|
|
310
|
+
serveFile(import_node_path.default.join(resourcesDir, CLIENT_ASSET))
|
|
326
311
|
);
|
|
327
312
|
router.get("/nrg/assets/vue.esm-browser.prod.js", serveFile(vueFile));
|
|
328
313
|
}
|
|
@@ -335,206 +320,41 @@ function initRoutes(RED) {
|
|
|
335
320
|
initAssetsRoutes(RED.httpAdmin);
|
|
336
321
|
}
|
|
337
322
|
|
|
338
|
-
// src/sdk/lib/shared/schemas/
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
"json",
|
|
350
|
-
"bin",
|
|
351
|
-
"re",
|
|
352
|
-
"jsonata",
|
|
353
|
-
"date",
|
|
354
|
-
"env",
|
|
355
|
-
"node",
|
|
356
|
-
"cred"
|
|
357
|
-
];
|
|
358
|
-
|
|
359
|
-
// src/sdk/lib/shared/schemas/base.ts
|
|
360
|
-
var import_typebox = require("@sinclair/typebox");
|
|
361
|
-
var TypedInputSchema = import_typebox.Type.Object(
|
|
362
|
-
{
|
|
363
|
-
value: import_typebox.Type.Union(
|
|
364
|
-
[
|
|
365
|
-
import_typebox.Type.String(),
|
|
366
|
-
import_typebox.Type.Number(),
|
|
367
|
-
import_typebox.Type.Boolean(),
|
|
368
|
-
import_typebox.Type.Null()
|
|
369
|
-
],
|
|
370
|
-
{
|
|
371
|
-
description: "The actual value entered or selected.",
|
|
372
|
-
default: ""
|
|
373
|
-
}
|
|
374
|
-
),
|
|
375
|
-
type: import_typebox.Type.Union(
|
|
376
|
-
TYPED_INPUT_TYPES.map((type) => import_typebox.Type.Literal(type)),
|
|
377
|
-
{
|
|
378
|
-
description: "The type of the value (string, number, message property, etc.)",
|
|
379
|
-
default: "str"
|
|
380
|
-
}
|
|
381
|
-
)
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
description: "Represents a Node-RED TypedInput value and its type.",
|
|
385
|
-
default: {
|
|
386
|
-
type: "str",
|
|
387
|
-
value: ""
|
|
323
|
+
// src/sdk/lib/shared/schemas/utils.ts
|
|
324
|
+
function getCredentialsFromSchema(schema) {
|
|
325
|
+
if (!schema?.properties) return void 0;
|
|
326
|
+
const result = {};
|
|
327
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
328
|
+
const property = value;
|
|
329
|
+
const isPassword = property.format === "password";
|
|
330
|
+
if (!isPassword) {
|
|
331
|
+
console.warn(
|
|
332
|
+
`[nrg] credential "${key}" has no format:"password" \u2014 it is stored as a visible (cleartext-in-editor) credential. Add { format: "password" } to mask it.`
|
|
333
|
+
);
|
|
388
334
|
}
|
|
335
|
+
result[key] = {
|
|
336
|
+
// NOTE: required is always false because it is controlled by the JSON Schema and AJV validation instead of using node-red client core
|
|
337
|
+
required: false,
|
|
338
|
+
type: isPassword ? "password" : "text",
|
|
339
|
+
value: property.default ?? void 0
|
|
340
|
+
};
|
|
389
341
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
// src/sdk/lib/shared/schemas/factories.ts
|
|
393
|
-
var JSON_TYPES = /* @__PURE__ */ new Set([
|
|
394
|
-
"null",
|
|
395
|
-
"boolean",
|
|
396
|
-
"object",
|
|
397
|
-
"array",
|
|
398
|
-
"number",
|
|
399
|
-
"integer",
|
|
400
|
-
"string"
|
|
401
|
-
]);
|
|
402
|
-
function isJSONType(value) {
|
|
403
|
-
return typeof value === "string" && JSON_TYPES.has(value);
|
|
404
|
-
}
|
|
405
|
-
function NodeRef(type, options) {
|
|
406
|
-
return {
|
|
407
|
-
...import_typebox2.Type.String({
|
|
408
|
-
description: options?.description || `Reference to ${type}`
|
|
409
|
-
}),
|
|
410
|
-
// `...options` first so user options (description, x-nrg-form, …) apply, but
|
|
411
|
-
// the framework keys below win — a caller can't clobber `format`/
|
|
412
|
-
// `x-nrg-node-type`/`Kind` and silently break NodeRef resolution.
|
|
413
|
-
...options,
|
|
414
|
-
format: "node-id",
|
|
415
|
-
"x-nrg-node-type": type,
|
|
416
|
-
[import_typebox2.Kind]: "NodeRef"
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
function TypedInput(options) {
|
|
420
|
-
const opts = options ?? {};
|
|
421
|
-
const restrictedTypes = opts["x-nrg-form"]?.typedInputTypes;
|
|
422
|
-
if (restrictedTypes && restrictedTypes.length === 0) {
|
|
423
|
-
throw new Error(
|
|
424
|
-
"SchemaType.TypedInput: typedInputTypes must list at least one type."
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
const explicitType = opts.default?.type;
|
|
428
|
-
if (restrictedTypes && explicitType !== void 0 && !restrictedTypes.includes(explicitType)) {
|
|
429
|
-
throw new Error(
|
|
430
|
-
`SchemaType.TypedInput: default type "${explicitType}" is not one of its typedInputTypes [${restrictedTypes.join(", ")}].`
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
const defaultType = explicitType ?? (restrictedTypes ? restrictedTypes.includes("str") ? "str" : restrictedTypes[0] : "str");
|
|
434
|
-
return {
|
|
435
|
-
...TypedInputSchema,
|
|
436
|
-
// `...options` first; the framework keys below win (see NodeRef).
|
|
437
|
-
...options,
|
|
438
|
-
default: {
|
|
439
|
-
...TypedInputSchema.default,
|
|
440
|
-
...opts.default,
|
|
441
|
-
type: defaultType
|
|
442
|
-
},
|
|
443
|
-
"x-nrg-typed-input": true,
|
|
444
|
-
[import_typebox2.Kind]: "TypedInput"
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
function NrgString(options) {
|
|
448
|
-
return import_typebox2.Type.String(options);
|
|
449
|
-
}
|
|
450
|
-
function OutputReturnProperties(options) {
|
|
451
|
-
return import_typebox2.Type.Record(
|
|
452
|
-
import_typebox2.Type.Number(),
|
|
453
|
-
import_typebox2.Type.String({ pattern: "^[A-Za-z_$][A-Za-z0-9_$]*$" }),
|
|
454
|
-
{
|
|
455
|
-
description: "Per-port return property, keyed by output port index. A missing entry falls back to `output`.",
|
|
456
|
-
default: {},
|
|
457
|
-
...options
|
|
458
|
-
}
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
function OutputContextModes(options) {
|
|
462
|
-
return import_typebox2.Type.Record(
|
|
463
|
-
import_typebox2.Type.Number(),
|
|
464
|
-
import_typebox2.Type.Union([
|
|
465
|
-
import_typebox2.Type.Literal("carry"),
|
|
466
|
-
import_typebox2.Type.Literal("trace"),
|
|
467
|
-
import_typebox2.Type.Literal("reset")
|
|
468
|
-
]),
|
|
469
|
-
{
|
|
470
|
-
description: "Per-port context mode, keyed by output port index. A missing entry falls back to `carry`.",
|
|
471
|
-
default: {},
|
|
472
|
-
...options
|
|
473
|
-
}
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
function NrgUnsafe(options) {
|
|
477
|
-
return import_typebox2.Type.Unsafe(options);
|
|
342
|
+
return result;
|
|
478
343
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
import_typebox2.Type,
|
|
490
|
-
NRG_SCHEMA_TYPES_FACTORIES
|
|
491
|
-
);
|
|
492
|
-
function markNonValidatable(schema) {
|
|
493
|
-
const type = schema.type;
|
|
494
|
-
const hasInvalidType = type !== void 0 && (Array.isArray(type) ? !type.every(isJSONType) : !isJSONType(type));
|
|
495
|
-
if (hasInvalidType) {
|
|
496
|
-
schema["x-nrg-skip-validation"] = true;
|
|
497
|
-
if (schema.default !== void 0) {
|
|
498
|
-
schema._default = schema.default;
|
|
499
|
-
delete schema.default;
|
|
500
|
-
}
|
|
501
|
-
delete schema.type;
|
|
502
|
-
}
|
|
503
|
-
if (schema.properties) {
|
|
504
|
-
for (const prop of Object.values(schema.properties)) {
|
|
505
|
-
markNonValidatable(prop);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (schema.patternProperties) {
|
|
509
|
-
for (const prop of Object.values(schema.patternProperties)) {
|
|
510
|
-
markNonValidatable(prop);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
514
|
-
markNonValidatable(schema.additionalProperties);
|
|
515
|
-
}
|
|
516
|
-
if (schema.items) {
|
|
517
|
-
markNonValidatable(schema.items);
|
|
518
|
-
}
|
|
519
|
-
if (schema.anyOf) {
|
|
520
|
-
schema.anyOf.forEach((s) => markNonValidatable(s));
|
|
521
|
-
}
|
|
522
|
-
if (schema.allOf) {
|
|
523
|
-
schema.allOf.forEach((s) => markNonValidatable(s));
|
|
524
|
-
}
|
|
525
|
-
if (schema.oneOf) {
|
|
526
|
-
schema.oneOf.forEach((s) => markNonValidatable(s));
|
|
344
|
+
function getSettingsFromSchema(schema, nodeType) {
|
|
345
|
+
const settings = {};
|
|
346
|
+
if (!schema?.properties) return settings;
|
|
347
|
+
const prefix = nodeType.replace(/-./g, (x) => x[1].toUpperCase());
|
|
348
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
349
|
+
const settingKey = prefix + key.charAt(0).toUpperCase() + key.slice(1);
|
|
350
|
+
settings[settingKey] = {
|
|
351
|
+
value: prop.default,
|
|
352
|
+
exportable: prop.exportable ?? false
|
|
353
|
+
};
|
|
527
354
|
}
|
|
528
|
-
return
|
|
529
|
-
}
|
|
530
|
-
function defineSchema(properties, options) {
|
|
531
|
-
const schema = SchemaType.Object(properties, options);
|
|
532
|
-
return markNonValidatable(schema);
|
|
355
|
+
return settings;
|
|
533
356
|
}
|
|
534
357
|
|
|
535
|
-
// src/sdk/lib/shared/schemas/index.ts
|
|
536
|
-
var import_typebox3 = require("@sinclair/typebox");
|
|
537
|
-
|
|
538
358
|
// src/sdk/lib/server/registration.ts
|
|
539
359
|
async function registerType(RED, NodeClass) {
|
|
540
360
|
RED.log.debug(`Registering Type: ${NodeClass.type}`);
|
|
@@ -546,49 +366,52 @@ async function registerType(RED, NodeClass) {
|
|
|
546
366
|
if (!NodeClass.type) {
|
|
547
367
|
throw new NrgError("type must be provided when registering the node");
|
|
548
368
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const found = [];
|
|
554
|
-
const add = (role, schema) => {
|
|
555
|
-
if (schema && typeof schema === "object") {
|
|
556
|
-
found.push({ role, schema });
|
|
557
|
-
}
|
|
558
|
-
};
|
|
559
|
-
add("config", NodeClass.configSchema);
|
|
560
|
-
add("credentials", NodeClass.credentialsSchema);
|
|
561
|
-
add("settings", NodeClass.settingsSchema);
|
|
562
|
-
add("input", NodeClass.inputSchema);
|
|
563
|
-
const outputs = NodeClass.outputsSchema;
|
|
564
|
-
if (Array.isArray(outputs)) {
|
|
565
|
-
outputs.forEach((schema, i) => add(`output[${i}]`, schema));
|
|
566
|
-
} else if (outputs && typeof outputs === "object" && !(import_typebox3.Kind in outputs)) {
|
|
567
|
-
for (const [port, schema] of Object.entries(outputs)) {
|
|
568
|
-
add(`output.${port}`, schema);
|
|
569
|
-
}
|
|
570
|
-
} else {
|
|
571
|
-
add("output", outputs);
|
|
369
|
+
if (NodeClass.color && !/^#[0-9A-Fa-f]{6}$/.test(NodeClass.color)) {
|
|
370
|
+
throw new NrgError(
|
|
371
|
+
`Invalid color "${NodeClass.color}" for ${NodeClass.type}: must be a 6-digit hex color like "#a6bbcf" (shorthand "#abc" is not accepted).`
|
|
372
|
+
);
|
|
572
373
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
function
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
374
|
+
RED.nodes.registerType(
|
|
375
|
+
NodeClass.type,
|
|
376
|
+
function(config) {
|
|
377
|
+
RED.nodes.createNode(this, config);
|
|
378
|
+
const node = new NodeClass(RED, this, config, this.credentials);
|
|
379
|
+
Object.defineProperty(this, "_node", {
|
|
380
|
+
value: node,
|
|
381
|
+
writable: false,
|
|
382
|
+
configurable: false,
|
|
383
|
+
enumerable: false
|
|
384
|
+
});
|
|
385
|
+
const createdPromise = Promise.resolve(node.created?.()).catch(
|
|
386
|
+
(error) => {
|
|
387
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
388
|
+
this.error("Error during created hook: " + message);
|
|
389
|
+
throw error;
|
|
586
390
|
}
|
|
587
|
-
|
|
391
|
+
);
|
|
392
|
+
createdPromise.catch(() => {
|
|
393
|
+
this.status({ fill: "red", shape: "ring", text: "created() failed" });
|
|
394
|
+
});
|
|
395
|
+
node[NRG_SETUP_CLOSE_HANDLER]();
|
|
396
|
+
if (NRG_SETUP_INPUT_HANDLER in node && typeof node[NRG_SETUP_INPUT_HANDLER] === "function") {
|
|
397
|
+
node[NRG_SETUP_INPUT_HANDLER](createdPromise);
|
|
588
398
|
}
|
|
589
|
-
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
credentials: NodeClass.credentialsSchema ? getCredentialsFromSchema(NodeClass.credentialsSchema) : void 0,
|
|
402
|
+
settings: NodeClass.settingsSchema ? getSettingsFromSchema(NodeClass.settingsSchema, NodeClass.type) : void 0
|
|
590
403
|
}
|
|
404
|
+
);
|
|
405
|
+
NodeClass.validateSettings(RED);
|
|
406
|
+
try {
|
|
407
|
+
await Promise.resolve(NodeClass.registered?.(RED));
|
|
408
|
+
} catch (error) {
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
RED.log.error(
|
|
411
|
+
`Error during registered hook for ${NodeClass.type}: ${message}`
|
|
412
|
+
);
|
|
591
413
|
}
|
|
414
|
+
RED.log.debug(`Type registered: ${NodeClass.type}`);
|
|
592
415
|
}
|
|
593
416
|
function registerTypes(nodes) {
|
|
594
417
|
const fn = Object.assign(
|
|
@@ -596,7 +419,6 @@ function registerTypes(nodes) {
|
|
|
596
419
|
initValidator(RED);
|
|
597
420
|
initRoutes(RED);
|
|
598
421
|
try {
|
|
599
|
-
checkSchemaIds(RED, nodes);
|
|
600
422
|
RED.log.info("Registering node types in series");
|
|
601
423
|
for (const NodeClass of nodes) {
|
|
602
424
|
await registerType(RED, NodeClass);
|
|
@@ -613,7 +435,7 @@ function registerTypes(nodes) {
|
|
|
613
435
|
}
|
|
614
436
|
|
|
615
437
|
// src/sdk/lib/server/typed-input.ts
|
|
616
|
-
var
|
|
438
|
+
var TypedInput = class {
|
|
617
439
|
constructor(RED, node, input) {
|
|
618
440
|
this.RED = RED;
|
|
619
441
|
this.node = node;
|
|
@@ -684,7 +506,7 @@ function setupConfigProxy(opts) {
|
|
|
684
506
|
if (fieldSchema?.["x-nrg-typed-input"] && value && typeof value === "object" && "type" in value && "value" in value) {
|
|
685
507
|
let ref = typedInputCache.get(value);
|
|
686
508
|
if (!ref) {
|
|
687
|
-
ref = new
|
|
509
|
+
ref = new TypedInput(RED, node, value);
|
|
688
510
|
typedInputCache.set(value, ref);
|
|
689
511
|
}
|
|
690
512
|
return ref;
|
|
@@ -711,31 +533,8 @@ function setupConfigProxy(opts) {
|
|
|
711
533
|
return createProxy(config, schema);
|
|
712
534
|
}
|
|
713
535
|
|
|
714
|
-
// src/sdk/lib/shared/schemas/utils.ts
|
|
715
|
-
function getCredentialsFromSchema(schema) {
|
|
716
|
-
if (!schema?.properties) return void 0;
|
|
717
|
-
const result = {};
|
|
718
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
719
|
-
const property = value;
|
|
720
|
-
const isPassword = property.format === "password";
|
|
721
|
-
if (!isPassword) {
|
|
722
|
-
console.warn(
|
|
723
|
-
`[nrg] credential "${key}" has no format:"password" \u2014 it is stored as a visible (cleartext-in-editor) credential. Add { format: "password" } to mask it.`
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
result[key] = {
|
|
727
|
-
// NOTE: required is always false because it is controlled by the JSON Schema and AJV validation instead of using node-red client core
|
|
728
|
-
required: false,
|
|
729
|
-
type: isPassword ? "password" : "text",
|
|
730
|
-
value: property.default ?? void 0
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
return result;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
536
|
// src/sdk/lib/server/nodes/node.ts
|
|
737
|
-
var
|
|
738
|
-
var Node = class _Node {
|
|
537
|
+
var Node = class {
|
|
739
538
|
/**
|
|
740
539
|
* Runtime NRG-node brand — inherited by every subclass (IONode/ConfigNode and
|
|
741
540
|
* factory-built classes) and checked by `defineModule` at runtime. See
|
|
@@ -747,6 +546,11 @@ var Node = class _Node {
|
|
|
747
546
|
static configSchema;
|
|
748
547
|
static credentialsSchema;
|
|
749
548
|
static settingsSchema;
|
|
549
|
+
// Per-type settings, resolved from `RED.settings` and validated once by
|
|
550
|
+
// `validateSettings` at registration; the instance `settings` getter returns
|
|
551
|
+
// it. It's *assigned* (never mutated in place), so every node type gets its
|
|
552
|
+
// own value with no cross-subclass sharing — a plain static, no cache needed.
|
|
553
|
+
static resolvedSettings;
|
|
750
554
|
static validateSettings(RED) {
|
|
751
555
|
if (!this.settingsSchema) return;
|
|
752
556
|
RED.log.info("Validating settings");
|
|
@@ -761,93 +565,28 @@ var Node = class _Node {
|
|
|
761
565
|
}
|
|
762
566
|
}
|
|
763
567
|
for (const [key, prop] of Object.entries(properties)) {
|
|
764
|
-
if (settings[key] === void 0) {
|
|
765
|
-
|
|
766
|
-
if (defaultValue !== void 0) {
|
|
767
|
-
settings[key] = defaultValue;
|
|
768
|
-
}
|
|
568
|
+
if (settings[key] === void 0 && prop._default !== void 0) {
|
|
569
|
+
settings[key] = prop._default;
|
|
769
570
|
}
|
|
770
571
|
}
|
|
771
572
|
RED.validator.validate(settings, this.settingsSchema, {
|
|
772
573
|
throwOnError: true
|
|
773
574
|
});
|
|
774
|
-
|
|
575
|
+
this.resolvedSettings = settings;
|
|
775
576
|
RED.log.info("Settings are valid");
|
|
776
577
|
}
|
|
777
|
-
static #buildSettings(NC) {
|
|
778
|
-
if (!NC.settingsSchema) return;
|
|
779
|
-
const settings = {};
|
|
780
|
-
const prefix = NC.type.replace(/-./g, (x) => x[1].toUpperCase());
|
|
781
|
-
for (const [key, prop] of Object.entries(NC.settingsSchema.properties)) {
|
|
782
|
-
const settingKey = prefix + key.charAt(0).toUpperCase() + key.slice(1);
|
|
783
|
-
settings[settingKey] = {
|
|
784
|
-
value: prop.default,
|
|
785
|
-
exportable: prop.exportable ?? false
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
return settings;
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Registers this node class with Node-RED. Handles instance creation,
|
|
792
|
-
* event handler wiring, settings validation, and the user's registered() hook.
|
|
793
|
-
*/
|
|
794
|
-
static async register(RED) {
|
|
795
|
-
const NodeClass = this;
|
|
796
|
-
if (NodeClass.color && !/^#[0-9A-Fa-f]{6}$/.test(NodeClass.color)) {
|
|
797
|
-
throw new NrgError(
|
|
798
|
-
`Invalid color "${NodeClass.color}" for ${NodeClass.type}: must be a 6-digit hex color like "#a6bbcf" (shorthand "#abc" is not accepted).`
|
|
799
|
-
);
|
|
800
|
-
}
|
|
801
|
-
RED.nodes.registerType(
|
|
802
|
-
NodeClass.type,
|
|
803
|
-
function(config) {
|
|
804
|
-
RED.nodes.createNode(this, config);
|
|
805
|
-
const node = new NodeClass(RED, this, config, this.credentials);
|
|
806
|
-
Object.defineProperty(this, "_node", {
|
|
807
|
-
value: node,
|
|
808
|
-
writable: false,
|
|
809
|
-
configurable: false,
|
|
810
|
-
enumerable: false
|
|
811
|
-
});
|
|
812
|
-
const createdPromise = Promise.resolve(node.created?.()).catch(
|
|
813
|
-
(error) => {
|
|
814
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
815
|
-
this.error("Error during created hook: " + message);
|
|
816
|
-
throw error;
|
|
817
|
-
}
|
|
818
|
-
);
|
|
819
|
-
createdPromise.catch(() => {
|
|
820
|
-
this.status({ fill: "red", shape: "ring", text: "created() failed" });
|
|
821
|
-
});
|
|
822
|
-
node[NRG_WIRE_HANDLERS](this, createdPromise);
|
|
823
|
-
},
|
|
824
|
-
{
|
|
825
|
-
credentials: NodeClass.credentialsSchema ? getCredentialsFromSchema(NodeClass.credentialsSchema) : {},
|
|
826
|
-
settings: _Node.#buildSettings(this)
|
|
827
|
-
}
|
|
828
|
-
);
|
|
829
|
-
NodeClass.validateSettings(RED);
|
|
830
|
-
try {
|
|
831
|
-
await Promise.resolve(NodeClass.registered?.(RED));
|
|
832
|
-
} catch (error) {
|
|
833
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
834
|
-
RED.log.error(
|
|
835
|
-
`Error during registered hook for ${NodeClass.type}: ${message}`
|
|
836
|
-
);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
578
|
RED;
|
|
840
579
|
node;
|
|
841
580
|
context;
|
|
842
581
|
config;
|
|
843
|
-
timers = /* @__PURE__ */ new Set();
|
|
844
|
-
intervals = /* @__PURE__ */ new Set();
|
|
582
|
+
#timers = /* @__PURE__ */ new Set();
|
|
583
|
+
#intervals = /* @__PURE__ */ new Set();
|
|
845
584
|
constructor(RED, node, config, credentials) {
|
|
846
585
|
this.RED = RED;
|
|
847
586
|
this.node = node;
|
|
848
587
|
const constructor = this.constructor;
|
|
849
588
|
if (constructor.configSchema) {
|
|
850
|
-
this.log("Validating configs");
|
|
589
|
+
this.node.log("Validating configs");
|
|
851
590
|
const configResult = this.RED.validator.validate(
|
|
852
591
|
config,
|
|
853
592
|
constructor.configSchema,
|
|
@@ -858,7 +597,7 @@ var Node = class _Node {
|
|
|
858
597
|
}
|
|
859
598
|
);
|
|
860
599
|
if (!configResult.valid && configResult.errors?.length) {
|
|
861
|
-
this.warn(
|
|
600
|
+
this.node.warn(
|
|
862
601
|
`Config validation errors: ${configResult.errors.map((e) => `${e.instancePath} ${e.message}`).join("; ")}`
|
|
863
602
|
);
|
|
864
603
|
}
|
|
@@ -870,7 +609,7 @@ var Node = class _Node {
|
|
|
870
609
|
schema: constructor.configSchema
|
|
871
610
|
});
|
|
872
611
|
if (constructor.credentialsSchema && credentials) {
|
|
873
|
-
this.log("Validating credentials");
|
|
612
|
+
this.node.log("Validating credentials");
|
|
874
613
|
const credResult = this.RED.validator.validate(
|
|
875
614
|
credentials,
|
|
876
615
|
constructor.credentialsSchema,
|
|
@@ -879,27 +618,30 @@ var Node = class _Node {
|
|
|
879
618
|
}
|
|
880
619
|
);
|
|
881
620
|
if (!credResult.valid && credResult.errors?.length) {
|
|
882
|
-
this.warn(
|
|
621
|
+
this.node.warn(
|
|
883
622
|
`Credentials validation errors: ${credResult.errors.map((e) => `${e.instancePath} ${e.message}`).join("; ")}`
|
|
884
623
|
);
|
|
885
624
|
}
|
|
886
625
|
}
|
|
887
626
|
}
|
|
888
|
-
|
|
889
|
-
|
|
627
|
+
// Wires the `close` handler, common to every node kind. IONode adds the `input`
|
|
628
|
+
// handler separately (NRG_SETUP_INPUT_HANDLER), so this setup carries none of the
|
|
629
|
+
// input-only `createdPromise`.
|
|
630
|
+
[NRG_SETUP_CLOSE_HANDLER]() {
|
|
631
|
+
this.node.on(
|
|
890
632
|
"close",
|
|
891
633
|
async (removed, done) => {
|
|
892
634
|
try {
|
|
893
|
-
this.log("Calling closed");
|
|
635
|
+
this.node.log("Calling closed");
|
|
894
636
|
await this.#closed(removed);
|
|
895
|
-
this.log("Node was closed");
|
|
637
|
+
this.node.log("Node was closed");
|
|
896
638
|
done();
|
|
897
639
|
} catch (error) {
|
|
898
640
|
if (error instanceof Error) {
|
|
899
|
-
this.error("Error while closing node: " + error.message);
|
|
641
|
+
this.node.error("Error while closing node: " + error.message);
|
|
900
642
|
done(error);
|
|
901
643
|
} else {
|
|
902
|
-
this.error("Unknown error occurred while closing node");
|
|
644
|
+
this.node.error("Unknown error occurred while closing node");
|
|
903
645
|
done(new Error("Unknown error occurred while closing node"));
|
|
904
646
|
}
|
|
905
647
|
}
|
|
@@ -910,12 +652,12 @@ var Node = class _Node {
|
|
|
910
652
|
try {
|
|
911
653
|
await Promise.resolve(this.closed?.(removed));
|
|
912
654
|
} finally {
|
|
913
|
-
this.log("clearing timers and intervals");
|
|
914
|
-
this
|
|
915
|
-
this
|
|
916
|
-
this
|
|
917
|
-
this
|
|
918
|
-
this.log("timers and intervals cleared");
|
|
655
|
+
this.node.log("clearing timers and intervals");
|
|
656
|
+
this.#timers.forEach((t) => clearTimeout(t));
|
|
657
|
+
this.#intervals.forEach((i) => clearInterval(i));
|
|
658
|
+
this.#timers.clear();
|
|
659
|
+
this.#intervals.clear();
|
|
660
|
+
this.node.log("timers and intervals cleared");
|
|
919
661
|
}
|
|
920
662
|
}
|
|
921
663
|
i18n(key, substitutions) {
|
|
@@ -924,24 +666,24 @@ var Node = class _Node {
|
|
|
924
666
|
}
|
|
925
667
|
setTimeout(fn, ms) {
|
|
926
668
|
const timer = setTimeout(() => {
|
|
927
|
-
this
|
|
669
|
+
this.#timers.delete(timer);
|
|
928
670
|
fn();
|
|
929
671
|
}, ms);
|
|
930
|
-
this
|
|
672
|
+
this.#timers.add(timer);
|
|
931
673
|
return timer;
|
|
932
674
|
}
|
|
933
675
|
setInterval(fn, ms) {
|
|
934
676
|
const interval = setInterval(fn, ms);
|
|
935
|
-
this
|
|
677
|
+
this.#intervals.add(interval);
|
|
936
678
|
return interval;
|
|
937
679
|
}
|
|
938
680
|
clearTimeout(timer) {
|
|
939
681
|
clearTimeout(timer);
|
|
940
|
-
this
|
|
682
|
+
this.#timers.delete(timer);
|
|
941
683
|
}
|
|
942
684
|
clearInterval(interval) {
|
|
943
685
|
clearInterval(interval);
|
|
944
|
-
this
|
|
686
|
+
this.#intervals.delete(interval);
|
|
945
687
|
}
|
|
946
688
|
on(event, callback) {
|
|
947
689
|
this.node.on(event, callback);
|
|
@@ -969,7 +711,7 @@ var Node = class _Node {
|
|
|
969
711
|
}
|
|
970
712
|
get settings() {
|
|
971
713
|
const constructor = this.constructor;
|
|
972
|
-
return
|
|
714
|
+
return constructor.resolvedSettings ?? {};
|
|
973
715
|
}
|
|
974
716
|
};
|
|
975
717
|
|
|
@@ -1048,56 +790,32 @@ function setupContext(context, store) {
|
|
|
1048
790
|
|
|
1049
791
|
// src/sdk/lib/server/nodes/io-node.ts
|
|
1050
792
|
var import_node_async_hooks = require("node:async_hooks");
|
|
1051
|
-
function isSchemaLike(obj) {
|
|
1052
|
-
return obj != null && typeof obj === "object" && import_typebox3.Kind in obj;
|
|
1053
|
-
}
|
|
1054
793
|
var RETURN_PROPERTY_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1055
794
|
var INPUT_KEY = "input";
|
|
1056
795
|
var IONode = class _IONode extends Node {
|
|
1057
796
|
static align;
|
|
797
|
+
/**
|
|
798
|
+
* Node palette color. The template-literal type only enforces a leading `#`
|
|
799
|
+
* (an exact 6-hex-digit template type is infeasible — it explodes to `TS2590`),
|
|
800
|
+
* so shorthand (`#abc`) and invalid hex type-check but are rejected at runtime.
|
|
801
|
+
* The real gate is the `/^#[0-9A-Fa-f]{6}$/` check in `Node.register`.
|
|
802
|
+
*/
|
|
1058
803
|
static color;
|
|
1059
|
-
|
|
1060
|
-
//
|
|
1061
|
-
// validated, and results are frequently non-objects.
|
|
1062
|
-
static outputsSchema;
|
|
1063
|
-
static validateInput = false;
|
|
1064
|
-
// A single boolean applies to every output port; a boolean[] sets the
|
|
1065
|
-
// per-port default by base-output index (missing entries default to false).
|
|
1066
|
-
static validateOutput = false;
|
|
804
|
+
// Port topology is TS-types-only: the build extracts a node's `Input`/`Output`
|
|
805
|
+
// generics and stamps them under `NRG_PORTS`. There is no schema fallback.
|
|
1067
806
|
static get inputs() {
|
|
1068
|
-
return this
|
|
807
|
+
return this[NRG_PORTS]?.inputs ?? 0;
|
|
1069
808
|
}
|
|
1070
809
|
static get outputs() {
|
|
1071
|
-
|
|
1072
|
-
if (!s) return 0;
|
|
1073
|
-
if (Array.isArray(s)) return s.length;
|
|
1074
|
-
if (isSchemaLike(s)) return 1;
|
|
1075
|
-
const keys = Object.keys(s);
|
|
1076
|
-
for (const key of keys) {
|
|
1077
|
-
if (/^\d+$/.test(key)) {
|
|
1078
|
-
throw new NrgError(
|
|
1079
|
-
`outputsSchema record key "${key}" in ${this.type} looks numeric. Use descriptive string names (e.g. "success", "failure") to avoid JavaScript object key ordering issues.`
|
|
1080
|
-
);
|
|
1081
|
-
}
|
|
1082
|
-
if (key === "error" || key === "complete" || key === "status") {
|
|
1083
|
-
throw new NrgError(
|
|
1084
|
-
`outputsSchema record key "${key}" in ${this.type} is reserved for built-in ports. Use a different name (e.g. "failed" instead of "error").`
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
return keys.length;
|
|
810
|
+
return this[NRG_PORTS]?.outputs ?? 0;
|
|
1089
811
|
}
|
|
1090
812
|
/**
|
|
1091
|
-
* The names of the base output ports
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
* TypeBox's `Kind` symbol is intact, so the editor reads the names directly
|
|
1095
|
-
* instead of guessing them from a serialized (symbol-stripped) schema.
|
|
813
|
+
* The names of the base output ports for a named-port node (`Port<T>` record
|
|
814
|
+
* in the `Output` generic), in declaration order — otherwise `undefined` (a
|
|
815
|
+
* single or positional output). Read straight off the injected topology.
|
|
1096
816
|
*/
|
|
1097
817
|
static get outputPortNames() {
|
|
1098
|
-
|
|
1099
|
-
if (!s || Array.isArray(s) || isSchemaLike(s)) return void 0;
|
|
1100
|
-
return Object.keys(s);
|
|
818
|
+
return this[NRG_PORTS]?.outputNames;
|
|
1101
819
|
}
|
|
1102
820
|
/**
|
|
1103
821
|
* Per-`input()`-invocation context, scoped via AsyncLocalStorage. One store
|
|
@@ -1113,7 +831,6 @@ var IONode = class _IONode extends Node {
|
|
|
1113
831
|
* of the node runtime and is never imported in a browser.
|
|
1114
832
|
*/
|
|
1115
833
|
static #invocation = new import_node_async_hooks.AsyncLocalStorage();
|
|
1116
|
-
context;
|
|
1117
834
|
constructor(RED, node, config, credentials) {
|
|
1118
835
|
super(RED, node, config, credentials);
|
|
1119
836
|
const context = node.context();
|
|
@@ -1137,10 +854,10 @@ var IONode = class _IONode extends Node {
|
|
|
1137
854
|
}
|
|
1138
855
|
}
|
|
1139
856
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
857
|
+
// Wires the `input` handler (IONode only). The base Node wires `close`
|
|
858
|
+
// separately (NRG_SETUP_CLOSE_HANDLER); the registrar invokes both.
|
|
859
|
+
[NRG_SETUP_INPUT_HANDLER](createdPromise) {
|
|
860
|
+
this.node.on(
|
|
1144
861
|
"input",
|
|
1145
862
|
async (msg, send, done) => {
|
|
1146
863
|
try {
|
|
@@ -1153,7 +870,7 @@ var IONode = class _IONode extends Node {
|
|
|
1153
870
|
}
|
|
1154
871
|
const store = { inputMsg: msg, send };
|
|
1155
872
|
try {
|
|
1156
|
-
|
|
873
|
+
this.node.log("Calling input");
|
|
1157
874
|
const result = await this.#input(msg, store);
|
|
1158
875
|
if (this.config.completePort) {
|
|
1159
876
|
this.#sendToPort("complete", {
|
|
@@ -1166,7 +883,7 @@ var IONode = class _IONode extends Node {
|
|
|
1166
883
|
});
|
|
1167
884
|
}
|
|
1168
885
|
done();
|
|
1169
|
-
|
|
886
|
+
this.node.log("Input processed");
|
|
1170
887
|
} catch (error) {
|
|
1171
888
|
const errorMsg = error instanceof Error ? error.message : "Unknown error during input handling";
|
|
1172
889
|
if (this.config.errorPort && !store.errorEmitted) {
|
|
@@ -1183,13 +900,13 @@ var IONode = class _IONode extends Node {
|
|
|
1183
900
|
});
|
|
1184
901
|
}
|
|
1185
902
|
if (error instanceof Error) {
|
|
1186
|
-
|
|
903
|
+
this.node.error(
|
|
1187
904
|
"Error while processing input: " + error.message,
|
|
1188
905
|
msg
|
|
1189
906
|
);
|
|
1190
907
|
done(error);
|
|
1191
908
|
} else {
|
|
1192
|
-
|
|
909
|
+
this.node.error(
|
|
1193
910
|
"Unknown error occurred during input handling",
|
|
1194
911
|
msg
|
|
1195
912
|
);
|
|
@@ -1203,17 +920,19 @@ var IONode = class _IONode extends Node {
|
|
|
1203
920
|
return void 0;
|
|
1204
921
|
}
|
|
1205
922
|
async #input(msg, store) {
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
923
|
+
const shouldValidateInput = this.config.validateInput ?? false;
|
|
924
|
+
if (shouldValidateInput) {
|
|
925
|
+
const inputSchema = this.#effectiveInputSchema();
|
|
926
|
+
if (inputSchema) {
|
|
927
|
+
this.node.log("Validating input");
|
|
928
|
+
this.RED.validator.validate(msg, inputSchema, {
|
|
929
|
+
// Pure predicate: never coerce or inject defaults into the live msg
|
|
930
|
+
// that continues downstream.
|
|
931
|
+
mutate: false,
|
|
932
|
+
throwOnError: true
|
|
933
|
+
});
|
|
934
|
+
this.node.log("Input is valid");
|
|
935
|
+
}
|
|
1217
936
|
}
|
|
1218
937
|
return await _IONode.#invocation.run(
|
|
1219
938
|
store,
|
|
@@ -1221,8 +940,8 @@ var IONode = class _IONode extends Node {
|
|
|
1221
940
|
);
|
|
1222
941
|
}
|
|
1223
942
|
send(msg) {
|
|
1224
|
-
const multi = this
|
|
1225
|
-
const values = multi ? msg.slice(0, this
|
|
943
|
+
const multi = this.#baseOutputs > 1 && Array.isArray(msg);
|
|
944
|
+
const values = multi ? msg.slice(0, this.#baseOutputs) : [msg];
|
|
1226
945
|
const out = values.map((m, port) => {
|
|
1227
946
|
if (m == null) return m;
|
|
1228
947
|
this.#validatePort(m, port);
|
|
@@ -1240,39 +959,67 @@ var IONode = class _IONode extends Node {
|
|
|
1240
959
|
}
|
|
1241
960
|
/**
|
|
1242
961
|
* Per-port output validation. A port validates when its flow-author flag
|
|
1243
|
-
* (`config.validateOutputs[port]
|
|
1244
|
-
*
|
|
1245
|
-
* a single boolean (all ports) or a boolean[] (per-port, by base index).
|
|
962
|
+
* (`config.validateOutputs[port]`, whose default the node author seeds in the
|
|
963
|
+
* config schema) is on and a schema exists for that port.
|
|
1246
964
|
*/
|
|
1247
965
|
#validatePort(value, port) {
|
|
1248
|
-
|
|
1249
|
-
const
|
|
1250
|
-
const staticForPort = Array.isArray(staticFlag) ? staticFlag[port] ?? false : staticFlag;
|
|
1251
|
-
const configured = this.config.validateOutputs?.[port];
|
|
1252
|
-
if (!(configured ?? staticForPort)) return;
|
|
1253
|
-
const schema = this.#outputSchemaForPort(port);
|
|
966
|
+
if (!this.config.validateOutputs?.[port]) return;
|
|
967
|
+
const schema = this.#effectiveOutputSchema(port);
|
|
1254
968
|
if (!schema) return;
|
|
1255
|
-
this.log("Validating output");
|
|
969
|
+
this.node.log("Validating output");
|
|
1256
970
|
this.RED.validator.validate(value, schema, {
|
|
1257
971
|
// Pure predicate: don't coerce or default the outgoing value.
|
|
1258
972
|
mutate: false,
|
|
1259
973
|
throwOnError: true
|
|
1260
974
|
});
|
|
1261
|
-
this.log("Output is valid");
|
|
975
|
+
this.node.log("Output is valid");
|
|
976
|
+
}
|
|
977
|
+
// Per-instance cache of resolved data-validation schemas, keyed by the raw JSON
|
|
978
|
+
// string (a node's config is immutable for its lifetime). `null` = the string
|
|
979
|
+
// is unusable (bad JSON, or valid JSON that does not compile) → the boundary is
|
|
980
|
+
// not validated. Memoizing keeps the SAME parsed object across messages, so
|
|
981
|
+
// the validator's object-keyed compile cache hits: no per-message JSON.parse,
|
|
982
|
+
// no per-message recompile/leak, and the warning fires exactly once.
|
|
983
|
+
#overrideSchemas = /* @__PURE__ */ new Map();
|
|
984
|
+
/** Resolve a data-validation schema (a JSON-Schema string from config — the
|
|
985
|
+
* author default, overridable by the flow author in the editor) to a schema
|
|
986
|
+
* object, memoized per instance. `undefined` → no schema (blank / not a
|
|
987
|
+
* string), so the boundary is not validated. A string that is invalid JSON, or
|
|
988
|
+
* valid JSON that does NOT compile (a typo'd keyword), also yields `undefined`
|
|
989
|
+
* — warned once here rather than throwing on every message inside `validate()`. */
|
|
990
|
+
#resolveOverride(raw) {
|
|
991
|
+
if (typeof raw !== "string" || !raw.trim()) return void 0;
|
|
992
|
+
const cached = this.#overrideSchemas.get(raw);
|
|
993
|
+
if (cached !== void 0) return cached ?? void 0;
|
|
994
|
+
let resolved = null;
|
|
995
|
+
try {
|
|
996
|
+
const parsed = JSON.parse(raw);
|
|
997
|
+
this.RED.validator.createValidator(parsed, false);
|
|
998
|
+
resolved = parsed;
|
|
999
|
+
} catch (e) {
|
|
1000
|
+
const reason = e instanceof Error ? e.message : "parse/compile error";
|
|
1001
|
+
this.node.warn(
|
|
1002
|
+
`Ignoring an invalid data-validation schema (${reason}); skipping validation.`
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
this.#overrideSchemas.set(raw, resolved);
|
|
1006
|
+
return resolved ?? void 0;
|
|
1007
|
+
}
|
|
1008
|
+
/** The schema used for INPUT validation: the `config.inputSchema` field (the
|
|
1009
|
+
* node author's default, overridable by the flow author). Data-validation
|
|
1010
|
+
* schemas are config-driven only — there is no static schema. */
|
|
1011
|
+
#effectiveInputSchema() {
|
|
1012
|
+
return this.#resolveOverride(this.config.inputSchema);
|
|
1262
1013
|
}
|
|
1263
|
-
/**
|
|
1264
|
-
*
|
|
1265
|
-
#
|
|
1266
|
-
|
|
1267
|
-
if (!raw) return void 0;
|
|
1268
|
-
if (Array.isArray(raw)) return raw[port];
|
|
1269
|
-
if (isSchemaLike(raw)) return raw;
|
|
1270
|
-
return Object.values(raw)[port];
|
|
1014
|
+
/** The schema used for OUTPUT validation on a port: the `config.outputSchemas`
|
|
1015
|
+
* field for that port (author default, flow-author-overridable). */
|
|
1016
|
+
#effectiveOutputSchema(port) {
|
|
1017
|
+
return this.#resolveOverride(this.config.outputSchemas?.[port]);
|
|
1271
1018
|
}
|
|
1272
1019
|
/**
|
|
1273
1020
|
* The return key for an output port — `"output"` unless a custom one is set
|
|
1274
|
-
* via `outputReturnProperties[port]` (author default and/or
|
|
1275
|
-
* override,
|
|
1021
|
+
* via `outputReturnProperties[port]` (the node author's declared default and/or
|
|
1022
|
+
* the flow author's per-port override, which the editor always allows).
|
|
1276
1023
|
* `this.send(x)` always means "x is the value at this port's return key",
|
|
1277
1024
|
* never "x is the whole outgoing message".
|
|
1278
1025
|
*/
|
|
@@ -1285,8 +1032,8 @@ var IONode = class _IONode extends Node {
|
|
|
1285
1032
|
}
|
|
1286
1033
|
/**
|
|
1287
1034
|
* Resolves the context mode for a base-output port from the flow author's
|
|
1288
|
-
* per-port config (`config.outputContextModes[port]`,
|
|
1289
|
-
*
|
|
1035
|
+
* per-port config (`config.outputContextModes[port]`, which the editor lets the
|
|
1036
|
+
* flow author set on any port), falling back to `"carry"`.
|
|
1290
1037
|
*/
|
|
1291
1038
|
#resolveContextMode(port) {
|
|
1292
1039
|
return this.config.outputContextModes?.[port] ?? "carry";
|
|
@@ -1308,19 +1055,28 @@ var IONode = class _IONode extends Node {
|
|
|
1308
1055
|
return { ...input, [key]: value };
|
|
1309
1056
|
}
|
|
1310
1057
|
// --- Built-in port management ---
|
|
1311
|
-
|
|
1058
|
+
// Private, override-proof accessors the framework reads for port routing. A
|
|
1059
|
+
// consumer field named `baseOutputs`/`totalOutputs` shadows the PUBLIC getters
|
|
1060
|
+
// below (self-inflicted), but never these `#` ones the framework relies on.
|
|
1061
|
+
get #baseOutputs() {
|
|
1312
1062
|
return this.constructor.outputs ?? 0;
|
|
1313
1063
|
}
|
|
1314
|
-
get totalOutputs() {
|
|
1315
|
-
let count = this
|
|
1064
|
+
get #totalOutputs() {
|
|
1065
|
+
let count = this.#baseOutputs;
|
|
1316
1066
|
if (this.config.errorPort) count++;
|
|
1317
1067
|
if (this.config.completePort) count++;
|
|
1318
1068
|
if (this.config.statusPort) count++;
|
|
1319
1069
|
return count;
|
|
1320
1070
|
}
|
|
1071
|
+
get baseOutputs() {
|
|
1072
|
+
return this.#baseOutputs;
|
|
1073
|
+
}
|
|
1074
|
+
get totalOutputs() {
|
|
1075
|
+
return this.#totalOutputs;
|
|
1076
|
+
}
|
|
1321
1077
|
/**
|
|
1322
1078
|
* Send a message to a specific output port by index or name.
|
|
1323
|
-
*
|
|
1079
|
+
* Named ports are resolved from the node's named `Port<T>` Output generic.
|
|
1324
1080
|
* Numeric indices refer to the base output ports (0-based).
|
|
1325
1081
|
*
|
|
1326
1082
|
* Built-in ports (`"error"`, `"complete"`, `"status"`) are managed by the
|
|
@@ -1338,13 +1094,18 @@ var IONode = class _IONode extends Node {
|
|
|
1338
1094
|
if (typeof port === "string" && portIndex === null) {
|
|
1339
1095
|
const keys = this.#namedPortKeys();
|
|
1340
1096
|
throw new NrgError(
|
|
1341
|
-
keys && keys.length ? `sendToPort("${port}") \u2014 unknown output port. Valid named ports: ${keys.map((n) => `"${n}"`).join(", ")}.` : `sendToPort("${port}") \u2014 this node has no named output ports.
|
|
1097
|
+
keys && keys.length ? `sendToPort("${port}") \u2014 unknown output port. Valid named ports: ${keys.map((n) => `"${n}"`).join(", ")}.` : `sendToPort("${port}") \u2014 this node has no named output ports. Declare named ports with a Port<T> record in the node's Output generic, or send to a numeric port index.`
|
|
1342
1098
|
);
|
|
1343
1099
|
}
|
|
1344
|
-
const
|
|
1100
|
+
const idx = portIndex ?? 0;
|
|
1101
|
+
if (msg == null) {
|
|
1102
|
+
this.#sendToPort(port, msg);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
this.#validatePort(msg, idx);
|
|
1345
1106
|
this.#sendToPort(
|
|
1346
1107
|
port,
|
|
1347
|
-
|
|
1108
|
+
this.#wrapOutgoing(msg, this.#resolveContextMode(idx), idx)
|
|
1348
1109
|
);
|
|
1349
1110
|
}
|
|
1350
1111
|
#sendToPort(port, msg) {
|
|
@@ -1360,16 +1121,16 @@ var IONode = class _IONode extends Node {
|
|
|
1360
1121
|
throw new NrgError(`Unknown output port "${port}".`);
|
|
1361
1122
|
}
|
|
1362
1123
|
}
|
|
1363
|
-
const out = new Array(this
|
|
1124
|
+
const out = new Array(this.#totalOutputs);
|
|
1364
1125
|
out[portIndex] = msg;
|
|
1365
1126
|
this.node.send(out);
|
|
1366
1127
|
}
|
|
1367
|
-
/** The declared named output ports (
|
|
1368
|
-
*
|
|
1128
|
+
/** The declared named output ports (from the injected `Output`-generic
|
|
1129
|
+
* topology's `Port<T>` record), or null when the node has none (a
|
|
1130
|
+
* positional/single output, or no declaration). */
|
|
1369
1131
|
#namedPortKeys() {
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
return Object.keys(schema);
|
|
1132
|
+
const NC = this.constructor;
|
|
1133
|
+
return NC[NRG_PORTS]?.outputNames ?? null;
|
|
1373
1134
|
}
|
|
1374
1135
|
#getNamedPortIndex(name) {
|
|
1375
1136
|
const keys = this.#namedPortKeys();
|
|
@@ -1379,9 +1140,9 @@ var IONode = class _IONode extends Node {
|
|
|
1379
1140
|
}
|
|
1380
1141
|
#getBuiltinPortIndex(name) {
|
|
1381
1142
|
if (name === "error") {
|
|
1382
|
-
return this.config.errorPort ? this
|
|
1143
|
+
return this.config.errorPort ? this.#baseOutputs : null;
|
|
1383
1144
|
}
|
|
1384
|
-
let idx = this
|
|
1145
|
+
let idx = this.#baseOutputs;
|
|
1385
1146
|
if (this.config.errorPort) idx++;
|
|
1386
1147
|
if (name === "complete") {
|
|
1387
1148
|
return this.config.completePort ? idx : null;
|
|
@@ -1391,9 +1152,9 @@ var IONode = class _IONode extends Node {
|
|
|
1391
1152
|
}
|
|
1392
1153
|
#nodeSource() {
|
|
1393
1154
|
return {
|
|
1394
|
-
id: this.id,
|
|
1155
|
+
id: this.node.id,
|
|
1395
1156
|
type: this.constructor.type,
|
|
1396
|
-
name: this.name
|
|
1157
|
+
name: this.node.name
|
|
1397
1158
|
};
|
|
1398
1159
|
}
|
|
1399
1160
|
status(status) {
|
|
@@ -1451,7 +1212,6 @@ var ConfigNode = class extends Node {
|
|
|
1451
1212
|
// NodeRef resolution) can verify a referenced node is a config node — the only
|
|
1452
1213
|
// guard a JS author gets. See NRG_CONFIG_NODE in ./symbols.
|
|
1453
1214
|
[NRG_CONFIG_NODE] = true;
|
|
1454
|
-
context;
|
|
1455
1215
|
constructor(RED, node, config, credentials) {
|
|
1456
1216
|
super(RED, node, config, credentials);
|
|
1457
1217
|
const context = node.context();
|
|
@@ -1480,96 +1240,224 @@ var ConfigNode = class extends Node {
|
|
|
1480
1240
|
}
|
|
1481
1241
|
};
|
|
1482
1242
|
|
|
1483
|
-
// src/sdk/lib/
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1243
|
+
// src/sdk/lib/shared/schemas/factories.ts
|
|
1244
|
+
var import_typebox2 = require("@sinclair/typebox");
|
|
1245
|
+
|
|
1246
|
+
// src/sdk/lib/shared/constants.ts
|
|
1247
|
+
var TYPED_INPUT_TYPES = [
|
|
1248
|
+
"msg",
|
|
1249
|
+
"flow",
|
|
1250
|
+
"global",
|
|
1251
|
+
"str",
|
|
1252
|
+
"num",
|
|
1253
|
+
"bool",
|
|
1254
|
+
"json",
|
|
1255
|
+
"bin",
|
|
1256
|
+
"re",
|
|
1257
|
+
"jsonata",
|
|
1258
|
+
"date",
|
|
1259
|
+
"env",
|
|
1260
|
+
"node",
|
|
1261
|
+
"cred"
|
|
1262
|
+
];
|
|
1263
|
+
|
|
1264
|
+
// src/sdk/lib/shared/schemas/base.ts
|
|
1265
|
+
var import_typebox = require("@sinclair/typebox");
|
|
1266
|
+
var TypedInputSchema = import_typebox.Type.Object(
|
|
1267
|
+
{
|
|
1268
|
+
value: import_typebox.Type.Union(
|
|
1269
|
+
[
|
|
1270
|
+
import_typebox.Type.String(),
|
|
1271
|
+
import_typebox.Type.Number(),
|
|
1272
|
+
import_typebox.Type.Boolean(),
|
|
1273
|
+
import_typebox.Type.Null()
|
|
1274
|
+
],
|
|
1275
|
+
{
|
|
1276
|
+
description: "The actual value entered or selected.",
|
|
1277
|
+
default: ""
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
type: import_typebox.Type.Union(
|
|
1281
|
+
TYPED_INPUT_TYPES.map((type) => import_typebox.Type.Literal(type)),
|
|
1282
|
+
{
|
|
1283
|
+
description: "The type of the value (string, number, message property, etc.)",
|
|
1284
|
+
default: "str"
|
|
1285
|
+
}
|
|
1286
|
+
)
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
description: "Represents a Node-RED TypedInput value and its type.",
|
|
1290
|
+
default: {
|
|
1291
|
+
type: "str",
|
|
1292
|
+
value: ""
|
|
1496
1293
|
}
|
|
1497
1294
|
}
|
|
1295
|
+
);
|
|
1296
|
+
|
|
1297
|
+
// src/sdk/lib/shared/schemas/factories.ts
|
|
1298
|
+
var JSON_TYPES = /* @__PURE__ */ new Set([
|
|
1299
|
+
"null",
|
|
1300
|
+
"boolean",
|
|
1301
|
+
"object",
|
|
1302
|
+
"array",
|
|
1303
|
+
"number",
|
|
1304
|
+
"integer",
|
|
1305
|
+
"string"
|
|
1306
|
+
]);
|
|
1307
|
+
function isJSONType(value) {
|
|
1308
|
+
return typeof value === "string" && JSON_TYPES.has(value);
|
|
1498
1309
|
}
|
|
1499
|
-
function
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1310
|
+
function NodeRef(type, options) {
|
|
1311
|
+
return {
|
|
1312
|
+
...import_typebox2.Type.String({
|
|
1313
|
+
description: options?.description || `Reference to ${type}`
|
|
1314
|
+
}),
|
|
1315
|
+
// `...options` first so user options (description, x-nrg-form, …) apply, but
|
|
1316
|
+
// the framework keys below win — a caller can't clobber `format`/
|
|
1317
|
+
// `x-nrg-node-type`/`Kind` and silently break NodeRef resolution.
|
|
1318
|
+
...options,
|
|
1319
|
+
format: "node-id",
|
|
1320
|
+
"x-nrg-node-type": type,
|
|
1321
|
+
[import_typebox2.Kind]: "NodeRef"
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
function TypedInput2(options) {
|
|
1325
|
+
const opts = options ?? {};
|
|
1326
|
+
const restrictedTypes = opts["x-nrg-form"]?.typedInputTypes;
|
|
1327
|
+
if (restrictedTypes && restrictedTypes.length === 0) {
|
|
1328
|
+
throw new Error(
|
|
1329
|
+
"SchemaType.TypedInput: typedInputTypes must list at least one type."
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
const explicitType = opts.default?.type;
|
|
1333
|
+
if (restrictedTypes && explicitType !== void 0 && !restrictedTypes.includes(explicitType)) {
|
|
1334
|
+
throw new Error(
|
|
1335
|
+
`SchemaType.TypedInput: default type "${explicitType}" is not one of its typedInputTypes [${restrictedTypes.join(", ")}].`
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
const defaultType = explicitType ?? (restrictedTypes ? restrictedTypes.includes("str") ? "str" : restrictedTypes[0] : "str");
|
|
1339
|
+
return {
|
|
1340
|
+
...TypedInputSchema,
|
|
1341
|
+
// `...options` first; the framework keys below win (see NodeRef).
|
|
1342
|
+
...options,
|
|
1343
|
+
default: {
|
|
1344
|
+
...TypedInputSchema.default,
|
|
1345
|
+
...opts.default,
|
|
1346
|
+
type: defaultType
|
|
1347
|
+
},
|
|
1348
|
+
"x-nrg-typed-input": true,
|
|
1349
|
+
[import_typebox2.Kind]: "TypedInput"
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function NrgString(options) {
|
|
1353
|
+
return import_typebox2.Type.String(options);
|
|
1354
|
+
}
|
|
1355
|
+
function OutputReturnProperties(options) {
|
|
1356
|
+
return import_typebox2.Type.Record(
|
|
1357
|
+
import_typebox2.Type.Number(),
|
|
1358
|
+
import_typebox2.Type.String({ pattern: "^[A-Za-z_$][A-Za-z0-9_$]*$" }),
|
|
1359
|
+
{
|
|
1360
|
+
description: "Per-port return property, keyed by output port index. A missing entry falls back to `output`.",
|
|
1361
|
+
default: {},
|
|
1362
|
+
...options
|
|
1520
1363
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
function OutputContextModes(options) {
|
|
1367
|
+
return import_typebox2.Type.Record(
|
|
1368
|
+
import_typebox2.Type.Number(),
|
|
1369
|
+
import_typebox2.Type.Union([
|
|
1370
|
+
import_typebox2.Type.Literal("carry"),
|
|
1371
|
+
import_typebox2.Type.Literal("trace"),
|
|
1372
|
+
import_typebox2.Type.Literal("reset")
|
|
1373
|
+
]),
|
|
1374
|
+
{
|
|
1375
|
+
description: "Per-port context mode, keyed by output port index. A missing entry falls back to `carry`.",
|
|
1376
|
+
default: {},
|
|
1377
|
+
...options
|
|
1523
1378
|
}
|
|
1524
|
-
};
|
|
1525
|
-
Object.defineProperty(NodeClass, "name", {
|
|
1526
|
-
value: def.type.replace(
|
|
1527
|
-
/(^|-)(\w)/g,
|
|
1528
|
-
(_, __, c) => c.toUpperCase()
|
|
1529
|
-
),
|
|
1530
|
-
configurable: true
|
|
1531
|
-
});
|
|
1532
|
-
normalizeSchemas(
|
|
1533
|
-
[
|
|
1534
|
-
NodeClass.configSchema,
|
|
1535
|
-
NodeClass.credentialsSchema,
|
|
1536
|
-
NodeClass.settingsSchema,
|
|
1537
|
-
NodeClass.inputSchema
|
|
1538
|
-
],
|
|
1539
|
-
NodeClass.outputsSchema
|
|
1540
1379
|
);
|
|
1541
|
-
return NodeClass;
|
|
1542
1380
|
}
|
|
1543
|
-
function
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1381
|
+
function OutputSchemas(options) {
|
|
1382
|
+
return import_typebox2.Type.Record(import_typebox2.Type.Number(), import_typebox2.Type.String(), {
|
|
1383
|
+
description: "Per-port output data-validation schema (JSON Schema string), keyed by output port index.",
|
|
1384
|
+
default: {},
|
|
1385
|
+
...options
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
function InputSchema(options) {
|
|
1389
|
+
return import_typebox2.Type.String({
|
|
1390
|
+
description: "Input data-validation schema (JSON Schema string).",
|
|
1391
|
+
default: "",
|
|
1392
|
+
...options
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
function NrgUnsafe(options) {
|
|
1396
|
+
return import_typebox2.Type.Unsafe(options);
|
|
1397
|
+
}
|
|
1398
|
+
var NRG_SCHEMA_TYPES_FACTORIES = {
|
|
1399
|
+
String: NrgString,
|
|
1400
|
+
Unsafe: NrgUnsafe,
|
|
1401
|
+
NodeRef,
|
|
1402
|
+
TypedInput: TypedInput2,
|
|
1403
|
+
OutputReturnProperties,
|
|
1404
|
+
OutputContextModes,
|
|
1405
|
+
OutputSchemas,
|
|
1406
|
+
InputSchema
|
|
1407
|
+
};
|
|
1408
|
+
var SchemaType = Object.assign(
|
|
1409
|
+
{},
|
|
1410
|
+
import_typebox2.Type,
|
|
1411
|
+
NRG_SCHEMA_TYPES_FACTORIES
|
|
1412
|
+
);
|
|
1413
|
+
function markNonValidatable(schema) {
|
|
1414
|
+
const type = schema.type;
|
|
1415
|
+
const hasInvalidType = type !== void 0 && (Array.isArray(type) ? !type.every(isJSONType) : !isJSONType(type));
|
|
1416
|
+
if (hasInvalidType) {
|
|
1417
|
+
schema["x-nrg-skip-validation"] = true;
|
|
1418
|
+
if (schema.default !== void 0) {
|
|
1419
|
+
schema._default = schema.default;
|
|
1420
|
+
delete schema.default;
|
|
1551
1421
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1422
|
+
delete schema.type;
|
|
1423
|
+
}
|
|
1424
|
+
if (schema.properties) {
|
|
1425
|
+
for (const prop of Object.values(schema.properties)) {
|
|
1426
|
+
markNonValidatable(prop);
|
|
1554
1427
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1428
|
+
}
|
|
1429
|
+
if (schema.patternProperties) {
|
|
1430
|
+
for (const prop of Object.values(schema.patternProperties)) {
|
|
1431
|
+
markNonValidatable(prop);
|
|
1557
1432
|
}
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
)
|
|
1564
|
-
|
|
1433
|
+
}
|
|
1434
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
1435
|
+
markNonValidatable(schema.additionalProperties);
|
|
1436
|
+
}
|
|
1437
|
+
if (schema.items) {
|
|
1438
|
+
markNonValidatable(schema.items);
|
|
1439
|
+
}
|
|
1440
|
+
if (schema.anyOf) {
|
|
1441
|
+
schema.anyOf.forEach((s) => markNonValidatable(s));
|
|
1442
|
+
}
|
|
1443
|
+
if (schema.allOf) {
|
|
1444
|
+
schema.allOf.forEach((s) => markNonValidatable(s));
|
|
1445
|
+
}
|
|
1446
|
+
if (schema.oneOf) {
|
|
1447
|
+
schema.oneOf.forEach((s) => markNonValidatable(s));
|
|
1448
|
+
}
|
|
1449
|
+
return schema;
|
|
1450
|
+
}
|
|
1451
|
+
function defineSchema(properties, options) {
|
|
1452
|
+
const schema = SchemaType.Object(properties, {
|
|
1453
|
+
...options,
|
|
1454
|
+
$id: options?.$id ?? crypto.randomUUID()
|
|
1565
1455
|
});
|
|
1566
|
-
|
|
1567
|
-
NodeClass.configSchema,
|
|
1568
|
-
NodeClass.credentialsSchema,
|
|
1569
|
-
NodeClass.settingsSchema
|
|
1570
|
-
]);
|
|
1571
|
-
return NodeClass;
|
|
1456
|
+
return markNonValidatable(schema);
|
|
1572
1457
|
}
|
|
1458
|
+
|
|
1459
|
+
// src/sdk/lib/shared/schemas/index.ts
|
|
1460
|
+
var import_typebox3 = require("@sinclair/typebox");
|
|
1573
1461
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1574
1462
|
0 && (module.exports = {
|
|
1575
1463
|
ConfigNode,
|
|
@@ -1578,8 +1466,6 @@ function defineConfigNode(def) {
|
|
|
1578
1466
|
Node,
|
|
1579
1467
|
NrgError,
|
|
1580
1468
|
SchemaType,
|
|
1581
|
-
defineConfigNode,
|
|
1582
|
-
defineIONode,
|
|
1583
1469
|
defineModule,
|
|
1584
1470
|
defineSchema,
|
|
1585
1471
|
registerType,
|