@bonsae/nrg 0.6.3 → 0.8.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/README.md +6 -4
- package/package.json +1 -1
- package/server/index.cjs +100 -85
- package/server/resources/nrg-client.js +2308 -2220
- package/types/server.d.ts +45 -32
package/README.md
CHANGED
|
@@ -14,10 +14,12 @@ Build Node-RED nodes with Vue 3, TypeScript, and JSON Schema validation.
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
pnpm add @bonsae/nrg
|
|
18
|
-
pnpm add -D vite
|
|
17
|
+
pnpm add @bonsae/nrg
|
|
18
|
+
pnpm add -D vite vue
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
> `vite` and `vue` are dev dependencies because they are only needed at build time. The Vue runtime is bundled by nrg and served automatically — your published package does not need them at runtime.
|
|
22
|
+
|
|
21
23
|
## Package Exports
|
|
22
24
|
|
|
23
25
|
| Export | Description |
|
|
@@ -32,8 +34,8 @@ pnpm add -D vite
|
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
36
|
# In your Node-RED package project
|
|
35
|
-
pnpm add @bonsae/nrg
|
|
36
|
-
pnpm add -D vite
|
|
37
|
+
pnpm add @bonsae/nrg
|
|
38
|
+
pnpm add -D vite vue
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
**vite.config.ts**
|
package/package.json
CHANGED
package/server/index.cjs
CHANGED
|
@@ -103,11 +103,11 @@ var Validator = class {
|
|
|
103
103
|
*/
|
|
104
104
|
addCustomFormats(formats) {
|
|
105
105
|
if (!formats) return;
|
|
106
|
-
Object.entries(formats).forEach(([name,
|
|
107
|
-
if (
|
|
108
|
-
this.ajv.addFormat(name,
|
|
106
|
+
Object.entries(formats).forEach(([name, validator2]) => {
|
|
107
|
+
if (validator2 instanceof RegExp) {
|
|
108
|
+
this.ajv.addFormat(name, validator2);
|
|
109
109
|
} else {
|
|
110
|
-
this.ajv.addFormat(name, { validate:
|
|
110
|
+
this.ajv.addFormat(name, { validate: validator2 });
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
}
|
|
@@ -124,23 +124,23 @@ var Validator = class {
|
|
|
124
124
|
const cached = this.ajv.getSchema(schema.$id);
|
|
125
125
|
if (cached) return cached;
|
|
126
126
|
}
|
|
127
|
-
const
|
|
128
|
-
return
|
|
127
|
+
const validator2 = this.ajv.compile(schema);
|
|
128
|
+
return validator2;
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Validate data against a schema and return a structured result
|
|
132
132
|
*/
|
|
133
133
|
validate(data, schema, options) {
|
|
134
|
-
const
|
|
135
|
-
const valid =
|
|
134
|
+
const validator2 = this.createValidator(schema, options?.cacheKey);
|
|
135
|
+
const valid = validator2(data);
|
|
136
136
|
if (!valid) {
|
|
137
|
-
const errorMessage = this.formatErrors(
|
|
137
|
+
const errorMessage = this.formatErrors(validator2.errors);
|
|
138
138
|
if (options?.throwOnError) {
|
|
139
|
-
throw new ValidationError(errorMessage,
|
|
139
|
+
throw new ValidationError(errorMessage, validator2.errors || []);
|
|
140
140
|
}
|
|
141
141
|
return {
|
|
142
142
|
valid: false,
|
|
143
|
-
errors:
|
|
143
|
+
errors: validator2.errors || void 0,
|
|
144
144
|
errorMessage
|
|
145
145
|
};
|
|
146
146
|
}
|
|
@@ -198,39 +198,33 @@ var ValidationError = class _ValidationError extends Error {
|
|
|
198
198
|
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
199
199
|
}
|
|
200
200
|
};
|
|
201
|
-
var validator = new Validator();
|
|
202
201
|
|
|
203
|
-
// src/core/server/
|
|
204
|
-
var
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
202
|
+
// src/core/server/validation.ts
|
|
203
|
+
var validator = void 0;
|
|
204
|
+
function initValidator(RED) {
|
|
205
|
+
validator = new Validator({
|
|
206
|
+
customKeywords: [
|
|
207
|
+
{
|
|
208
|
+
keyword: "x-nrg-skip-validation",
|
|
209
|
+
schemaType: "boolean",
|
|
210
|
+
valid: true
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
keyword: "x-nrg-node-type",
|
|
214
|
+
type: "string",
|
|
215
|
+
validate: (schemaValue, dataValue) => {
|
|
216
|
+
if (!dataValue) return true;
|
|
217
|
+
const node = RED.nodes.getNode(dataValue);
|
|
218
|
+
return node?.type === schemaValue;
|
|
221
219
|
}
|
|
222
|
-
],
|
|
223
|
-
customFormats: {
|
|
224
|
-
"node-id": /^[a-zA-Z0-9-_]+$/,
|
|
225
|
-
"flow-id": /^[a-f0-9]{16}$/,
|
|
226
|
-
"topic-path": (data) => /^[a-zA-Z0-9/_-]+$/.test(data)
|
|
227
220
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
221
|
+
],
|
|
222
|
+
customFormats: {
|
|
223
|
+
"node-id": /^[a-zA-Z0-9-_]+$/,
|
|
224
|
+
"flow-id": /^[a-f0-9]{16}$/,
|
|
225
|
+
"topic-path": (data) => /^[a-zA-Z0-9/_-]+$/.test(data)
|
|
226
|
+
}
|
|
227
|
+
});
|
|
234
228
|
}
|
|
235
229
|
|
|
236
230
|
// src/core/errors.ts
|
|
@@ -242,6 +236,40 @@ var NrgError = class _NrgError extends Error {
|
|
|
242
236
|
}
|
|
243
237
|
};
|
|
244
238
|
|
|
239
|
+
// src/core/server/typed-input.ts
|
|
240
|
+
var TypedInput = class {
|
|
241
|
+
constructor(RED, node, input) {
|
|
242
|
+
this.RED = RED;
|
|
243
|
+
this.node = node;
|
|
244
|
+
this.input = input;
|
|
245
|
+
}
|
|
246
|
+
resolvers = {
|
|
247
|
+
// NOTE: NRG nodes are wrapped — surface the NRG instance, fall back to raw NodeRedNode
|
|
248
|
+
node: (raw) => raw?._node ?? raw
|
|
249
|
+
};
|
|
250
|
+
get type() {
|
|
251
|
+
return this.input.type;
|
|
252
|
+
}
|
|
253
|
+
get value() {
|
|
254
|
+
return this.input.value;
|
|
255
|
+
}
|
|
256
|
+
resolve(msg) {
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
this.RED.util.evaluateNodeProperty(
|
|
259
|
+
this.input.value,
|
|
260
|
+
this.input.type,
|
|
261
|
+
this.node,
|
|
262
|
+
msg,
|
|
263
|
+
(err, raw) => {
|
|
264
|
+
if (err) return reject(err);
|
|
265
|
+
const post = this.resolvers[this.input.type];
|
|
266
|
+
resolve(post ? post(raw) : raw);
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
245
273
|
// src/core/server/nodes/utils.ts
|
|
246
274
|
function setupContext(context, store) {
|
|
247
275
|
return {
|
|
@@ -265,14 +293,16 @@ function setupContext(context, store) {
|
|
|
265
293
|
)
|
|
266
294
|
};
|
|
267
295
|
}
|
|
268
|
-
function setupConfigProxy(
|
|
296
|
+
function setupConfigProxy(opts) {
|
|
297
|
+
const { RED, node, config, schema } = opts;
|
|
269
298
|
const SKIP_PROPS = /* @__PURE__ */ new Set(["id", "_id", "_users"]);
|
|
270
299
|
const nodeRefProps = /* @__PURE__ */ new Set();
|
|
300
|
+
const typedInputProps = /* @__PURE__ */ new Set();
|
|
271
301
|
if (schema?.properties) {
|
|
272
302
|
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
303
|
+
const s = propSchema;
|
|
304
|
+
if (s?.["x-nrg-node-type"]) nodeRefProps.add(key);
|
|
305
|
+
if (s?.["x-nrg-typed-input"]) typedInputProps.add(key);
|
|
276
306
|
}
|
|
277
307
|
}
|
|
278
308
|
const cache = /* @__PURE__ */ new WeakMap();
|
|
@@ -280,12 +310,9 @@ function setupConfigProxy(RED, config, schema) {
|
|
|
280
310
|
const cached = cache.get(obj);
|
|
281
311
|
if (cached) return cached;
|
|
282
312
|
if (Array.isArray(obj)) {
|
|
283
|
-
const mapped = obj.map(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
return item;
|
|
288
|
-
});
|
|
313
|
+
const mapped = obj.map(
|
|
314
|
+
(item) => item && typeof item === "object" ? createProxy(item) : item
|
|
315
|
+
);
|
|
289
316
|
cache.set(obj, mapped);
|
|
290
317
|
return mapped;
|
|
291
318
|
}
|
|
@@ -297,6 +324,14 @@ function setupConfigProxy(RED, config, schema) {
|
|
|
297
324
|
if (typeof value === "string" && value.length > 0 && nodeRefProps.has(prop)) {
|
|
298
325
|
return RED.nodes.getNode(value)?._node ?? value;
|
|
299
326
|
}
|
|
327
|
+
if (typedInputProps.has(prop) && value && typeof value === "object" && "type" in value && "value" in value) {
|
|
328
|
+
let ref = cache.get(value);
|
|
329
|
+
if (!ref) {
|
|
330
|
+
ref = new TypedInput(RED, node, value);
|
|
331
|
+
cache.set(value, ref);
|
|
332
|
+
}
|
|
333
|
+
return ref;
|
|
334
|
+
}
|
|
300
335
|
if (value && typeof value === "object") {
|
|
301
336
|
return createProxy(value);
|
|
302
337
|
}
|
|
@@ -358,7 +393,7 @@ var Node = class {
|
|
|
358
393
|
}
|
|
359
394
|
}
|
|
360
395
|
}
|
|
361
|
-
|
|
396
|
+
validator.validate(settings, this.settingsSchema, {
|
|
362
397
|
cacheKey: this.settingsSchema.$id || `${this.type}:settings`,
|
|
363
398
|
throwOnError: true
|
|
364
399
|
});
|
|
@@ -377,7 +412,7 @@ var Node = class {
|
|
|
377
412
|
const constructor = this.constructor;
|
|
378
413
|
if (constructor.configSchema) {
|
|
379
414
|
this.log("Validating configs");
|
|
380
|
-
const configResult =
|
|
415
|
+
const configResult = validator.validate(
|
|
381
416
|
config,
|
|
382
417
|
constructor.configSchema,
|
|
383
418
|
{
|
|
@@ -391,14 +426,15 @@ var Node = class {
|
|
|
391
426
|
);
|
|
392
427
|
}
|
|
393
428
|
}
|
|
394
|
-
this.config = setupConfigProxy(
|
|
429
|
+
this.config = setupConfigProxy({
|
|
395
430
|
RED,
|
|
431
|
+
node,
|
|
396
432
|
config,
|
|
397
|
-
constructor.configSchema
|
|
398
|
-
);
|
|
433
|
+
schema: constructor.configSchema
|
|
434
|
+
});
|
|
399
435
|
if (constructor.credentialsSchema && credentials) {
|
|
400
436
|
this.log("Validating credentials");
|
|
401
|
-
const credResult =
|
|
437
|
+
const credResult = validator.validate(
|
|
402
438
|
credentials,
|
|
403
439
|
constructor.credentialsSchema,
|
|
404
440
|
{
|
|
@@ -438,28 +474,6 @@ var Node = class {
|
|
|
438
474
|
clearInterval(interval);
|
|
439
475
|
this.intervals.delete(interval);
|
|
440
476
|
}
|
|
441
|
-
// NOTE: typing msg isnt necessary in this case
|
|
442
|
-
resolveTypedInput(typedInput, msg) {
|
|
443
|
-
return new Promise((resolve, reject) => {
|
|
444
|
-
this.RED.util.evaluateNodeProperty(
|
|
445
|
-
typedInput.value,
|
|
446
|
-
typedInput.type,
|
|
447
|
-
this.node,
|
|
448
|
-
msg,
|
|
449
|
-
(error, result) => {
|
|
450
|
-
if (error) {
|
|
451
|
-
reject(error);
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
if (typedInput.type === "node" && result) {
|
|
455
|
-
resolve(result._node ?? result);
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
resolve(result);
|
|
459
|
-
}
|
|
460
|
-
);
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
477
|
// NOTE: used by the registered function. Had to be a different one to avoid calling the parent's closed again
|
|
464
478
|
/** @internal */
|
|
465
479
|
async _closed(removed) {
|
|
@@ -545,7 +559,7 @@ var IONode = class extends Node {
|
|
|
545
559
|
const shouldValidateInput = this.config.validateInput ?? NodeClass.validateInput;
|
|
546
560
|
if (shouldValidateInput && NodeClass.inputSchema) {
|
|
547
561
|
this.log("Validating input");
|
|
548
|
-
|
|
562
|
+
validator.validate(msg, NodeClass.inputSchema, {
|
|
549
563
|
cacheKey: NodeClass.inputSchema.$id || `${NodeClass.type}:input-schema`,
|
|
550
564
|
throwOnError: true
|
|
551
565
|
});
|
|
@@ -568,7 +582,7 @@ var IONode = class extends Node {
|
|
|
568
582
|
const msgs = msg;
|
|
569
583
|
for (let i = 0; i < schemas.length; i++) {
|
|
570
584
|
if (msgs[i] == null) continue;
|
|
571
|
-
|
|
585
|
+
validator.validate(msgs[i], schemas[i], {
|
|
572
586
|
cacheKey: schemas[i].$id || `${NodeClass.type}:output-schema:${i}`,
|
|
573
587
|
throwOnError: true
|
|
574
588
|
});
|
|
@@ -576,13 +590,13 @@ var IONode = class extends Node {
|
|
|
576
590
|
} else if (Array.isArray(msg)) {
|
|
577
591
|
for (let i = 0; i < msg.length; i++) {
|
|
578
592
|
if (msg[i] == null) continue;
|
|
579
|
-
|
|
593
|
+
validator.validate(msg[i], schemas, {
|
|
580
594
|
cacheKey: schemas.$id || `${NodeClass.type}:output-schema`,
|
|
581
595
|
throwOnError: true
|
|
582
596
|
});
|
|
583
597
|
}
|
|
584
598
|
} else {
|
|
585
|
-
|
|
599
|
+
validator.validate(msg, schemas, {
|
|
586
600
|
cacheKey: schemas.$id || `${NodeClass.type}:output-schema`,
|
|
587
601
|
throwOnError: true
|
|
588
602
|
});
|
|
@@ -801,16 +815,17 @@ function NodeRef(nodeClass, options) {
|
|
|
801
815
|
[import_typebox.Kind]: "NodeRef"
|
|
802
816
|
};
|
|
803
817
|
}
|
|
804
|
-
function
|
|
818
|
+
function TypedInput2(options) {
|
|
805
819
|
return {
|
|
806
820
|
...TypedInputSchema,
|
|
821
|
+
"x-nrg-typed-input": true,
|
|
807
822
|
...options,
|
|
808
823
|
[import_typebox.Kind]: "TypedInput"
|
|
809
824
|
};
|
|
810
825
|
}
|
|
811
826
|
var SchemaType = Object.assign({}, import_typebox.Type, {
|
|
812
827
|
NodeRef,
|
|
813
|
-
TypedInput
|
|
828
|
+
TypedInput: TypedInput2
|
|
814
829
|
});
|
|
815
830
|
function markNonValidatable(schema) {
|
|
816
831
|
const type = schema.type;
|