@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 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 vue
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 vue
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonsae/nrg",
3
- "version": "0.6.3",
3
+ "version": "0.8.0",
4
4
  "description": "NRG framework — build Node-RED nodes with Vue 3, TypeScript, and JSON Schema",
5
5
  "author": "Allan Oricil <allanoricil@duck.com>",
6
6
  "license": "MIT",
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, validator3]) => {
107
- if (validator3 instanceof RegExp) {
108
- this.ajv.addFormat(name, validator3);
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: validator3 });
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 validator3 = this.ajv.compile(schema);
128
- return validator3;
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 validator3 = this.createValidator(schema, options?.cacheKey);
135
- const valid = validator3(data);
134
+ const validator2 = this.createValidator(schema, options?.cacheKey);
135
+ const valid = validator2(data);
136
136
  if (!valid) {
137
- const errorMessage = this.formatErrors(validator3.errors);
137
+ const errorMessage = this.formatErrors(validator2.errors);
138
138
  if (options?.throwOnError) {
139
- throw new ValidationError(errorMessage, validator3.errors || []);
139
+ throw new ValidationError(errorMessage, validator2.errors || []);
140
140
  }
141
141
  return {
142
142
  valid: false,
143
- errors: validator3.errors || void 0,
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/validator.ts
204
- var NodeRedValidator = class extends Validator {
205
- constructor(RED) {
206
- super({
207
- customKeywords: [
208
- {
209
- keyword: "x-nrg-skip-validation",
210
- schemaType: "boolean",
211
- valid: true
212
- },
213
- {
214
- keyword: "x-nrg-node-type",
215
- type: "string",
216
- validate: (schemaValue, dataValue) => {
217
- if (!dataValue) return true;
218
- const node = RED.nodes.getNode(dataValue);
219
- return node?.type === schemaValue;
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
- var validator2 = void 0;
232
- function initValidator(RED) {
233
- validator2 = new NodeRedValidator(RED);
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(RED, config, schema) {
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
- if (propSchema?.["x-nrg-node-type"]) {
274
- nodeRefProps.add(key);
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((item) => {
284
- if (item && typeof item === "object") {
285
- return createProxy(item);
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
- validator2.validate(settings, this.settingsSchema, {
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 = validator2.validate(
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 = validator2.validate(
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
- validator2.validate(msg, NodeClass.inputSchema, {
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
- validator2.validate(msgs[i], schemas[i], {
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
- validator2.validate(msg[i], schemas, {
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
- validator2.validate(msg, schemas, {
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 TypedInput(options) {
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;