@theotherwillembotha/node-red-plugincore 0.0.55 → 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 (158) hide show
  1. package/README.md +616 -217
  2. package/build/GenerateNodes.js +3 -6
  3. package/build/Nodes.html +3 -1502
  4. package/build/Nodes.js +5 -15
  5. package/build/Plugins.html +227 -0
  6. package/build/Plugins.js +18 -9
  7. package/build/core/NodeConstructor.d.ts +10 -16
  8. package/build/core/NodeConstructor.d.ts.map +1 -1
  9. package/build/core/NodeConstructor.js +112 -156
  10. package/build/core/NodeGenerator.d.ts.map +1 -1
  11. package/build/core/NodeGenerator.js +79 -29
  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/WebhookDecorator.d.ts.map +1 -1
  70. package/build/core/webhook/WebhookDecorator.js +43 -6
  71. package/build/core/webhook/node/WebhookServerConfigNode.d.ts.map +1 -1
  72. package/build/core/webhook/service/ReverseProxyTypeService.d.ts +1 -0
  73. package/build/core/webhook/service/ReverseProxyTypeService.d.ts.map +1 -1
  74. package/build/core/webhook/service/WebhookServerService.d.ts +3 -4
  75. package/build/core/webhook/service/WebhookServerService.d.ts.map +1 -1
  76. package/build/core/webhook/service/WebhookServerService.js +48 -23
  77. package/build/core/webhook/template/WebhookTemplate.d.ts.map +1 -1
  78. package/build/index.d.ts +5 -2
  79. package/build/index.d.ts.map +1 -1
  80. package/build/index.js +6 -2
  81. package/build/runtime/NodeManagerRuntime.js +140 -0
  82. package/documentation/CounterMetricConfigNode.png +0 -0
  83. package/documentation/GaugeMetricConfigNode.png +0 -0
  84. package/documentation/LoggerTemplate.png +0 -0
  85. package/documentation/MetricsTemplate.png +0 -0
  86. package/documentation/StateTemplate.png +0 -0
  87. package/documentation/TimerMetricConfigNode_histogram.png +0 -0
  88. package/documentation/TimerMetricConfigNode_summary.png +0 -0
  89. package/documentation/WebhookServerConfigNode.png +0 -0
  90. package/documentation/WebhookTemplate.png +0 -0
  91. package/package.json +5 -12
  92. package/src/GenerateNodes.ts +7 -11
  93. package/src/core/NodeConstructor.ts +134 -197
  94. package/src/core/NodeGenerator.ts +88 -34
  95. package/src/core/NodeManagerRuntime.ts +179 -0
  96. package/src/core/logger/service/LoggerService.ts +39 -26
  97. package/src/core/logger/template/LoggerTemplate.html +13 -8
  98. package/src/core/metrics/MetricsDecorator.ts +140 -168
  99. package/src/core/metrics/node/CounterMetricConfigNode.ts +20 -20
  100. package/src/core/metrics/node/GaugeMetricConfigNode.ts +24 -25
  101. package/src/core/metrics/node/MetricsConfigNode.ts +29 -48
  102. package/src/core/metrics/node/TimerMetricConfigNode.html +2 -2
  103. package/src/core/metrics/node/TimerMetricConfigNode.ts +50 -59
  104. package/src/core/metrics/service/MetricsService.ts +214 -489
  105. package/src/core/metrics/template/CounterMetricTemplate.html +0 -1
  106. package/src/core/metrics/template/GaugeMetricTemplate.html +1 -1
  107. package/src/core/metrics/template/MetricsTemplate.html +132 -13
  108. package/src/core/metrics/template/MetricsTemplate.ts +3 -4
  109. package/src/core/other/node/DelegatedConfigReferenceNode.ts +1 -1
  110. package/src/core/other/service/SettingsService.ts +6 -10
  111. package/src/core/other/template/BasicTemplate.html +5 -2
  112. package/src/core/state/node/InternalStateConfigNode.html +77 -0
  113. package/src/core/state/node/InternalStateConfigNode.ts +102 -0
  114. package/src/core/state/node/StateConfigNode.ts +29 -0
  115. package/src/core/state/service/StateService.ts +180 -0
  116. package/src/core/state/template/StateTemplate.html +244 -0
  117. package/src/core/state/template/StateTemplate.ts +17 -0
  118. package/src/core/tagging/ServiceDescriptionDecorator.ts +23 -0
  119. package/src/core/tagging/service/NodeTypeService.ts +6 -11
  120. package/src/core/ui/template/UIHelperTemplate.html +41 -26
  121. package/src/core/webhook/WebhookDecorator.ts +53 -10
  122. package/src/core/webhook/service/ReverseProxyTypeService.ts +1 -0
  123. package/src/core/webhook/service/WebhookServerService.ts +39 -28
  124. package/src/core/webhook/template/WebhookTemplate.html +29 -36
  125. package/src/index.ts +7 -2
  126. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts +0 -20
  127. package/build/core/logger/node/ConsoleLoggerConfigNode.d.ts.map +0 -1
  128. package/build/core/logger/node/ConsoleLoggerConfigNode.html +0 -90
  129. package/build/core/logger/node/ConsoleLoggerConfigNode.js +0 -62
  130. package/build/core/logger/node/RestLoggerConfigNode.d.ts +0 -40
  131. package/build/core/logger/node/RestLoggerConfigNode.d.ts.map +0 -1
  132. package/build/core/logger/node/RestLoggerConfigNode.html +0 -183
  133. package/build/core/logger/node/RestLoggerConfigNode.js +0 -128
  134. package/build/core/logger/template/LoggerTemplate.html +0 -331
  135. package/build/core/metrics/node/CounterMetricConfigNode.html +0 -48
  136. package/build/core/metrics/node/GaugeMetricConfigNode.html +0 -48
  137. package/build/core/metrics/node/MetricsConfigNode.html +0 -21
  138. package/build/core/metrics/node/TimerMetricConfigNode.html +0 -190
  139. package/build/core/metrics/template/CounterMetricTemplate.html +0 -40
  140. package/build/core/metrics/template/GaugeMetricTemplate.html +0 -39
  141. package/build/core/metrics/template/MetricsTemplate.html +0 -54
  142. package/build/core/metrics/template/TimerMetricTemplate.html +0 -39
  143. package/build/core/other/node/DelegatedConfigReferenceNode.html +0 -1
  144. package/build/core/other/template/BasicTemplate.html +0 -76
  145. package/build/core/other/template/SettingsTemplate.html +0 -128
  146. package/build/core/ui/template/ScriptEditorTemplate.html +0 -50
  147. package/build/core/ui/template/UIHelperTemplate.html +0 -229
  148. package/build/core/webhook/node/WebhookServerConfigNode.html +0 -61
  149. package/build/core/webhook/template/WebhookTemplate.html +0 -442
  150. package/documentation/ConsoleLoggerConfigNode.png +0 -0
  151. package/documentation/MetricsConfigNode.png +0 -0
  152. package/documentation/RestLoggerConfigNode.png +0 -0
  153. package/documentation/TimerMetricConfigNode.png +0 -0
  154. package/documentation/WebhookNode.png +0 -0
  155. package/src/core/logger/node/ConsoleLoggerConfigNode.html +0 -90
  156. package/src/core/logger/node/ConsoleLoggerConfigNode.ts +0 -71
  157. package/src/core/logger/node/RestLoggerConfigNode.html +0 -183
  158. 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,124 +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
- console.log("NodeTypeService installed!");
295
- }
296
- else {
297
- // Register a listener to trigger when the RegerseProxyTypService gets installed.
298
- console.log("nodetypeservice not installed yet. waiting for it to be installed");
299
- RED.events.on('plugin.instantiated', nodeTypeServiceListener);
300
- }
301
- }
302
- static get RED() {
303
- return NodeManager._RED;
304
- }
305
- registerService(type) {
306
- let servicePlugin = NodeManager.RED.plugins.get(NodeManager.PLUGINID);
307
- if (servicePlugin) {
308
- servicePlugin.instance.install(NodeManager.RED, type);
309
- }
310
- else {
311
- let serviceName = `${type.getServiceDescriptor().sourceFile()}.${type.getServiceDescriptor().name()}`;
312
- console.log(`ERROR: plugin ${NodeManager.PLUGINID} does not seem to be loaded. Cannot start service: ${serviceName}`);
313
- }
314
- return this;
315
- }
316
- registerNodeType(typeName, type) {
317
- if (!type) {
318
- console.error(`Type "${typeName}" has no associated type. Make sure that it was exported and included in the index.js file!`);
319
- }
320
- let nodeConstructor = function (config) {
321
- //console.log("constructing node", typeName);
322
- try {
323
- // instantiate the node.
324
- NodeManager.RED.nodes.createNode(this, config);
325
- // assign an instance of it to the config.
326
- //(config as any).__node = this;
327
- // instatiate the actual node.
328
- let node = new type(this, config);
329
- //this.__node = node;
330
- // run the constructor initializers.
331
- runPostConstructInitializers(node);
332
- // after the node has been instantiated, attempt to invole the onInit method.
333
- if (node.onInit) {
334
- node.onInit();
335
- }
336
- }
337
- catch (error) {
338
- console.log(error);
339
- console.log("typeName:", typeName);
340
- console.log("type:", type);
341
- console.log("config:", config);
342
- }
343
- };
344
- try {
345
- console.log("registering node " + typeName);
346
- NodeManager.RED.nodes.registerType(typeName, nodeConstructor, { settings: {} });
347
- let nodeDescription = type.getNodeDescriptor();
348
- // registger the node tags.
349
- if (this.nodeTypeService) {
350
- for (let tag of nodeDescription.tags()) {
351
- this.nodeTypeService.registerNodeType(tag, type);
352
- }
353
- }
354
- else {
355
- this.typeBacklog.push(type);
356
- }
357
- console.log("registered node " + typeName);
358
- }
359
- catch (error) {
360
- console.error("registering node: " + typeName + " failed", error);
361
- }
362
- return this;
363
- }
364
- }
365
- exports.NodeManager = NodeManager;
366
248
  class BaseService {
367
249
  constructor(name) {
368
250
  this._name = name;
@@ -378,23 +260,26 @@ class BaseService {
378
260
  }
379
261
  }
380
262
  exports.BaseService = BaseService;
381
- const { JSDOM } = jsdom_1.default;
382
- const md = (0, markdown_it_1.default)({
383
- html: true,
384
- linkify: true,
385
- typographer: true
386
- });
387
- // Custom renderer for headings
388
- md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
389
- const token = tokens[idx];
390
- const level = token.tag.substring(1); // 'h1', 'h2', etc. -> '1', '2'
391
- return `<h${level} class="custom-heading">`;
392
- };
393
- md.renderer.rules.heading_close = function (tokens, idx, options, env, self) {
394
- const token = tokens[idx];
395
- const level = token.tag.substring(1);
396
- return `</h${level}>`;
397
- };
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
+ }
398
283
  let beautifyOptions = {
399
284
  "indent_size": 4,
400
285
  "preserve-newlines": false,
@@ -430,12 +315,12 @@ class NodeBuilder {
430
315
  this._category = category;
431
316
  }
432
317
  addDefault(name, template) {
433
- // if the template type ends with squeare brackets, its an array type.
434
- if (template.type && template.type.endsWith("[]")) {
318
+ // if list:true, this is an array of config references - generate shadow defaults for each slot.
319
+ if (template.list === true) {
435
320
  template.maxInstances = template.maxInstances ? template.maxInstances : 10;
436
- this._defaults[name] = { type: template.type, value: "", required: true, maxInstances: template.maxInstances };
321
+ this._defaults[name] = { value: "", required: (template.required) ? template.required : false, maxInstances: template.maxInstances };
437
322
  for (let i = 0; i < template.maxInstances; i++) {
438
- this._defaults["_" + name + "_" + i] = { value: "", required: false };
323
+ this._defaults["_" + name + "_" + i] = { value: "", required: false, type: template.type };
439
324
  }
440
325
  }
441
326
  else {
@@ -473,8 +358,9 @@ class NodeBuilder {
473
358
  }
474
359
  addTemplate(template) {
475
360
  let templateDescriptor = template.template.getTemplateDescriptor();
361
+ let templateConfig = template.config;
476
362
  // load the html source file
477
- let uiDom = new JSDOM(fs_1.default.readFileSync(templateDescriptor.templateFile()));
363
+ let uiDom = new (getJSDOM())(fs_1.default.readFileSync(templateDescriptor.templateFile()));
478
364
  // apply the onCompose section if available.
479
365
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
480
366
  if (onCompose) {
@@ -485,6 +371,14 @@ class NodeBuilder {
485
371
  Object.values(sections).forEach(section => {
486
372
  let sectionData = uiDom.window.document.querySelector(`[template-section='on${section}']`)?.innerHTML;
487
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
+ }
488
382
  this[`add${section}`](templateDescriptor.name(), sectionData);
489
383
  }
490
384
  });
@@ -518,6 +412,63 @@ class NodeBuilder {
518
412
  this._onIncludeDocumentation.push({ source: source, script: script });
519
413
  return this;
520
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
+ }
521
472
  serializeProperty(value) {
522
473
  switch (typeof value) {
523
474
  case "boolean": {
@@ -547,7 +498,7 @@ class NodeBuilder {
547
498
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
548
499
  };
549
500
  return `<!-- ${source} -->
550
- ${handlebars_1.default.compile(script)(context)}
501
+ ${getHandlebars().compile(script)(context)}
551
502
  `;
552
503
  }
553
504
  serializeJavaScript(source, script) {
@@ -556,7 +507,7 @@ class NodeBuilder {
556
507
  ? Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-config-input-${k}`]))
557
508
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
558
509
  };
559
- let serialized = handlebars_1.default.compile(script)(context).trim();
510
+ let serialized = getHandlebars().compile(script)(context).trim();
560
511
  if (!serialized.startsWith("{") && !serialized.endsWith("}")) {
561
512
  serialized = "{\n" + serialized + "\n}";
562
513
  }
@@ -571,7 +522,7 @@ class NodeBuilder {
571
522
  : Object.fromEntries(Object.keys(this._defaults).map(k => [k, `node-input-${k}`]))
572
523
  };
573
524
  return `
574
- <div id='section_${source}'>${handlebars_1.default.compile(script)(context)}
525
+ <div id='section_${source}'>${getHandlebars().compile(script)(context)}
575
526
  </div>
576
527
  `;
577
528
  }
@@ -594,7 +545,7 @@ class NodeBuilder {
594
545
  .join("\n\n") +
595
546
  `}`);
596
547
  }
597
- return js_beautify_1.default.html_beautify(`
548
+ return getBeautify().html_beautify(`
598
549
  ${this._onIncludeOnce.map(entry => this.serializeHTML(entry.source, entry.script)).join("")}
599
550
  <script type="text/javascript">
600
551
  RED.nodes.registerType('${this._name}',{
@@ -628,7 +579,7 @@ class NodeBuilder {
628
579
  }
629
580
  ;
630
581
  buildHtml() {
631
- return js_beautify_1.default.html_beautify(`
582
+ return getBeautify().html_beautify(`
632
583
  <script type="text/html" data-template-name='${this._name}'>
633
584
  ${this._onIncludeEditForm.map(entry => this.serializeFormEntry(entry.source, entry.script)).join("")}
634
585
  </script>
@@ -658,12 +609,12 @@ let evalInContext = (context, js) => {
658
609
  throw error;
659
610
  }
660
611
  };
661
- let buildNode = function (node) {
612
+ let buildNodeBuilder = function (node) {
662
613
  // get the node descriptor and create the builder.
663
614
  let nodeDescriptor = node.getNodeDescriptor();
664
615
  let nodeBuilder = new NodeBuilder(nodeDescriptor.id(), nodeDescriptor.group());
665
616
  // load the html source file
666
- let uiDom = new JSDOM(fs_1.default.readFileSync(nodeDescriptor.sourceFile()));
617
+ let uiDom = new (getJSDOM())(fs_1.default.readFileSync(nodeDescriptor.sourceFile()));
667
618
  // apply the onCompose section if available.
668
619
  let onCompose = uiDom.window.document.querySelector("script[template-section='onCompose']")?.innerHTML;
669
620
  if (onCompose) {
@@ -679,6 +630,11 @@ let buildNode = function (node) {
679
630
  });
680
631
  // add some of the dependencies here.
681
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);
682
638
  return [nodeBuilder.buildType(), nodeBuilder.buildHtml(), nodeBuilder.buildDocumentation()].join("\n\n");
683
639
  };
684
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;IA0IhE,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()});`;
@@ -191,31 +201,39 @@ module.exports = function (RED) {
191
201
  let plugin = RED.plugins.get(pluginID);
192
202
 
193
203
  if(addedPlugin && plugin.instantiate && !plugin.instance){
194
- console.log("Deploying instance of: " + plugin.id);
195
204
  plugin.instance = new plugin.class();
196
-
205
+
197
206
  // instantiate the plugin.
198
207
  await plugin.instance.init(RED);
199
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
+
200
219
  // publish an event that it has ben deployed.
201
220
  RED.events.emit("plugin.instantiated", pluginID);
202
-
221
+
203
222
  pluginList.splice(pluginList.findIndex(current => current.id() === addedPlugin.id()), 1);
204
223
 
205
224
  // register a flow deployment listener.
206
- let startupDeployment = true;
225
+ // startupDeployment is only needed for a truly fresh Node-RED with no saved flows.
226
+ let startupDeployment = !hasExistingFlows;
207
227
  RED.events.on('runtime-event', async (event) => {
208
- // startup deployment.
228
+ // startup deployment (fresh Node-RED with no pre-existing flows).
209
229
  if ("runtime-deploy" === event?.id && startupDeployment) {
210
- //console.log("STARTUP DEPLOYMENT");
211
230
  startupDeployment = false;
212
231
  const flows = await runtime.flows.getFlows({});
213
232
  await plugin.instance.onDeploy(flows);
214
233
  }
215
-
234
+
216
235
  // stopping flows on redeployment.
217
236
  if ("runtime-state" === event?.id && event?.payload?.state === "stop" && event?.payload?.deploy) {
218
- //console.log("REDEPLOY!");
219
237
  const flows = await runtime.flows.getFlows({});
220
238
  await plugin.instance.onDeploy(flows);
221
239
  }
@@ -236,6 +254,38 @@ module.exports = function (RED) {
236
254
  `;
237
255
  fs.writeFileSync(pluginsOutputFile + ".js", output);
238
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`);
239
289
  }
240
290
  static generateHeader(node) {
241
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"}