@theotherwillembotha/node-red-plugincore 0.1.0 → 0.2.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.
Files changed (134) hide show
  1. package/README.md +616 -220
  2. package/build/GenerateNodes.js +3 -6
  3. package/build/Nodes.html +3 -1504
  4. package/build/Nodes.js +5 -15
  5. package/build/Plugins.html +227 -0
  6. package/build/Plugins.js +18 -8
  7. package/build/core/NodeConstructor.d.ts +9 -16
  8. package/build/core/NodeConstructor.d.ts.map +1 -1
  9. package/build/core/NodeConstructor.js +109 -146
  10. package/build/core/NodeGenerator.d.ts.map +1 -1
  11. package/build/core/NodeGenerator.js +79 -28
  12. package/build/core/NodeManagerRuntime.d.ts +44 -0
  13. package/build/core/NodeManagerRuntime.d.ts.map +1 -0
  14. package/build/core/NodeManagerRuntime.js +140 -0
  15. package/build/core/Utils.d.ts.map +1 -1
  16. package/build/core/logger/service/LoggerService.d.ts +6 -5
  17. package/build/core/logger/service/LoggerService.d.ts.map +1 -1
  18. package/build/core/logger/service/LoggerService.js +51 -20
  19. package/build/core/logger/template/LoggerTemplate.d.ts.map +1 -1
  20. package/build/core/metrics/MetricsDecorator.d.ts +1 -2
  21. package/build/core/metrics/MetricsDecorator.d.ts.map +1 -1
  22. package/build/core/metrics/MetricsDecorator.js +89 -116
  23. package/build/core/metrics/node/CounterMetricConfigNode.d.ts.map +1 -1
  24. package/build/core/metrics/node/CounterMetricConfigNode.js +4 -5
  25. package/build/core/metrics/node/GaugeMetricConfigNode.d.ts.map +1 -1
  26. package/build/core/metrics/node/GaugeMetricConfigNode.js +5 -6
  27. package/build/core/metrics/node/MetricsConfigNode.d.ts +19 -9
  28. package/build/core/metrics/node/MetricsConfigNode.d.ts.map +1 -1
  29. package/build/core/metrics/node/MetricsConfigNode.js +14 -45
  30. package/build/core/metrics/node/TimerMetricConfigNode.d.ts.map +1 -1
  31. package/build/core/metrics/node/TimerMetricConfigNode.js +11 -17
  32. package/build/core/metrics/service/MetricsService.d.ts +110 -114
  33. package/build/core/metrics/service/MetricsService.d.ts.map +1 -1
  34. package/build/core/metrics/service/MetricsService.js +122 -339
  35. package/build/core/metrics/template/CounterMetricTemplate.d.ts.map +1 -1
  36. package/build/core/metrics/template/GaugeMetricTemplate.d.ts.map +1 -1
  37. package/build/core/metrics/template/MetricsTemplate.d.ts.map +1 -1
  38. package/build/core/metrics/template/MetricsTemplate.js +1 -2
  39. package/build/core/metrics/template/TimerMetricTemplate.d.ts.map +1 -1
  40. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts +1 -1
  41. package/build/core/other/node/DelegatedConfigReferenceNode.d.ts.map +1 -1
  42. package/build/core/other/node/DelegatedConfigReferenceNode.js +1 -1
  43. package/build/core/other/service/InputService.d.ts.map +1 -1
  44. package/build/core/other/service/SettingsService.d.ts +1 -2
  45. package/build/core/other/service/SettingsService.d.ts.map +1 -1
  46. package/build/core/other/service/SettingsService.js +19 -5
  47. package/build/core/other/template/BasicTemplate.d.ts.map +1 -1
  48. package/build/core/other/template/SettingsTemplate.d.ts.map +1 -1
  49. package/build/core/state/node/InternalStateConfigNode.d.ts +16 -0
  50. package/build/core/state/node/InternalStateConfigNode.d.ts.map +1 -0
  51. package/build/core/state/node/InternalStateConfigNode.js +89 -0
  52. package/build/core/state/node/StateConfigNode.d.ts +24 -0
  53. package/build/core/state/node/StateConfigNode.d.ts.map +1 -0
  54. package/build/core/state/node/StateConfigNode.js +22 -0
  55. package/build/core/state/service/StateService.d.ts +34 -0
  56. package/build/core/state/service/StateService.d.ts.map +1 -0
  57. package/build/core/state/service/StateService.js +201 -0
  58. package/build/core/state/template/StateTemplate.d.ts +8 -0
  59. package/build/core/state/template/StateTemplate.d.ts.map +1 -0
  60. package/build/core/state/template/StateTemplate.js +11 -0
  61. package/build/core/tagging/ServiceDescriptionDecorator.d.ts +10 -0
  62. package/build/core/tagging/ServiceDescriptionDecorator.d.ts.map +1 -0
  63. package/build/core/tagging/ServiceDescriptionDecorator.js +10 -0
  64. package/build/core/tagging/service/NodeTypeService.d.ts +1 -2
  65. package/build/core/tagging/service/NodeTypeService.d.ts.map +1 -1
  66. package/build/core/tagging/service/NodeTypeService.js +19 -5
  67. package/build/core/ui/template/ScriptEditorTemplate.d.ts.map +1 -1
  68. package/build/core/ui/template/UIHelperTemplate.d.ts.map +1 -1
  69. package/build/core/webhook/node/WebhookServerConfigNode.d.ts.map +1 -1
  70. package/build/core/webhook/service/ReverseProxyTypeService.d.ts.map +1 -1
  71. package/build/core/webhook/service/WebhookServerService.d.ts +3 -4
  72. package/build/core/webhook/service/WebhookServerService.d.ts.map +1 -1
  73. package/build/core/webhook/service/WebhookServerService.js +48 -18
  74. package/build/core/webhook/template/WebhookTemplate.d.ts.map +1 -1
  75. package/build/index.d.ts +5 -2
  76. package/build/index.d.ts.map +1 -1
  77. package/build/index.js +6 -2
  78. package/build/runtime/NodeManagerRuntime.js +140 -0
  79. package/documentation/CounterMetricConfigNode.png +0 -0
  80. package/documentation/GaugeMetricConfigNode.png +0 -0
  81. package/documentation/LoggerTemplate.png +0 -0
  82. package/documentation/MetricsTemplate.png +0 -0
  83. package/documentation/StateTemplate.png +0 -0
  84. package/documentation/TimerMetricConfigNode_histogram.png +0 -0
  85. package/documentation/TimerMetricConfigNode_summary.png +0 -0
  86. package/documentation/WebhookServerConfigNode.png +0 -0
  87. package/documentation/WebhookTemplate.png +0 -0
  88. package/package.json +4 -10
  89. package/src/GenerateNodes.ts +7 -11
  90. package/src/core/NodeConstructor.ts +130 -186
  91. package/src/core/NodeGenerator.ts +88 -33
  92. package/src/core/NodeManagerRuntime.ts +179 -0
  93. package/src/core/logger/service/LoggerService.ts +39 -26
  94. package/src/core/logger/template/LoggerTemplate.html +11 -6
  95. package/src/core/metrics/MetricsDecorator.ts +140 -168
  96. package/src/core/metrics/node/CounterMetricConfigNode.ts +20 -20
  97. package/src/core/metrics/node/GaugeMetricConfigNode.ts +24 -25
  98. package/src/core/metrics/node/MetricsConfigNode.ts +29 -48
  99. package/src/core/metrics/node/TimerMetricConfigNode.ts +50 -59
  100. package/src/core/metrics/service/MetricsService.ts +214 -489
  101. package/src/core/metrics/template/CounterMetricTemplate.html +0 -1
  102. package/src/core/metrics/template/GaugeMetricTemplate.html +1 -1
  103. package/src/core/metrics/template/MetricsTemplate.html +132 -13
  104. package/src/core/metrics/template/MetricsTemplate.ts +3 -4
  105. package/src/core/other/node/DelegatedConfigReferenceNode.ts +1 -1
  106. package/src/core/other/service/SettingsService.ts +6 -10
  107. package/src/core/other/template/BasicTemplate.html +1 -1
  108. package/src/core/state/node/InternalStateConfigNode.html +77 -0
  109. package/src/core/state/node/InternalStateConfigNode.ts +102 -0
  110. package/src/core/state/node/StateConfigNode.ts +29 -0
  111. package/src/core/state/service/StateService.ts +180 -0
  112. package/src/core/state/template/StateTemplate.html +244 -0
  113. package/src/core/state/template/StateTemplate.ts +17 -0
  114. package/src/core/tagging/ServiceDescriptionDecorator.ts +23 -0
  115. package/src/core/tagging/service/NodeTypeService.ts +6 -11
  116. package/src/core/ui/template/UIHelperTemplate.html +41 -26
  117. package/src/core/webhook/service/WebhookServerService.ts +39 -23
  118. package/src/core/webhook/template/WebhookTemplate.html +28 -35
  119. package/src/index.ts +7 -2
  120. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts +0 -20
  121. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts.map +0 -1
  122. package/build/core/logger/node/ConsoleLoggerConfigNode.js +0 -62
  123. package/build/core/logger/node/RestLoggerConfigNode.d.ts +0 -40
  124. package/build/core/logger/node/RestLoggerConfigNode.d.ts.map +0 -1
  125. package/build/core/logger/node/RestLoggerConfigNode.js +0 -128
  126. package/documentation/ConsoleLoggerConfigNode.png +0 -0
  127. package/documentation/MetricsConfigNode.png +0 -0
  128. package/documentation/RestLoggerConfigNode.png +0 -0
  129. package/documentation/TimerMetricConfigNode.png +0 -0
  130. package/documentation/WebhookNode.png +0 -0
  131. package/src/core/logger/node/ConsoleLoggerConfigNode.html +0 -90
  132. package/src/core/logger/node/ConsoleLoggerConfigNode.ts +0 -71
  133. package/src/core/logger/node/RestLoggerConfigNode.html +0 -183
  134. package/src/core/logger/node/RestLoggerConfigNode.ts +0 -170
@@ -3,14 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.buildNode = exports.NodeBuilder = exports.BaseService = exports.NodeManager = exports.Template = exports.ServiceDescriptor = exports.TemplateDescriptor = exports.NodeDescriptor = exports.ConfigNode = exports.BaseNode = void 0;
6
+ exports.buildNodeBuilder = exports.buildNode = exports.NodeBuilder = exports.BaseService = exports.Template = exports.ServiceDescriptor = exports.TemplateDescriptor = exports.NodeDescriptor = exports.ConfigNode = exports.BaseNode = exports.NodeManager = exports.runPostConstructInitializers = exports.POST_CONSTRUCT_KEY = exports.NODEMANAGER_API_VERSION = void 0;
7
7
  exports.createPostConstructDecorator = createPostConstructDecorator;
8
8
  require("reflect-metadata");
9
- const jsdom_1 = __importDefault(require("jsdom"));
10
9
  const fs_1 = __importDefault(require("fs"));
11
- const js_beautify_1 = __importDefault(require("js-beautify"));
12
- const markdown_it_1 = __importDefault(require("markdown-it"));
13
- const handlebars_1 = __importDefault(require("handlebars"));
10
+ const NodeManagerRuntime_1 = require("./NodeManagerRuntime");
11
+ Object.defineProperty(exports, "NODEMANAGER_API_VERSION", { enumerable: true, get: function () { return NodeManagerRuntime_1.NODEMANAGER_API_VERSION; } });
12
+ Object.defineProperty(exports, "POST_CONSTRUCT_KEY", { enumerable: true, get: function () { return NodeManagerRuntime_1.POST_CONSTRUCT_KEY; } });
13
+ Object.defineProperty(exports, "runPostConstructInitializers", { enumerable: true, get: function () { return NodeManagerRuntime_1.runPostConstructInitializers; } });
14
+ Object.defineProperty(exports, "NodeManager", { enumerable: true, get: function () { return NodeManagerRuntime_1.NodeManager; } });
14
15
  class BaseNode {
15
16
  node() { return this._node; }
16
17
  config() { return this._config; }
@@ -135,7 +136,6 @@ class Template {
135
136
  }
136
137
  }
137
138
  exports.Template = Template;
138
- const POST_CONSTRUCT_KEY = Symbol('postConstructInitializers');
139
139
  // Builder class for post-construct decorators
140
140
  class PostConstructDecoratorBuilder {
141
141
  constructor(decoratorName) {
@@ -201,10 +201,10 @@ class PostConstructDecoratorBuilder {
201
201
  ` Issue: Method returned a value (${typeof result}) but should return void or Promise<void>.`);
202
202
  }
203
203
  };
204
- if (!target[POST_CONSTRUCT_KEY]) {
205
- target[POST_CONSTRUCT_KEY] = [];
204
+ if (!target[NodeManagerRuntime_1.POST_CONSTRUCT_KEY]) {
205
+ target[NodeManagerRuntime_1.POST_CONSTRUCT_KEY] = [];
206
206
  }
207
- target[POST_CONSTRUCT_KEY].push((instance) => {
207
+ target[NodeManagerRuntime_1.POST_CONSTRUCT_KEY].push((instance) => {
208
208
  if (initLogic) {
209
209
  try {
210
210
  initLogic(instance, propertyKey, config);
@@ -245,117 +245,6 @@ class PostConstructDecoratorBuilder {
245
245
  function createPostConstructDecorator(decoratorName) {
246
246
  return new PostConstructDecoratorBuilder(decoratorName);
247
247
  }
248
- // Single function to run all post-construct initializers
249
- let runPostConstructInitializers = function (instance) {
250
- const initializers = instance[POST_CONSTRUCT_KEY];
251
- if (initializers && initializers.length > 0) {
252
- const className = instance.constructor.name;
253
- //console.log(`Running ${initializers.length} post-construct initializers for ${className}`);
254
- initializers.forEach((init, index) => {
255
- try {
256
- init(instance);
257
- }
258
- catch (error) {
259
- console.error(`Post-construct initialization failed:\n` +
260
- ` Class: ${className}\n` +
261
- ` Initializer: ${index + 1}/${initializers.length}\n` +
262
- ` Error: ${error instanceof Error ? error.message : String(error)}`);
263
- throw error;
264
- }
265
- });
266
- // Clear the initializers after running
267
- delete instance[POST_CONSTRUCT_KEY];
268
- }
269
- };
270
- // ************************************************
271
- class NodeManager {
272
- static { this.PLUGINID = "node-red-contrib-theotherwillembotha/services-plugin"; }
273
- constructor(RED) {
274
- this.typeBacklog = [];
275
- NodeManager._RED = RED;
276
- let nodeTypeServiceListener = async (pluginID) => {
277
- if (pluginID === "@theotherwillembotha/nodetypeservice") {
278
- // disable the listener- the plugin has been installed.
279
- RED.events.off('plugin.instantiated', nodeTypeServiceListener);
280
- // get a handle on the NodeTypeService
281
- let plugin = RED.plugins.get("@theotherwillembotha/nodetypeservice");
282
- this.nodeTypeService = RED.plugins.get("@theotherwillembotha/nodetypeservice").instance;
283
- // process the backlog.
284
- for (let nodeType of this.typeBacklog) {
285
- for (let tag of nodeType.getNodeDescriptor().tags()) {
286
- this.nodeTypeService.registerNodeType(tag, nodeType);
287
- }
288
- }
289
- }
290
- };
291
- // check if the reverserproxyttypesewrvice is installed yet.
292
- this.nodeTypeService = RED.plugins.get("@theotherwillembotha/nodetypeservice").instance;
293
- if (!this.nodeTypeService) {
294
- RED.events.on('plugin.instantiated', nodeTypeServiceListener);
295
- }
296
- }
297
- static get RED() {
298
- return NodeManager._RED;
299
- }
300
- registerService(type) {
301
- let servicePlugin = NodeManager.RED.plugins.get(NodeManager.PLUGINID);
302
- if (servicePlugin) {
303
- servicePlugin.instance.install(NodeManager.RED, type);
304
- }
305
- else {
306
- let serviceName = `${type.getServiceDescriptor().sourceFile()}.${type.getServiceDescriptor().name()}`;
307
- console.log(`ERROR: plugin ${NodeManager.PLUGINID} does not seem to be loaded. Cannot start service: ${serviceName}`);
308
- }
309
- return this;
310
- }
311
- registerNodeType(typeName, type) {
312
- if (!type) {
313
- console.error(`Type "${typeName}" has no associated type. Make sure that it was exported and included in the index.js file!`);
314
- }
315
- let nodeConstructor = function (config) {
316
- //console.log("constructing node", typeName);
317
- try {
318
- // instantiate the node.
319
- NodeManager.RED.nodes.createNode(this, config);
320
- // assign an instance of it to the config.
321
- //(config as any).__node = this;
322
- // instatiate the actual node.
323
- let node = new type(this, config);
324
- //this.__node = node;
325
- // run the constructor initializers.
326
- runPostConstructInitializers(node);
327
- // after the node has been instantiated, attempt to invole the onInit method.
328
- if (node.onInit) {
329
- node.onInit();
330
- }
331
- }
332
- catch (error) {
333
- console.log(error);
334
- console.log("typeName:", typeName);
335
- console.log("type:", type);
336
- console.log("config:", config);
337
- }
338
- };
339
- try {
340
- NodeManager.RED.nodes.registerType(typeName, nodeConstructor, { settings: {} });
341
- let nodeDescription = type.getNodeDescriptor();
342
- // registger the node tags.
343
- if (this.nodeTypeService) {
344
- for (let tag of nodeDescription.tags()) {
345
- this.nodeTypeService.registerNodeType(tag, type);
346
- }
347
- }
348
- else {
349
- this.typeBacklog.push(type);
350
- }
351
- }
352
- catch (error) {
353
- console.error("registering node: " + typeName + " failed", error);
354
- }
355
- return this;
356
- }
357
- }
358
- exports.NodeManager = NodeManager;
359
248
  class BaseService {
360
249
  constructor(name) {
361
250
  this._name = name;
@@ -371,23 +260,26 @@ class BaseService {
371
260
  }
372
261
  }
373
262
  exports.BaseService = BaseService;
374
- const { JSDOM } = jsdom_1.default;
375
- const md = (0, markdown_it_1.default)({
376
- html: true,
377
- linkify: true,
378
- typographer: true
379
- });
380
- // Custom renderer for headings
381
- md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
382
- const token = tokens[idx];
383
- const level = token.tag.substring(1); // 'h1', 'h2', etc. -> '1', '2'
384
- return `<h${level} class="custom-heading">`;
385
- };
386
- md.renderer.rules.heading_close = function (tokens, idx, options, env, self) {
387
- const token = tokens[idx];
388
- const level = token.tag.substring(1);
389
- return `</h${level}>`;
390
- };
263
+ // Build-time deps - lazy loaded so they are never required at runtime in production bundles.
264
+ // These are only invoked during `npm run build` (GenerateNodes), never in the deployed container.
265
+ function getJSDOM() { return require('jsdom').JSDOM; }
266
+ function getBeautify() { return require('js-beautify'); }
267
+ function getHandlebars() { return require('handlebars'); }
268
+ let _md = null;
269
+ function getMd() {
270
+ if (!_md) {
271
+ _md = require('markdown-it')({ html: true, linkify: true, typographer: true });
272
+ _md.renderer.rules.heading_open = (tokens, idx) => {
273
+ const level = tokens[idx].tag.substring(1);
274
+ return `<h${level} class="custom-heading">`;
275
+ };
276
+ _md.renderer.rules.heading_close = (tokens, idx) => {
277
+ const level = tokens[idx].tag.substring(1);
278
+ return `</h${level}>`;
279
+ };
280
+ }
281
+ return _md;
282
+ }
391
283
  let beautifyOptions = {
392
284
  "indent_size": 4,
393
285
  "preserve-newlines": false,
@@ -423,7 +315,7 @@ class NodeBuilder {
423
315
  this._category = category;
424
316
  }
425
317
  addDefault(name, template) {
426
- // if list:true, this is an array of config references generate shadow defaults for each slot.
318
+ // if list:true, this is an array of config references - generate shadow defaults for each slot.
427
319
  if (template.list === true) {
428
320
  template.maxInstances = template.maxInstances ? template.maxInstances : 10;
429
321
  this._defaults[name] = { value: "", required: (template.required) ? template.required : false, maxInstances: template.maxInstances };
@@ -466,8 +358,9 @@ class NodeBuilder {
466
358
  }
467
359
  addTemplate(template) {
468
360
  let templateDescriptor = template.template.getTemplateDescriptor();
361
+ let templateConfig = template.config;
469
362
  // load the html source file
470
- let uiDom = new JSDOM(fs_1.default.readFileSync(templateDescriptor.templateFile()));
363
+ let uiDom = new (getJSDOM())(fs_1.default.readFileSync(templateDescriptor.templateFile()));
471
364
  // apply the onCompose section if available.
472
365
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
473
366
  if (onCompose) {
@@ -478,6 +371,14 @@ class NodeBuilder {
478
371
  Object.values(sections).forEach(section => {
479
372
  let sectionData = uiDom.window.document.querySelector(`[template-section='on${section}']`)?.innerHTML;
480
373
  if (sectionData) {
374
+ if (section !== "IncludeOnce" && section !== "IncludeEditForm" && section !== "IncludeDocumentation") {
375
+ // add the packageing around the sectionData
376
+ sectionData = `{
377
+ let node = this;
378
+ let templateConfig = ${JSON.stringify(templateConfig)};
379
+ ${sectionData}
380
+ }`;
381
+ }
481
382
  this[`add${section}`](templateDescriptor.name(), sectionData);
482
383
  }
483
384
  });
@@ -511,6 +412,63 @@ class NodeBuilder {
511
412
  this._onIncludeDocumentation.push({ source: source, script: script });
512
413
  return this;
513
414
  }
415
+ buildOnceHtml() {
416
+ return this._onIncludeOnce.map(entry => ({
417
+ source: entry.source,
418
+ html: this.serializeHTML(entry.source, entry.script)
419
+ }));
420
+ }
421
+ buildDeferredType() {
422
+ // Apply shadow defaults (same side-effect as buildType)
423
+ let shaddowDefaults = Object.entries(this._defaults).filter(([name, template]) => template.type && template.type.endsWith("[]"));
424
+ if (shaddowDefaults.length > 0) {
425
+ this.addIncludeEditSave(this._name + "_shaddowDefaults", `{ let node = this; ` +
426
+ shaddowDefaults
427
+ .map(([name, template]) => `
428
+ for(let i = 0; i < ${template.maxInstances}; i++){
429
+ node["_${name}_" + i] = (node.${name}[i] || {}).proxy || "";
430
+ }`)
431
+ .join("\n\n") +
432
+ `}`);
433
+ }
434
+ const registerTypeBody = `{
435
+ category: '${this._category}',
436
+ ${(this._category !== "config" && this._icon) ? `icon: '${this._icon}',` : ""}
437
+ ${(this._category !== "config" && this._color) ? `color: '${this._color}',` : ""}
438
+ ${(this._category !== "config" && this._labelStyle) ? `labelStyle: '${this._labelStyle}',` : ""}
439
+ ${(this._label) ? `label: ${this._label},` : ""}
440
+ ${this._paletteLabel ? `paletteLabel: '${this._paletteLabel}',` : ""}
441
+ ${(this._category !== "config") ? `inputs:${this._inputs ? "1" : "0"},` : ""}
442
+ ${(this._category !== "config") ? `inputLabels:(i) => '${this._inputs}',` : ""}
443
+ ${(this._category !== "config") ? `outputs:${this._outputs.length},` : ""}
444
+ ${(this._category !== "config") ? `outputLabels:(i) => ${this.serializeProperty(this._outputs)}[i],` : ""}
445
+ defaults: {
446
+ ${Object.entries(this._defaults).map(([key, value]) => this.serializeDefault(key, value)).join("\n")}
447
+ },
448
+ oneditprepare: function() {
449
+ ${this._onIncludeEditPrepare.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
450
+ },
451
+ oneditsave: function() {
452
+ ${this._onIncludeEditSave.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
453
+ },
454
+ oneditcancel: function() {
455
+ ${this._onIncludeEditCancel.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
456
+ },
457
+ oneditdelete: function() {
458
+ ${this._onIncludeEditDelete.map(entry => this.serializeJavaScript(entry.source, entry.script)).join("")}
459
+ },
460
+ }`;
461
+ return getBeautify().html_beautify(`
462
+ <script type="text/javascript">
463
+ (function () {
464
+ RED.events.on('registry:node-set-added', function (ns) {
465
+ if (ns.types && ns.types.indexOf('${this._name}') !== -1) {
466
+ RED.nodes.registerType('${this._name}', ${registerTypeBody});
467
+ }
468
+ });
469
+ }());
470
+ </script>`, beautifyOptions);
471
+ }
514
472
  serializeProperty(value) {
515
473
  switch (typeof value) {
516
474
  case "boolean": {
@@ -540,7 +498,7 @@ class NodeBuilder {
540
498
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
541
499
  };
542
500
  return `<!-- ${source} -->
543
- ${handlebars_1.default.compile(script)(context)}
501
+ ${getHandlebars().compile(script)(context)}
544
502
  `;
545
503
  }
546
504
  serializeJavaScript(source, script) {
@@ -549,7 +507,7 @@ class NodeBuilder {
549
507
  ? Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-config-input-${k}`]))
550
508
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
551
509
  };
552
- let serialized = handlebars_1.default.compile(script)(context).trim();
510
+ let serialized = getHandlebars().compile(script)(context).trim();
553
511
  if (!serialized.startsWith("{") && !serialized.endsWith("}")) {
554
512
  serialized = "{\n" + serialized + "\n}";
555
513
  }
@@ -564,7 +522,7 @@ class NodeBuilder {
564
522
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
565
523
  };
566
524
  return `
567
- <div id='section_${source}'>${handlebars_1.default.compile(script)(context)}
525
+ <div id='section_${source}'>${getHandlebars().compile(script)(context)}
568
526
  </div>
569
527
  `;
570
528
  }
@@ -587,7 +545,7 @@ class NodeBuilder {
587
545
  .join("\n\n") +
588
546
  `}`);
589
547
  }
590
- return js_beautify_1.default.html_beautify(`
548
+ return getBeautify().html_beautify(`
591
549
  ${this._onIncludeOnce.map(entry => this.serializeHTML(entry.source, entry.script)).join("")}
592
550
  <script type="text/javascript">
593
551
  RED.nodes.registerType('${this._name}',{
@@ -621,7 +579,7 @@ class NodeBuilder {
621
579
  }
622
580
  ;
623
581
  buildHtml() {
624
- return js_beautify_1.default.html_beautify(`
582
+ return getBeautify().html_beautify(`
625
583
  <script type="text/html" data-template-name='${this._name}'>
626
584
  ${this._onIncludeEditForm.map(entry => this.serializeFormEntry(entry.source, entry.script)).join("")}
627
585
  </script>
@@ -651,12 +609,12 @@ let evalInContext = (context, js) => {
651
609
  throw error;
652
610
  }
653
611
  };
654
- let buildNode = function (node) {
612
+ let buildNodeBuilder = function (node) {
655
613
  // get the node descriptor and create the builder.
656
614
  let nodeDescriptor = node.getNodeDescriptor();
657
615
  let nodeBuilder = new NodeBuilder(nodeDescriptor.id(), nodeDescriptor.group());
658
616
  // load the html source file
659
- let uiDom = new JSDOM(fs_1.default.readFileSync(nodeDescriptor.sourceFile()));
617
+ let uiDom = new (getJSDOM())(fs_1.default.readFileSync(nodeDescriptor.sourceFile()));
660
618
  // apply the onCompose section if available.
661
619
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
662
620
  if (onCompose) {
@@ -672,6 +630,11 @@ let buildNode = function (node) {
672
630
  });
673
631
  // add some of the dependencies here.
674
632
  nodeDescriptor.templates().forEach(template => nodeBuilder.addTemplate(template));
633
+ return nodeBuilder;
634
+ };
635
+ exports.buildNodeBuilder = buildNodeBuilder;
636
+ let buildNode = function (node) {
637
+ let nodeBuilder = buildNodeBuilder(node);
675
638
  return [nodeBuilder.buildType(), nodeBuilder.buildHtml(), nodeBuilder.buildDocumentation()].join("\n\n");
676
639
  };
677
640
  exports.buildNode = buildNode;
@@ -1 +1 @@
1
- {"version":3,"file":"NodeGenerator.d.ts","sourceRoot":"","sources":["../../src/core/NodeGenerator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,QAAQ,EAAE,cAAc,EAAe,iBAAiB,EAA4B,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAIrJ,MAAM,WAAW,cAAc;IAC3B,QAAQ,QAAQ,CAAC;IACjB,qBAAqB,IAAI,kBAAkB,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACvB,iBAAiB,IAAG,cAAc,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC1B,oBAAoB,IAAG,iBAAiB,CAAC;CAC5C;AAED,cAAM,aAAa;IAEf,OAAO,CAAC,MAAM,CAAC,WAAW,CAAM;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAKpB;IAEF,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,MAAM,CAA4E;IAC1F,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,UAAU,CAA2B;gBAE1B,UAAU,EAAC,MAAM;IAIpC,OAAO,CAAC,aAAa;IAmBd,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa;IAiBzD,YAAY,CAAC,IAAI,EAAC,UAAU,GAAE,aAAa;IAiB3C,eAAe,CAAC,OAAO,EAAE,aAAa,GAAE,aAAa;IAerD,QAAQ,CAAC,eAAe,EAAC,MAAM,EAAE,iBAAiB,EAAC,MAAM;IAyIhE,OAAO,CAAC,MAAM,CAAC,cAAc;CAQhC;AAID,OAAO,EACH,aAAa,EAChB,CAAA;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAC,MAAM,CAAC;CACf;AAmCD,qBAAa,aAAa;WAER,aAAa,CAAC,SAAS,EAAC,MAAM,EAAE,UAAU,EAAC,MAAM,GAAE,MAAM;CA2B1E;AAED,MAAM,MAAM,OAAO,GAAG;IAClB,CAAC,GAAG,EAAC,MAAM,GAAE,GAAG,CAAC;CACpB,CAAA"}
1
+ {"version":3,"file":"NodeGenerator.d.ts","sourceRoot":"","sources":["../../src/core/NodeGenerator.ts"],"names":[],"mappings":"AAGA,OAAO,EAA+B,QAAQ,EAAE,cAAc,EAAe,iBAAiB,EAA4B,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAIvK,MAAM,WAAW,cAAc;IAC3B,QAAQ,QAAQ,CAAC;IACjB,qBAAqB,IAAI,kBAAkB,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACvB,iBAAiB,IAAG,cAAc,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC1B,oBAAoB,IAAG,iBAAiB,CAAC;CAC5C;AAED,cAAM,aAAa;IAEf,OAAO,CAAC,MAAM,CAAC,WAAW,CAAM;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAKpB;IAEF,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,MAAM,CAA4E;IAC1F,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,UAAU,CAA2B;IAE7C,YAAmB,UAAU,EAAC,MAAM,EAEnC;IAED,OAAO,CAAC,aAAa;IAmBd,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa,CAe/D;IAEM,YAAY,CAAC,IAAI,EAAC,UAAU,GAAE,aAAa,CAejD;IAEM,eAAe,CAAC,OAAO,EAAE,aAAa,GAAE,aAAa,CAa3D;IAEM,QAAQ,CAAC,eAAe,EAAC,MAAM,EAAE,iBAAiB,EAAC,MAAM,QA6L/D;IAED,OAAO,CAAC,MAAM,CAAC,cAAc;CAQhC;AAID,OAAO,EACH,aAAa,EAChB,CAAA;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAC,MAAM,CAAC;CACf;AAmCD,qBAAa,aAAa;IAEtB,OAAc,aAAa,CAAC,SAAS,EAAC,MAAM,EAAE,UAAU,EAAC,MAAM,GAAE,MAAM,CA0BtE;CACJ;AAED,MAAM,MAAM,OAAO,GAAG;IAClB,CAAC,GAAG,EAAC,MAAM,GAAE,GAAG,CAAC;CACpB,CAAA"}
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SourceUtility = exports.NodeGenerator = void 0;
37
37
  const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
38
39
  const NodeConstructor_1 = require("./NodeConstructor");
39
40
  const templatePropertyKeys = ["onIncludeOnce", "onIncludeDefaults", "onIncludeEditPrepare", "onIncludeEditPrepare", "onIncludeEditSave", "onIncludeEditForm"];
40
41
  class NodeGenerator {
@@ -111,28 +112,17 @@ class NodeGenerator {
111
112
  return this;
112
113
  }
113
114
  generate(nodesOutputFile, pluginsOutputFile) {
114
- // STEP 1. Compile the templates.
115
- // let templateInclude = this._templates
116
- // .map(template => new (template.clazz()))
117
- // .map(template => {
118
- // return template.onIncludeOnce();
119
- // })
120
- // .join("\n");
121
- let templateInclude = "";
122
- // STEP 2. compose the HTMLStructure of each of the nodes.
123
- let nodeHTMLOutut = Object.values(this._nodes)
124
- .map(node => {
125
- console.log(`Processing node: ${node.descriptor.id()}`);
126
- return (0, NodeConstructor_1.buildNode)(node.nodeClass);
127
- })
128
- .join("\n");
115
+ // STEP 1. Write an empty Nodes.html.
116
+ // Client-side node type definitions live in Plugins.html (generated below)
117
+ // to prevent HTML-scanning conflicts when multiple plugins bundle the same
118
+ // plugincore infrastructure nodes.
129
119
  fs.writeFileSync(nodesOutputFile + ".html", `
130
120
  <!--
131
121
  ${NodeGenerator.warning}
122
+ Client-side node type definitions have been moved to Plugins.html to prevent
123
+ duplicate type registration conflicts when multiple plugins share the same
124
+ plugincore infrastructure nodes.
132
125
  -->
133
- ${templateInclude}
134
- ${nodeHTMLOutut}
135
-
136
126
  `);
137
127
  // Step 3. compose the JS structure for each of the nodes.
138
128
  // 1. get a list of the files to import.
@@ -153,14 +143,34 @@ ${nodeHTMLOutut}
153
143
  ${NodeGenerator.warning}
154
144
  */
155
145
  "use strict";
156
- const NodeManager = require("@theotherwillembotha/node-red-plugincore").NodeManager;
146
+ const { NodeManager } = require("./runtime/NodeManagerRuntime");
157
147
  ${importList}
158
148
  module.exports = (RED) => {
159
149
  let manager = new NodeManager(RED);
160
150
  ${moduleExports}
161
- }
162
-
151
+ }
152
+
163
153
  `);
154
+ // Copy NodeManagerRuntime.js into the plugin's build/runtime/ folder so that
155
+ // the generated Nodes.js require("./runtime/NodeManagerRuntime") resolves locally
156
+ // with no dependency on plugincore being installed in the user's environment.
157
+ const outputDir = path.dirname(nodesOutputFile);
158
+ const runtimeDestDir = path.join(outputDir, 'runtime');
159
+ const runtimeDestPath = path.join(runtimeDestDir, 'NodeManagerRuntime.js');
160
+ let runtimeSrcPath;
161
+ try {
162
+ // Resolves when this is a consumer plugin - plugincore is in node_modules.
163
+ runtimeSrcPath = require.resolve('@theotherwillembotha/node-red-plugincore/build/runtime/NodeManagerRuntime');
164
+ }
165
+ catch {
166
+ // We ARE plugincore - NodeManagerRuntime.js is compiled alongside us in build/core/.
167
+ runtimeSrcPath = path.join(__dirname, 'NodeManagerRuntime.js');
168
+ }
169
+ if (path.resolve(runtimeSrcPath) !== path.resolve(runtimeDestPath)) {
170
+ fs.mkdirSync(runtimeDestDir, { recursive: true });
171
+ fs.copyFileSync(runtimeSrcPath, runtimeDestPath);
172
+ }
173
+ console.log(`NodeManagerRuntime.js → ${runtimeDestPath}`);
164
174
  console.log(`Done building ${Object.keys(this._nodes).length} nodes\n${Object.values(this._nodes).map(node => " - " + node.descriptor.id() + "\n").join("")}\n`);
165
175
  let serviceExports = this._services.map(service => {
166
176
  return ` manager.registerService(${service.name()}.${service.name()});`;
@@ -192,29 +202,38 @@ module.exports = function (RED) {
192
202
 
193
203
  if(addedPlugin && plugin.instantiate && !plugin.instance){
194
204
  plugin.instance = new plugin.class();
195
-
205
+
196
206
  // instantiate the plugin.
197
207
  await plugin.instance.init(RED);
198
208
 
209
+ // Eagerly call onDeploy with any pre-existing saved flows so that
210
+ // service registries (MetricsService, WebhookServerService, etc.) are
211
+ // populated before config nodes are constructed during flow startup.
212
+ // Without this, MetricsConfigNode._metrics is undefined on first boot.
213
+ const existingFlows = await runtime.flows.getFlows({});
214
+ const hasExistingFlows = existingFlows && existingFlows.flows && existingFlows.flows.length > 0;
215
+ if (hasExistingFlows) {
216
+ await plugin.instance.onDeploy(existingFlows);
217
+ }
218
+
199
219
  // publish an event that it has ben deployed.
200
220
  RED.events.emit("plugin.instantiated", pluginID);
201
-
221
+
202
222
  pluginList.splice(pluginList.findIndex(current => current.id() === addedPlugin.id()), 1);
203
223
 
204
224
  // register a flow deployment listener.
205
- let startupDeployment = true;
225
+ // startupDeployment is only needed for a truly fresh Node-RED with no saved flows.
226
+ let startupDeployment = !hasExistingFlows;
206
227
  RED.events.on('runtime-event', async (event) => {
207
- // startup deployment.
228
+ // startup deployment (fresh Node-RED with no pre-existing flows).
208
229
  if ("runtime-deploy" === event?.id && startupDeployment) {
209
- //console.log("STARTUP DEPLOYMENT");
210
230
  startupDeployment = false;
211
231
  const flows = await runtime.flows.getFlows({});
212
232
  await plugin.instance.onDeploy(flows);
213
233
  }
214
-
234
+
215
235
  // stopping flows on redeployment.
216
236
  if ("runtime-state" === event?.id && event?.payload?.state === "stop" && event?.payload?.deploy) {
217
- //console.log("REDEPLOY!");
218
237
  const flows = await runtime.flows.getFlows({});
219
238
  await plugin.instance.onDeploy(flows);
220
239
  }
@@ -235,6 +254,38 @@ module.exports = function (RED) {
235
254
  `;
236
255
  fs.writeFileSync(pluginsOutputFile + ".js", output);
237
256
  console.log(`Done building ${this._services.length} services\n${this._services.map(service => " - " + service.name() + "\n").join("")}\n`);
257
+ // STEP 4. Generate Plugins.html with deferred client-side node definitions.
258
+ // All nodes are registered via registry:node-set-added so that:
259
+ // a) registerType is only called after typeToId/nodeSets are populated, and
260
+ // b) data-template-name declarations are absent from Nodes.html, avoiding
261
+ // the HTML-scanning conflict that causes Node-RED to reject an entire
262
+ // plugin when a second package declares the same node type.
263
+ const seenOnce = new Set();
264
+ const onceHtmlParts = [];
265
+ const nodePluginParts = [];
266
+ Object.values(this._nodes).forEach(node => {
267
+ console.log(`Building Plugins.html for: ${node.descriptor.id()}`);
268
+ const nb = (0, NodeConstructor_1.buildNodeBuilder)(node.nodeClass);
269
+ nb.buildOnceHtml().forEach(({ source, html }) => {
270
+ if (!seenOnce.has(source)) {
271
+ seenOnce.add(source);
272
+ onceHtmlParts.push(html);
273
+ }
274
+ });
275
+ nodePluginParts.push([
276
+ nb.buildDeferredType(),
277
+ nb.buildHtml(),
278
+ nb.buildDocumentation()
279
+ ].join('\n\n'));
280
+ });
281
+ fs.writeFileSync(pluginsOutputFile + ".html", `
282
+ <!--
283
+ ${NodeGenerator.warning}
284
+ -->
285
+ ${onceHtmlParts.join('\n')}
286
+ ${nodePluginParts.join('\n\n')}
287
+ `);
288
+ console.log(`Done building Plugins.html\n`);
238
289
  }
239
290
  static generateHeader(node) {
240
291
  let padding = ' '.repeat(Math.max(0, Math.floor(NodeGenerator.headerWidth - node.id().length) / 2));
@@ -0,0 +1,44 @@
1
+ /**
2
+ * NodeManagerRuntime.ts
3
+ *
4
+ * STANDALONE - zero imports from the rest of plugincore.
5
+ *
6
+ * This file is compiled and copied into every plugin's build output so that
7
+ * plugins are self-contained and do not require plugincore to be separately
8
+ * installed in the user's Node-RED environment.
9
+ *
10
+ * When multiple plugins are deployed, each ships its own copy of this file.
11
+ * The first plugin to load registers its NodeManager and nodes; subsequent
12
+ * copies operate independently on their own node types with no conflict.
13
+ *
14
+ * POST_CONSTRUCT_KEY is intentionally a plain string (not a Symbol) so that
15
+ * the decorators (from plugincore's NodeConstructor, loaded via the plugin's
16
+ * dependency on plugincore) and this runtime file (a separate module instance)
17
+ * resolve to the same property key on node instances.
18
+ */
19
+ import type { NodeAPI, NodeAPISettingsWithData } from "node-red";
20
+ export declare const NODEMANAGER_API_VERSION = "1.0.0";
21
+ export declare const POST_CONSTRUCT_KEY = "__plugincore_postConstructInitializers__";
22
+ export interface BaseNodeConfig {
23
+ id: string;
24
+ name: string;
25
+ type: string;
26
+ }
27
+ interface INodeDescriptor {
28
+ id(): string;
29
+ tags(): string[];
30
+ }
31
+ interface INodeClass {
32
+ new (node: any, config: any): any;
33
+ getNodeDescriptor(): INodeDescriptor;
34
+ }
35
+ export declare function runPostConstructInitializers(instance: any): void;
36
+ export declare class NodeManager {
37
+ private nodeTypeService;
38
+ private typeBacklog;
39
+ constructor(RED: NodeAPI<NodeAPISettingsWithData>);
40
+ static get RED(): NodeAPI<NodeAPISettingsWithData>;
41
+ registerNodeType(typeName: string, type: INodeClass): NodeManager;
42
+ }
43
+ export {};
44
+ //# sourceMappingURL=NodeManagerRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeManagerRuntime.d.ts","sourceRoot":"","sources":["../../src/core/NodeManagerRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAMjE,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAI/C,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAI7E,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACrB,EAAE,IAAI,MAAM,CAAC;IACb,IAAI,IAAI,MAAM,EAAE,CAAC;CACpB;AAED,UAAU,UAAU;IAChB,KAAI,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IACjC,iBAAiB,IAAI,eAAe,CAAC;CACxC;AAID,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAoBhE;AA0BD,qBAAa,WAAW;IAGpB,OAAO,CAAC,eAAe,CAAM;IAC7B,OAAO,CAAC,WAAW,CAAoB;IAEvC,YAAmB,GAAG,EAAE,OAAO,CAAC,uBAAuB,CAAC,EAuBvD;IAED,WAAkB,GAAG,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAExD;IAEM,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,WAAW,CA6CvE;CACJ"}