@bonsae/nrg 0.13.0 → 0.14.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
@@ -17,10 +17,10 @@ Build Node-RED nodes with Vue 3, TypeScript, and JSON Schema validation.
17
17
 
18
18
  ```bash
19
19
  pnpm add @bonsae/nrg
20
- pnpm add -D vite vue
20
+ pnpm add -D vite
21
21
  ```
22
22
 
23
- > `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.
23
+ > `vite` is a dev dependency because it is only needed at build time. Vue is included as a dependency of nrg and served automatically at runtime.
24
24
 
25
25
  ## Package Exports
26
26
 
@@ -37,7 +37,7 @@ pnpm add -D vite vue
37
37
  ```bash
38
38
  # In your Node-RED package project
39
39
  pnpm add @bonsae/nrg
40
- pnpm add -D vite vue
40
+ pnpm add -D vite
41
41
  ```
42
42
 
43
43
  **vite.config.ts**
@@ -135,43 +135,6 @@ export default defineModule({
135
135
 
136
136
  See the [consumer template](https://github.com/AllanOricil/node-red-vue-template) for a complete example.
137
137
 
138
- ## Project Structure
139
-
140
- ```
141
- src/
142
- ├── core/ # Runtime framework
143
- │ ├── client/ # Vue 3 editor components
144
- │ │ ├── app.vue # Root form wrapper (validation, toggles)
145
- │ │ ├── components/ # Reusable form inputs
146
- │ │ │ ├── node-red-input.vue
147
- │ │ │ ├── node-red-typed-input.vue
148
- │ │ │ ├── node-red-config-input.vue
149
- │ │ │ ├── node-red-select-input.vue
150
- │ │ │ ├── node-red-editor-input.vue
151
- │ │ │ └── node-red-json-schema-form.vue
152
- │ │ └── index.ts # registerType, defineNode
153
- │ ├── server/ # Node.js server runtime
154
- │ │ ├── nodes/ # Node, IONode, ConfigNode classes
155
- │ │ ├── schemas/ # TypeBox schema system
156
- │ │ ├── types/ # RED, context store types
157
- │ │ └── index.ts # registerTypes, exports
158
- │ ├── constants.ts
159
- │ └── validator.ts # AJV-based validation
160
- ├── test/ # Test utilities for consumers
161
- │ ├── index.ts # createNode, receive, close, reset
162
- │ └── mocks.ts # RED and Node-RED node mocks
163
- ├── vite/ # Build tooling
164
- │ ├── plugin.ts # Vite plugin factory
165
- │ ├── plugins/ # Dev server, build orchestration
166
- │ ├── server/ # Server build (CJS/ESM + bridge)
167
- │ ├── client/ # Client build (Vue + auto-wiring)
168
- │ └── index.ts # nodeRed(), defineRuntimeSettings()
169
- └── tsconfig/ # Shared configs for consumers
170
- ├── base.json
171
- ├── client.json
172
- └── server.json
173
- ```
174
-
175
138
  ## Testing
176
139
 
177
140
  Test your nodes' server-side logic with `@bonsae/nrg/test`:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bonsae/nrg",
3
- "version": "0.13.0",
3
+ "version": "0.14.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",
@@ -62,6 +62,7 @@
62
62
  "tree-kill": "^1.2.2",
63
63
  "typescript": "^5.8.3",
64
64
  "vite-plugin-dts": "^4.5.4",
65
- "vite-plugin-static-copy": "^3.1.0"
65
+ "vite-plugin-static-copy": "^3.1.0",
66
+ "vue": "^3.5.14"
66
67
  }
67
68
  }
package/server/index.cjs CHANGED
@@ -39,7 +39,6 @@ __export(index_exports, {
39
39
  defineIONode: () => defineIONode,
40
40
  defineModule: () => defineModule,
41
41
  defineSchema: () => defineSchema,
42
- initValidator: () => initValidator,
43
42
  registerType: () => registerType,
44
43
  registerTypes: () => registerTypes
45
44
  });
@@ -60,174 +59,6 @@ function getCredentialsFromSchema(schema) {
60
59
  return result;
61
60
  }
62
61
 
63
- // src/core/validator.ts
64
- var import_ajv = __toESM(require("ajv"), 1);
65
- var import_ajv_formats = __toESM(require("ajv-formats"), 1);
66
- var import_ajv_errors = __toESM(require("ajv-errors"), 1);
67
- var Validator = class {
68
- ajv;
69
- constructor(options) {
70
- const { customKeywords, customFormats, ...ajvOptions } = options || {};
71
- this.ajv = new import_ajv.default({
72
- allErrors: true,
73
- code: {
74
- source: false
75
- },
76
- coerceTypes: true,
77
- removeAdditional: false,
78
- strict: false,
79
- strictSchema: false,
80
- useDefaults: true,
81
- validateFormats: true,
82
- // NOTE: typebox handles validation via typescript
83
- // NOTE: if true, types that are not serializable JSON, like Function, would not work
84
- validateSchema: false,
85
- verbose: true,
86
- ...ajvOptions
87
- });
88
- (0, import_ajv_formats.default)(this.ajv);
89
- (0, import_ajv_errors.default)(this.ajv);
90
- this.addCustomKeywords(customKeywords || []);
91
- this.addCustomFormats(customFormats || {});
92
- }
93
- /**
94
- * Add custom keywords to the validator
95
- */
96
- addCustomKeywords(keywords) {
97
- if (!keywords) return;
98
- keywords.forEach((keyword) => {
99
- this.ajv.addKeyword(keyword);
100
- });
101
- }
102
- /**
103
- * Add custom formats to the validator
104
- */
105
- addCustomFormats(formats) {
106
- if (!formats) return;
107
- Object.entries(formats).forEach(([name, validator2]) => {
108
- if (validator2 instanceof RegExp) {
109
- this.ajv.addFormat(name, validator2);
110
- } else {
111
- this.ajv.addFormat(name, { validate: validator2 });
112
- }
113
- });
114
- }
115
- /**
116
- * Create a validator function with caching
117
- * @param schema - JSON Schema to validate against
118
- * @param cacheKey - Optional cache key for reusing validators
119
- */
120
- createValidator(schema, cacheKey) {
121
- if (cacheKey && !schema.$id) {
122
- schema.$id = cacheKey;
123
- }
124
- if (schema.$id) {
125
- const cached = this.ajv.getSchema(schema.$id);
126
- if (cached) return cached;
127
- }
128
- const validator2 = this.ajv.compile(schema);
129
- return validator2;
130
- }
131
- /**
132
- * Validate data against a schema and return a structured result
133
- */
134
- validate(data, schema, options) {
135
- const validator2 = this.createValidator(schema, options?.cacheKey);
136
- const valid = validator2(data);
137
- if (!valid) {
138
- const errorMessage = this.formatErrors(validator2.errors);
139
- if (options?.throwOnError) {
140
- throw new ValidationError(errorMessage, validator2.errors || []);
141
- }
142
- return {
143
- valid: false,
144
- errors: validator2.errors || void 0,
145
- errorMessage
146
- };
147
- }
148
- return {
149
- valid: true,
150
- data
151
- };
152
- }
153
- /**
154
- * Format errors into a human-readable string
155
- */
156
- formatErrors(errors, options) {
157
- if (!errors || errors.length === 0) {
158
- return "No errors";
159
- }
160
- return this.ajv.errorsText(errors, {
161
- separator: "; ",
162
- dataVar: "data",
163
- ...options
164
- });
165
- }
166
- /**
167
- * Get detailed error information
168
- */
169
- getDetailedErrors(errors) {
170
- if (!errors || errors.length === 0) return [];
171
- return errors.map((error) => ({
172
- field: error.instancePath || "/",
173
- message: error.message || "Validation failed",
174
- keyword: error.keyword,
175
- params: error.params,
176
- schemaPath: error.schemaPath
177
- }));
178
- }
179
- /**
180
- * Add a schema to the validator for reference
181
- */
182
- addSchema(schema, key) {
183
- this.ajv.addSchema(schema, key);
184
- return this;
185
- }
186
- /**
187
- * Remove a schema from the validator
188
- */
189
- removeSchema(key) {
190
- this.ajv.removeSchema(key);
191
- return this;
192
- }
193
- };
194
- var ValidationError = class _ValidationError extends Error {
195
- constructor(message, errors) {
196
- super(message);
197
- this.errors = errors;
198
- this.name = "ValidationError";
199
- Object.setPrototypeOf(this, _ValidationError.prototype);
200
- }
201
- };
202
-
203
- // src/core/server/validation.ts
204
- var validator = void 0;
205
- function initValidator(RED) {
206
- validator = new Validator({
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
- }
221
- }
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
- }
228
- });
229
- }
230
-
231
62
  // src/core/errors.ts
232
63
  var NrgError = class _NrgError extends Error {
233
64
  constructor(message) {
@@ -402,7 +233,7 @@ var Node = class {
402
233
  }
403
234
  }
404
235
  }
405
- validator.validate(settings, this.settingsSchema, {
236
+ RED.validator.validate(settings, this.settingsSchema, {
406
237
  cacheKey: this.settingsSchema.$id || `${this.type}:settings`,
407
238
  throwOnError: true
408
239
  });
@@ -421,7 +252,7 @@ var Node = class {
421
252
  const constructor = this.constructor;
422
253
  if (constructor.configSchema) {
423
254
  this.log("Validating configs");
424
- const configResult = validator.validate(
255
+ const configResult = this.RED.validator.validate(
425
256
  config,
426
257
  constructor.configSchema,
427
258
  {
@@ -443,7 +274,7 @@ var Node = class {
443
274
  });
444
275
  if (constructor.credentialsSchema && credentials) {
445
276
  this.log("Validating credentials");
446
- const credResult = validator.validate(
277
+ const credResult = this.RED.validator.validate(
447
278
  credentials,
448
279
  constructor.credentialsSchema,
449
280
  {
@@ -572,7 +403,7 @@ var IONode = class extends Node {
572
403
  const shouldValidateInput = this.config.validateInput ?? NodeClass.validateInput;
573
404
  if (shouldValidateInput && NodeClass.inputSchema) {
574
405
  this.log("Validating input");
575
- validator.validate(msg, NodeClass.inputSchema, {
406
+ this.RED.validator.validate(msg, NodeClass.inputSchema, {
576
407
  cacheKey: NodeClass.inputSchema.$id || `${NodeClass.type}:input-schema`,
577
408
  throwOnError: true
578
409
  });
@@ -595,7 +426,7 @@ var IONode = class extends Node {
595
426
  const msgs = msg;
596
427
  for (let i = 0; i < schemas.length; i++) {
597
428
  if (msgs[i] == null) continue;
598
- validator.validate(msgs[i], schemas[i], {
429
+ this.RED.validator.validate(msgs[i], schemas[i], {
599
430
  cacheKey: schemas[i].$id || `${NodeClass.type}:output-schema:${i}`,
600
431
  throwOnError: true
601
432
  });
@@ -603,13 +434,13 @@ var IONode = class extends Node {
603
434
  } else if (Array.isArray(msg)) {
604
435
  for (let i = 0; i < msg.length; i++) {
605
436
  if (msg[i] == null) continue;
606
- validator.validate(msg[i], schemas, {
437
+ this.RED.validator.validate(msg[i], schemas, {
607
438
  cacheKey: schemas.$id || `${NodeClass.type}:output-schema`,
608
439
  throwOnError: true
609
440
  });
610
441
  }
611
442
  } else {
612
- validator.validate(msg, schemas, {
443
+ this.RED.validator.validate(msg, schemas, {
613
444
  cacheKey: schemas.$id || `${NodeClass.type}:output-schema`,
614
445
  throwOnError: true
615
446
  });
@@ -635,31 +466,40 @@ var IONode = class extends Node {
635
466
  if (this.config.emitStatus) count++;
636
467
  return count;
637
468
  }
469
+ /**
470
+ * Send a message to a specific output port by index or name.
471
+ * Named ports: `"error"`, `"complete"`, `"status"` — resolved automatically
472
+ * based on the node's emit port configuration.
473
+ * Numeric indices refer to the base output ports (0-based).
474
+ */
475
+ sendToPort(port, msg) {
476
+ this._sendToPort(port, msg);
477
+ }
638
478
  /** @internal */
639
- _sendToPort(portIndex, msg) {
479
+ _sendToPort(port, msg) {
480
+ let portIndex;
481
+ if (typeof port === "number") {
482
+ portIndex = port;
483
+ } else {
484
+ portIndex = this._getEmitPortIndex(port);
485
+ if (portIndex === null) return;
486
+ }
640
487
  const out = Array(this._totalOutputs).fill(null);
641
488
  out[portIndex] = msg;
642
489
  this.node.send(out);
643
490
  }
644
- /** @internal */
645
- _getErrorPortIndex() {
646
- if (!this.config.emitError) return null;
647
- return this._baseOutputs;
648
- }
649
- /** @internal */
650
- _getCompletePortIndex() {
651
- if (!this.config.emitComplete) return null;
652
- let idx = this._baseOutputs;
653
- if (this.config.emitError) idx++;
654
- return idx;
655
- }
656
- /** @internal */
657
- _getStatusPortIndex() {
658
- if (!this.config.emitStatus) return null;
491
+ _getEmitPortIndex(name) {
492
+ const config = this.config;
493
+ if (name === "error") {
494
+ return config.emitError ? this._baseOutputs : null;
495
+ }
659
496
  let idx = this._baseOutputs;
660
- if (this.config.emitError) idx++;
661
- if (this.config.emitComplete) idx++;
662
- return idx;
497
+ if (config.emitError) idx++;
498
+ if (name === "complete") {
499
+ return config.emitComplete ? idx : null;
500
+ }
501
+ if (config.emitComplete) idx++;
502
+ return config.emitStatus ? idx : null;
663
503
  }
664
504
  _nodeSource() {
665
505
  return {
@@ -670,19 +510,15 @@ var IONode = class extends Node {
670
510
  }
671
511
  status(status) {
672
512
  this.node.status(status);
673
- const portIdx = this._getStatusPortIndex();
674
- if (portIdx !== null) {
675
- this._sendToPort(portIdx, {
676
- status,
677
- source: this._nodeSource()
678
- });
679
- }
513
+ this._sendToPort("status", {
514
+ status,
515
+ source: this._nodeSource()
516
+ });
680
517
  }
681
518
  error(message, msg) {
682
519
  super.error(message, msg);
683
- const portIdx = this._getErrorPortIndex();
684
- if (portIdx !== null && msg) {
685
- this._sendToPort(portIdx, {
520
+ if (msg) {
521
+ this._sendToPort("error", {
686
522
  ...msg,
687
523
  error: {
688
524
  message,
@@ -815,29 +651,208 @@ function defineConfigNode(def) {
815
651
  return NodeClass;
816
652
  }
817
653
 
818
- // src/core/server/api/nrg-assets.ts
654
+ // src/core/validator.ts
655
+ var import_ajv = __toESM(require("ajv"), 1);
656
+ var import_ajv_formats = __toESM(require("ajv-formats"), 1);
657
+ var import_ajv_errors = __toESM(require("ajv-errors"), 1);
658
+ var Validator = class {
659
+ ajv;
660
+ constructor(options) {
661
+ const { customKeywords, customFormats, ...ajvOptions } = options || {};
662
+ this.ajv = new import_ajv.default({
663
+ allErrors: true,
664
+ code: {
665
+ source: false
666
+ },
667
+ coerceTypes: true,
668
+ removeAdditional: false,
669
+ strict: false,
670
+ strictSchema: false,
671
+ useDefaults: true,
672
+ validateFormats: true,
673
+ // NOTE: typebox handles validation via typescript
674
+ // NOTE: if true, types that are not serializable JSON, like Function, would not work
675
+ validateSchema: false,
676
+ verbose: true,
677
+ ...ajvOptions
678
+ });
679
+ (0, import_ajv_formats.default)(this.ajv);
680
+ (0, import_ajv_errors.default)(this.ajv);
681
+ this.addCustomKeywords(customKeywords || []);
682
+ this.addCustomFormats(customFormats || {});
683
+ }
684
+ /**
685
+ * Add custom keywords to the validator
686
+ */
687
+ addCustomKeywords(keywords) {
688
+ if (!keywords) return;
689
+ keywords.forEach((keyword) => {
690
+ this.ajv.addKeyword(keyword);
691
+ });
692
+ }
693
+ /**
694
+ * Add custom formats to the validator
695
+ */
696
+ addCustomFormats(formats) {
697
+ if (!formats) return;
698
+ Object.entries(formats).forEach(([name, validator]) => {
699
+ if (validator instanceof RegExp) {
700
+ this.ajv.addFormat(name, validator);
701
+ } else {
702
+ this.ajv.addFormat(name, { validate: validator });
703
+ }
704
+ });
705
+ }
706
+ /**
707
+ * Create a validator function with caching
708
+ * @param schema - JSON Schema to validate against
709
+ * @param cacheKey - Optional cache key for reusing validators
710
+ */
711
+ createValidator(schema, cacheKey) {
712
+ if (cacheKey && !schema.$id) {
713
+ schema.$id = cacheKey;
714
+ }
715
+ if (schema.$id) {
716
+ const cached = this.ajv.getSchema(schema.$id);
717
+ if (cached) return cached;
718
+ }
719
+ const validator = this.ajv.compile(schema);
720
+ return validator;
721
+ }
722
+ /**
723
+ * Validate data against a schema and return a structured result
724
+ */
725
+ validate(data, schema, options) {
726
+ const validator = this.createValidator(schema, options?.cacheKey);
727
+ const valid = validator(data);
728
+ if (!valid) {
729
+ const errorMessage = this.formatErrors(validator.errors);
730
+ if (options?.throwOnError) {
731
+ throw new ValidationError(errorMessage, validator.errors || []);
732
+ }
733
+ return {
734
+ valid: false,
735
+ errors: validator.errors || void 0,
736
+ errorMessage
737
+ };
738
+ }
739
+ return {
740
+ valid: true,
741
+ data
742
+ };
743
+ }
744
+ /**
745
+ * Format errors into a human-readable string
746
+ */
747
+ formatErrors(errors, options) {
748
+ if (!errors || errors.length === 0) {
749
+ return "No errors";
750
+ }
751
+ return this.ajv.errorsText(errors, {
752
+ separator: "; ",
753
+ dataVar: "data",
754
+ ...options
755
+ });
756
+ }
757
+ /**
758
+ * Get detailed error information
759
+ */
760
+ getDetailedErrors(errors) {
761
+ if (!errors || errors.length === 0) return [];
762
+ return errors.map((error) => ({
763
+ field: error.instancePath || "/",
764
+ message: error.message || "Validation failed",
765
+ keyword: error.keyword,
766
+ params: error.params,
767
+ schemaPath: error.schemaPath
768
+ }));
769
+ }
770
+ /**
771
+ * Add a schema to the validator for reference
772
+ */
773
+ addSchema(schema, key) {
774
+ this.ajv.addSchema(schema, key);
775
+ return this;
776
+ }
777
+ /**
778
+ * Remove a schema from the validator
779
+ */
780
+ removeSchema(key) {
781
+ this.ajv.removeSchema(key);
782
+ return this;
783
+ }
784
+ };
785
+ var ValidationError = class _ValidationError extends Error {
786
+ constructor(message, errors) {
787
+ super(message);
788
+ this.errors = errors;
789
+ this.name = "ValidationError";
790
+ Object.setPrototypeOf(this, _ValidationError.prototype);
791
+ }
792
+ };
793
+
794
+ // src/core/server/validation.ts
795
+ function initValidator(RED) {
796
+ const nrg = {
797
+ validator: new Validator({
798
+ customKeywords: [
799
+ {
800
+ keyword: "x-nrg-skip-validation",
801
+ schemaType: "boolean",
802
+ valid: true
803
+ },
804
+ {
805
+ keyword: "x-nrg-node-type",
806
+ type: "string",
807
+ validate: (schemaValue, dataValue) => {
808
+ if (!dataValue) return true;
809
+ const node = RED.nodes.getNode(dataValue);
810
+ return node?.type === schemaValue;
811
+ }
812
+ }
813
+ ],
814
+ customFormats: {
815
+ "node-id": /^[a-zA-Z0-9-_]+$/,
816
+ "flow-id": /^[a-f0-9]{16}$/,
817
+ "topic-path": (data) => /^[a-zA-Z0-9/_-]+$/.test(data)
818
+ }
819
+ })
820
+ };
821
+ Object.defineProperty(RED, "_nrg", {
822
+ value: nrg,
823
+ writable: false,
824
+ enumerable: false,
825
+ configurable: false
826
+ });
827
+ Object.defineProperty(RED, "validator", {
828
+ get: () => nrg.validator,
829
+ enumerable: false,
830
+ configurable: false
831
+ });
832
+ }
833
+
834
+ // src/core/server/api/assets.ts
819
835
  var import_path = __toESM(require("path"), 1);
820
836
  var import_fs = __toESM(require("fs"), 1);
821
- var RESOURCES_DIR = import_path.default.resolve(__dirname, "./resources");
822
- var ALLOWED_FILES = /* @__PURE__ */ new Set([
823
- "nrg-client.js",
824
- "vue.esm-browser.prod.js",
825
- "vue.esm-browser.js"
826
- ]);
827
- var handleNrgAsset = (req, res, next) => {
828
- let fileName = req.params[0];
829
- if (fileName === "vue.esm-browser.prod.js" && process.env.NODE_ENV !== "production") {
830
- fileName = "vue.esm-browser.js";
831
- }
832
- if (!ALLOWED_FILES.has(fileName)) return next();
833
- const filePath = import_path.default.join(RESOURCES_DIR, fileName);
834
- if (!import_fs.default.existsSync(filePath)) return next();
835
- res.setHeader("Content-Type", "application/javascript");
836
- import_fs.default.createReadStream(filePath).pipe(res);
837
- };
838
- function initNrgAssetsRoute(router) {
839
- if (!import_fs.default.existsSync(RESOURCES_DIR)) return;
840
- router.get("/nrg/assets/*", handleNrgAsset);
837
+ var import_module = require("module");
838
+ function serveFile(filePath) {
839
+ return (_req, res, next) => {
840
+ if (!import_fs.default.existsSync(filePath)) return next();
841
+ res.setHeader("Content-Type", "application/javascript");
842
+ import_fs.default.createReadStream(filePath).pipe(res);
843
+ };
844
+ }
845
+ function initAssetsRoutes(router) {
846
+ const resourcesDir = import_path.default.resolve(__dirname, "./resources");
847
+ if (!import_fs.default.existsSync(resourcesDir)) return;
848
+ const _require = (0, import_module.createRequire)(import_path.default.join(__dirname, "package.json"));
849
+ const vueFile = process.env.NODE_ENV !== "production" ? _require.resolve("vue/dist/vue.esm-browser.js") : _require.resolve("vue/dist/vue.esm-browser.prod.js");
850
+ router.get(
851
+ "/nrg/assets/nrg-client.js",
852
+ serveFile(import_path.default.join(resourcesDir, "nrg-client.js"))
853
+ );
854
+ router.get("/nrg/assets/vue.esm-browser.prod.js", serveFile(vueFile));
855
+ router.get("/nrg/assets/vue.esm-browser.js", serveFile(vueFile));
841
856
  }
842
857
 
843
858
  // src/core/server/api/routes.ts
@@ -845,7 +860,7 @@ var _initialized = false;
845
860
  function initRoutes(RED) {
846
861
  if (_initialized) return;
847
862
  _initialized = true;
848
- initNrgAssetsRoute(RED.httpAdmin);
863
+ initAssetsRoutes(RED.httpAdmin);
849
864
  }
850
865
 
851
866
  // src/core/constants.ts
@@ -1025,15 +1040,12 @@ async function registerType(RED, NodeClass) {
1025
1040
  try {
1026
1041
  this.log("Calling input");
1027
1042
  await Promise.resolve(node._input(msg, send));
1028
- const completeIdx = node._getCompletePortIndex();
1029
- if (completeIdx !== null) {
1030
- node._sendToPort(completeIdx, {
1031
- ...msg,
1032
- complete: {
1033
- source: { id: node.id, type: NC.type, name: node.name }
1034
- }
1035
- });
1036
- }
1043
+ node._sendToPort("complete", {
1044
+ ...msg,
1045
+ complete: {
1046
+ source: { id: node.id, type: NC.type, name: node.name }
1047
+ }
1048
+ });
1037
1049
  done();
1038
1050
  this.log("Input processed");
1039
1051
  } catch (error) {
@@ -1118,7 +1130,6 @@ function defineModule(definition) {
1118
1130
  defineIONode,
1119
1131
  defineModule,
1120
1132
  defineSchema,
1121
- initValidator,
1122
1133
  registerType,
1123
1134
  registerTypes
1124
1135
  });