iobroker.javascript 10.2.5 → 10.4.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 (205) hide show
  1. package/README.md +26 -23
  2. package/admin/assets/AiChatPanel-BjRIKaXG.js +27 -0
  3. package/admin/assets/{AiChatService-BOL3J2Jd.js → AiChatService-DP1EmVk2.js} +3 -3
  4. package/admin/assets/AiDiffView-DeynDw7L.js +1 -0
  5. package/admin/assets/{AiInlineProvider-BIlcrTkS.js → AiInlineProvider-jkEIJJJ-.js} +1 -1
  6. package/admin/assets/BlocklyEditor-DhwThchX.js +301 -0
  7. package/admin/assets/Compile-B0uHjrBE.css +1 -0
  8. package/admin/assets/Compile-e8N5zI-J.js +165 -0
  9. package/admin/assets/Debugger-BDQUKM1K.js +15 -0
  10. package/admin/assets/Diff--plofAtP.js +1 -0
  11. package/admin/assets/{Documentation-Bc0RmoEF.js → Documentation-D3E0U4AK.js} +4 -4
  12. package/admin/assets/ExpandMore-Dio4GeHC.js +1 -0
  13. package/admin/assets/FbEditor-B8S-mCvc.css +1 -0
  14. package/admin/assets/FbEditor-BjLPNzpK.js +15 -0
  15. package/admin/assets/History-Ewi5YEEn.js +1 -0
  16. package/admin/assets/Import-6Mm2vrM2.js +1 -0
  17. package/admin/assets/MonacoDiff-BYvG7IqB.js +1 -0
  18. package/admin/assets/NotificationsActive-CuNfWVJM.js +1 -0
  19. package/admin/assets/RulesEditor--YX6ETsU.js +1 -0
  20. package/admin/assets/{ScriptEditor-CEHpEFp7.js → ScriptEditor-BadPC8Mf.js} +2 -2
  21. package/admin/assets/ScriptEditorVanillaMonaco-BnAmy59d.js +3 -0
  22. package/admin/assets/Send-Pz1eveU5.js +1 -0
  23. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-Ceh8EDcF.js +1 -0
  24. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__234968779812303__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BRdHvuuG.js +1217 -0
  25. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__234968779812303__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DLWT3PGo.js +9 -0
  26. package/admin/assets/aiPromptBuilder-Co6AqZwx.js +1 -0
  27. package/admin/assets/{blockly-fcfyKYzp.js → blockly-B0t_IRJQ.js} +1 -1
  28. package/admin/assets/blocklyTheme-iZoge1S9.js +1 -0
  29. package/admin/assets/blocks_action-Ctn5A1AU.js +1 -0
  30. package/admin/assets/{blocks_convert-BP2RjhQH.js → blocks_convert-BI0lf0sf.js} +1 -1
  31. package/admin/assets/blocks_logic-o49klUX_.js +1 -0
  32. package/admin/assets/blocks_number-DZtyWFyR.js +1 -0
  33. package/admin/assets/blocks_object-BuzGCxVr.js +1 -0
  34. package/admin/assets/blocks_procedures-jn9sIYmR.js +3 -0
  35. package/admin/assets/blocks_sendto-Coui8w1H.js +5 -0
  36. package/admin/assets/blocks_switch-CeRZizTM.js +2 -0
  37. package/admin/assets/blocks_system-CaBPIC40.js +1 -0
  38. package/admin/assets/blocks_text-DeDtPYWf.js +1 -0
  39. package/admin/assets/blocks_time-CTXQH3Lm.js +1 -0
  40. package/admin/assets/blocks_timeout-D8IOKOb-.js +1 -0
  41. package/admin/assets/{blocks_trigger-CnmXLCUE.js → blocks_trigger-zuuXJbhb.js} +2 -2
  42. package/admin/assets/{cronHoverProvider-CcIFAhVy.js → cronHoverProvider-Curcjz4m.js} +1 -1
  43. package/admin/assets/dist-CzitsFi7.js +1 -0
  44. package/admin/assets/field_cron-RgLY53_S.js +1 -0
  45. package/admin/assets/field_oid-rBhotXJe.js +1 -0
  46. package/admin/assets/field_script-CU7g3-oB.js +1 -0
  47. package/admin/assets/helpers-BZgjFQNf.js +1 -0
  48. package/admin/assets/helpers-BwllSRzJ.js +1 -0
  49. package/admin/assets/hostInit-CrHjJGJJ.js +1 -0
  50. package/admin/assets/index-BHhhebRG.css +1 -0
  51. package/admin/assets/index-Bmmscq2G.js +157 -0
  52. package/admin/assets/{inlineChatWidget-N8xIkvxX.js → inlineChatWidget-ChchJD9a.js} +2 -2
  53. package/admin/assets/{inlineDiffController-BuqYsKKq.js → inlineDiffController-ly6qX4jk.js} +2 -2
  54. package/admin/assets/loadBlocks-BcFtthDb.js +117 -0
  55. package/admin/assets/loadMonaco-BVOEahNw.js +1 -0
  56. package/admin/assets/{mf-entry-bootstrap-0-a5a39621.js → mf-entry-bootstrap-0-6db6c4fc.js} +3 -3
  57. package/admin/assets/pendingShares-mBGPbRwZ.js +2 -0
  58. package/admin/assets/reactour.esm-Dk3aGxKn.js +137 -0
  59. package/admin/assets/{stateHoverProvider-BYAbe1Ay.js → stateHoverProvider-wAsP-THx.js} +1 -1
  60. package/admin/assets/tileFbd-CWAy7R7m.svg +85 -0
  61. package/admin/assets/utils-DUKzLJUz.js +4 -0
  62. package/admin/assets/vite-preload-helper-DjNwdemH.js +1 -0
  63. package/admin/custom/assets/Components-fDcxCyN4.js +1 -0
  64. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-BraOrMSu.js +1 -0
  65. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-DbKT6Hsb.js +1 -0
  66. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-CVvbIPAy.js +1 -0
  67. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DVb0ZZ1Q.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BhZYie9-.js} +89 -89
  68. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare___mf_0_mui_mf_1_x_mf_2_date_mf_2_pickers__loadShare__.js-CV50dgj4.js +1 -0
  69. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare__date_mf_2_fns__loadShare__.js-Djg9OMcQ.js +1 -0
  70. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare__leaflet__loadShare__.js-D9EyCd42.js +1 -0
  71. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__loadShare__shared-Hb6V6BnJ.js +2 -0
  72. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-BrY-nF2a.js +122 -0
  73. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__prebuild__leaflet__prebuild__-CHjWtrkp.js +1 -0
  74. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__prebuild__react_mf_2_dom_mf_1_client__prebuild__-DOkC-g0X.js +9 -0
  75. package/admin/custom/assets/dist-CzitsFi7.js +1 -0
  76. package/admin/custom/assets/helpers-BZgjFQNf.js +1 -0
  77. package/admin/custom/assets/hostInit-akwaQLq8.js +1 -0
  78. package/admin/custom/assets/index-BkItz-nL.js +1 -0
  79. package/admin/custom/assets/{lib-BmDlTzq2.js → lib-CADOx2YN.js} +1 -1
  80. package/admin/custom/assets/{mf-entry-bootstrap-0-ad1cd7d0.js → mf-entry-bootstrap-0-3c4565e0.js} +3 -3
  81. package/admin/custom/assets/pendingShares-TCMDgBLn.js +2 -0
  82. package/admin/custom/assets/utils-DUKzLJUz.js +4 -0
  83. package/admin/custom/assets/vite-preload-helper-DjNwdemH.js +1 -0
  84. package/admin/custom/customComponents.js +2 -2
  85. package/admin/custom/i18n/de.json +19 -1
  86. package/admin/custom/i18n/en.json +19 -1
  87. package/admin/custom/i18n/es.json +19 -1
  88. package/admin/custom/i18n/fr.json +19 -1
  89. package/admin/custom/i18n/it.json +19 -1
  90. package/admin/custom/i18n/nl.json +19 -1
  91. package/admin/custom/i18n/pl.json +19 -1
  92. package/admin/custom/i18n/pt.json +19 -1
  93. package/admin/custom/i18n/ru.json +19 -1
  94. package/admin/custom/i18n/uk.json +19 -1
  95. package/admin/custom/i18n/zh-cn.json +19 -1
  96. package/admin/i18n/de.json +13 -1
  97. package/admin/i18n/en.json +13 -1
  98. package/admin/i18n/es.json +13 -1
  99. package/admin/i18n/fr.json +13 -1
  100. package/admin/i18n/it.json +13 -1
  101. package/admin/i18n/nl.json +13 -1
  102. package/admin/i18n/pl.json +13 -1
  103. package/admin/i18n/pt.json +13 -1
  104. package/admin/i18n/ru.json +13 -1
  105. package/admin/i18n/uk.json +13 -1
  106. package/admin/i18n/zh-cn.json +13 -1
  107. package/admin/jsonConfig.json +274 -86
  108. package/admin/mf-manifest.json +1 -1
  109. package/admin/mf-stats.json +1 -1
  110. package/admin/remoteEntry.js +2 -2
  111. package/admin/tab.html +3 -7
  112. package/build/lib/aiProviderResolver.js +145 -25
  113. package/build/lib/aiProviderResolver.js.map +1 -1
  114. package/build/lib/anthropicAdapter.js +37 -0
  115. package/build/lib/anthropicAdapter.js.map +1 -1
  116. package/build/lib/fb/analyze.js +288 -0
  117. package/build/lib/fb/analyze.js.map +1 -0
  118. package/build/lib/fb/blocks.js +605 -0
  119. package/build/lib/fb/blocks.js.map +1 -0
  120. package/build/lib/fb/generate.js +326 -0
  121. package/build/lib/fb/generate.js.map +1 -0
  122. package/build/lib/fb/graph.js +240 -0
  123. package/build/lib/fb/graph.js.map +1 -0
  124. package/build/lib/fb/index.js +57 -0
  125. package/build/lib/fb/index.js.map +1 -0
  126. package/build/lib/fb/js.js +33 -0
  127. package/build/lib/fb/js.js.map +1 -0
  128. package/build/lib/fb/layout.js +205 -0
  129. package/build/lib/fb/layout.js.map +1 -0
  130. package/build/lib/fb/library.js +657 -0
  131. package/build/lib/fb/library.js.map +1 -0
  132. package/build/lib/fb/runtime.js +793 -0
  133. package/build/lib/fb/runtime.js.map +1 -0
  134. package/build/lib/fb/time.js +76 -0
  135. package/build/lib/fb/time.js.map +1 -0
  136. package/build/lib/fb/types.js +19 -0
  137. package/build/lib/fb/types.js.map +1 -0
  138. package/build/lib/fb/user.js +78 -0
  139. package/build/lib/fb/user.js.map +1 -0
  140. package/build/lib/inspect.js +66 -10
  141. package/build/lib/inspect.js.map +1 -1
  142. package/build/lib/javascript.d.ts +1 -1
  143. package/build/lib/mirror.js +28 -23
  144. package/build/lib/mirror.js.map +1 -1
  145. package/build/lib/sandbox.js +44 -0
  146. package/build/lib/sandbox.js.map +1 -1
  147. package/build/lib/scriptHistory.js +245 -0
  148. package/build/lib/scriptHistory.js.map +1 -0
  149. package/build/main.js +641 -32
  150. package/build/main.js.map +1 -1
  151. package/build/types.d.ts +20 -1
  152. package/docs/de/fbd.md +274 -0
  153. package/docs/en/fbd.md +260 -0
  154. package/io-package.json +55 -29
  155. package/package.json +7 -7
  156. package/admin/assets/AiChatPanel-oBeXbUFv.js +0 -27
  157. package/admin/assets/AiDiffView-C-KKjnJD.js +0 -1
  158. package/admin/assets/Debugger-DwHHsh9O.js +0 -15
  159. package/admin/assets/Error-DZ4vCykD.js +0 -1
  160. package/admin/assets/Import-Cu02ceLW.js +0 -1
  161. package/admin/assets/RulesEditor-QCbi1lV_.js +0 -1
  162. package/admin/assets/ScriptEditorVanillaMonaco-ChQmnk0p.js +0 -3
  163. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-CM95r358.js +0 -1
  164. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BEpNZh4U.js +0 -1217
  165. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-FeM77flT.js +0 -9
  166. package/admin/assets/aiPromptBuilder-DKler0UK.js +0 -1
  167. package/admin/assets/blocks_action-DywZVfRY.js +0 -1
  168. package/admin/assets/blocks_logic-CdTl5YZw.js +0 -1
  169. package/admin/assets/blocks_number-DXFd9Rva.js +0 -1
  170. package/admin/assets/blocks_object-by1uBDHi.js +0 -1
  171. package/admin/assets/blocks_procedures-BcJ7N70D.js +0 -3
  172. package/admin/assets/blocks_sendto-qIKzOAcV.js +0 -5
  173. package/admin/assets/blocks_switch-dLF6hSJl.js +0 -2
  174. package/admin/assets/blocks_system-DmX-SBy8.js +0 -1
  175. package/admin/assets/blocks_text-BI4zNU3o.js +0 -1
  176. package/admin/assets/blocks_time-BgYA4Y_4.js +0 -1
  177. package/admin/assets/blocks_timeout-DUzgMyAu.js +0 -1
  178. package/admin/assets/dist-D4uJqRBO.js +0 -4
  179. package/admin/assets/field_cron-Ba46q8Qc.js +0 -1
  180. package/admin/assets/field_oid-lWCjOzuq.js +0 -1
  181. package/admin/assets/field_script-BCWZHhnW.js +0 -1
  182. package/admin/assets/helpers-CnEjYAup.js +0 -1
  183. package/admin/assets/hostInit-CVybS7BB.js +0 -1
  184. package/admin/assets/index-B3s0zmWC.js +0 -871
  185. package/admin/assets/index-CXaeqPRy.css +0 -1
  186. package/admin/assets/pendingShares-DOAkyFMS.js +0 -1
  187. package/admin/assets/vite-preload-helper-CaC3yEL3.js +0 -1
  188. package/admin/custom/assets/Components-FkGDIlId.js +0 -1
  189. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-Cs88NUCd.js +0 -1
  190. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-P-8ffWXw.js +0 -1
  191. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-ZVuRAEo5.js +0 -1
  192. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-Dna3WF04.js +0 -1
  193. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_mui_mf_1_x_mf_2_date_mf_2_pickers__loadShare__.js-DcjdCGAx.js +0 -1
  194. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__date_mf_2_fns__loadShare__.js-CnhNjamd.js +0 -1
  195. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__leaflet__loadShare__.js-Dz7BFJqH.js +0 -1
  196. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-B0olko2J.js +0 -1
  197. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-CzN4reh_.js +0 -122
  198. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__leaflet__prebuild__-xPcBaiTQ.js +0 -1
  199. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_2_dom_mf_1_client__prebuild__-DuFBE5fh.js +0 -9
  200. package/admin/custom/assets/dist-D4uJqRBO.js +0 -4
  201. package/admin/custom/assets/hostInit-6oh4UY2k.js +0 -1
  202. package/admin/custom/assets/index-BrlyEy6J.js +0 -1
  203. package/admin/custom/assets/pendingShares-kV2kwejX.js +0 -1
  204. package/admin/custom/assets/vite-preload-helper-CaC3yEL3.js +0 -1
  205. /package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-CXpyx8Vy.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__198282747311932__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-CXpyx8Vy.js} +0 -0
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatIssue = formatIssue;
4
+ exports.analyzeGraph = analyzeGraph;
5
+ const graph_1 = require("./graph");
6
+ const js_1 = require("./js");
7
+ const library_1 = require("./library");
8
+ const user_1 = require("./user");
9
+ /**
10
+ * Strongly connected components (Tarjan). A component of one node only counts as a loop when the
11
+ * node feeds itself - the caller checks that.
12
+ */
13
+ function findComponents(ids, edges) {
14
+ let index = 0;
15
+ const indices = new Map();
16
+ const lowLinks = new Map();
17
+ const stack = [];
18
+ const onStack = new Set();
19
+ const components = [];
20
+ const visit = (id) => {
21
+ indices.set(id, index);
22
+ lowLinks.set(id, index);
23
+ index++;
24
+ stack.push(id);
25
+ onStack.add(id);
26
+ for (const next of edges.get(id) || []) {
27
+ if (!indices.has(next)) {
28
+ visit(next);
29
+ lowLinks.set(id, Math.min(lowLinks.get(id), lowLinks.get(next)));
30
+ }
31
+ else if (onStack.has(next)) {
32
+ lowLinks.set(id, Math.min(lowLinks.get(id), indices.get(next)));
33
+ }
34
+ }
35
+ if (lowLinks.get(id) === indices.get(id)) {
36
+ const component = [];
37
+ let member;
38
+ do {
39
+ member = stack.pop();
40
+ onStack.delete(member);
41
+ component.push(member);
42
+ } while (member !== id);
43
+ components.push(component);
44
+ }
45
+ };
46
+ ids.forEach(id => !indices.has(id) && visit(id));
47
+ return components;
48
+ }
49
+ function isLoop(component, edges) {
50
+ return component.length > 1 || !!edges.get(component[0])?.has(component[0]);
51
+ }
52
+ function buildEdges(ids, links, skip) {
53
+ const edges = new Map(ids.map(id => [id, new Set()]));
54
+ for (const link of links) {
55
+ if (!skip?.(link)) {
56
+ edges.get(link.from[0])?.add(link.to[0]);
57
+ }
58
+ }
59
+ return edges;
60
+ }
61
+ /** A pin name becomes a property of the instance next to `run()` */
62
+ const PIN_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
63
+ const RESERVED_PINS = ['run', 'self', 'constructor', 'prototype', '__proto__'];
64
+ function formatIssue(issue) {
65
+ let index = 0;
66
+ return issue.message.replace(/%s/g, () => issue.args?.[index++] ?? '');
67
+ }
68
+ /** Checks a user block once per analysis; its errors become one error of the instance */
69
+ function checkUserBlock(user, options) {
70
+ if (options.visiting.has(user.type)) {
71
+ return 'The block %s contains itself';
72
+ }
73
+ let analysis = options.cache.get(user.type);
74
+ if (!analysis) {
75
+ options.visiting.add(user.type);
76
+ analysis = analyzeGraph(user.graph, { ...options, isBlock: true });
77
+ options.visiting.delete(user.type);
78
+ options.cache.set(user.type, analysis);
79
+ }
80
+ return analysis.issues.some(issue => issue.severity === 'error') ? 'The block %s has errors: %s' : null;
81
+ }
82
+ /**
83
+ * Checks a diagram and works out the execution order.
84
+ *
85
+ * The order follows the data flow. A loop is only allowed when it runs through a block with a
86
+ * state (a timer, a flip-flop, a user block, ...): the link that leads back reads what that block
87
+ * put out in the previous cycle, as in any SPS. A loop without such a block has no solution and is
88
+ * an error.
89
+ */
90
+ function analyzeGraph(graph, analyzeOptions) {
91
+ const options = {
92
+ userBlocks: analyzeOptions?.userBlocks || graph.userBlocks || {},
93
+ isBlock: analyzeOptions?.isBlock ?? !!graph.block,
94
+ visiting: analyzeOptions?.visiting || new Set(graph.block ? [graph.block.type] : []),
95
+ cache: analyzeOptions?.cache || new Map(),
96
+ };
97
+ const issues = [];
98
+ const nodes = new Map();
99
+ if (graph.block && !user_1.FB_USER_TYPE_PATTERN.test(graph.block.type)) {
100
+ issues.push({ severity: 'error', message: 'Invalid block type %s', args: [graph.block.type] });
101
+ }
102
+ const pins = new Set();
103
+ for (const block of graph.blocks) {
104
+ if (!graph_1.FB_ID_PATTERN.test(block.id || '')) {
105
+ issues.push({ severity: 'error', message: 'Invalid block ID %s', args: [String(block.id)] });
106
+ continue;
107
+ }
108
+ if (nodes.has(block.id)) {
109
+ issues.push({ severity: 'error', message: 'Duplicate block ID %s', args: [block.id], blockId: block.id });
110
+ continue;
111
+ }
112
+ const def = (0, library_1.getBlockDef)(block.type, options.userBlocks);
113
+ if (!def) {
114
+ issues.push({ severity: 'error', message: 'Unknown block type %s', args: [block.type], blockId: block.id });
115
+ continue;
116
+ }
117
+ nodes.set(block.id, { block, def });
118
+ for (const param of def.params || []) {
119
+ const value = block.params?.[param.id] ?? param.default;
120
+ if (param.required && (value === undefined || value === null || value === '')) {
121
+ issues.push({
122
+ severity: 'error',
123
+ message: 'Parameter "%s" is not set',
124
+ args: [param.id],
125
+ blockId: block.id,
126
+ });
127
+ }
128
+ }
129
+ if (def.category === 'interface') {
130
+ if (!options.isBlock) {
131
+ issues.push({
132
+ severity: 'error',
133
+ message: 'Inputs and outputs of a block only work in the diagram of a block',
134
+ blockId: block.id,
135
+ });
136
+ }
137
+ const pin = String(block.params?.pin ?? '');
138
+ if (pin && (!PIN_NAME.test(pin) || RESERVED_PINS.includes(pin))) {
139
+ issues.push({ severity: 'error', message: 'Invalid pin name %s', args: [pin], blockId: block.id });
140
+ }
141
+ else if (pin && pins.has(pin)) {
142
+ issues.push({
143
+ severity: 'error',
144
+ message: 'The pin name %s is used twice',
145
+ args: [pin],
146
+ blockId: block.id,
147
+ });
148
+ }
149
+ pins.add(pin);
150
+ }
151
+ else if (options.isBlock && (block.type === 'STATE_IN' || block.type === 'STATE_OUT')) {
152
+ issues.push({
153
+ severity: 'error',
154
+ message: 'A block cannot read or write states - give it inputs and outputs instead',
155
+ blockId: block.id,
156
+ });
157
+ }
158
+ if (def.user) {
159
+ const problem = checkUserBlock(def.user, options);
160
+ if (problem) {
161
+ const inner = options.cache.get(def.user.type)?.issues.find(issue => issue.severity === 'error');
162
+ issues.push({
163
+ severity: 'error',
164
+ message: problem,
165
+ args: inner ? [def.user.name, formatIssue(inner)] : [def.user.name],
166
+ blockId: block.id,
167
+ });
168
+ }
169
+ }
170
+ if (block.type === 'JS') {
171
+ const problem = (0, js_1.checkJsCode)((0, js_1.jsCode)(block, def), (0, js_1.jsParameters)(block, def));
172
+ if (problem) {
173
+ issues.push({
174
+ severity: 'error',
175
+ message: 'The code has an error: %s',
176
+ args: [problem],
177
+ blockId: block.id,
178
+ });
179
+ }
180
+ }
181
+ }
182
+ // links: both ends must exist, the types must fit, and an input takes one signal only
183
+ const links = [];
184
+ const driven = new Set();
185
+ for (const link of graph.links) {
186
+ const from = nodes.get(link.from[0]);
187
+ const to = nodes.get(link.to[0]);
188
+ const output = from && (0, graph_1.getOutputs)(from.block, from.def).find(pin => pin.id === link.from[1]);
189
+ const input = to && (0, graph_1.getInputs)(to.block, to.def).find(pin => pin.id === link.to[1]);
190
+ if (!output || !input) {
191
+ issues.push({
192
+ severity: 'error',
193
+ message: 'The connection refers to a missing block or pin',
194
+ linkId: link.id,
195
+ blockId: to ? link.to[0] : from ? link.from[0] : undefined,
196
+ });
197
+ continue;
198
+ }
199
+ if (!(0, graph_1.isCompatible)(output.type, input.type)) {
200
+ issues.push({
201
+ severity: 'error',
202
+ message: 'Types do not match: %s cannot be connected to %s',
203
+ args: [output.type, input.type],
204
+ linkId: link.id,
205
+ blockId: link.to[0],
206
+ });
207
+ continue;
208
+ }
209
+ const key = `${link.to[0]}.${link.to[1]}`;
210
+ if (driven.has(key)) {
211
+ issues.push({
212
+ severity: 'error',
213
+ message: 'Input %s is connected more than once',
214
+ args: [link.to[1]],
215
+ linkId: link.id,
216
+ blockId: link.to[0],
217
+ });
218
+ continue;
219
+ }
220
+ driven.add(key);
221
+ links.push(link);
222
+ }
223
+ // Loops: inside a loop, the links that start at a block with a state may lead back. Whatever
224
+ // is still a loop without them has no block to take the value of the previous cycle from.
225
+ const ids = [...nodes.keys()];
226
+ const allEdges = buildEdges(ids, links);
227
+ const inLoop = new Map();
228
+ findComponents(ids, allEdges)
229
+ .filter(component => isLoop(component, allEdges))
230
+ .forEach((component, i) => component.forEach(id => inLoop.set(id, i)));
231
+ const breakable = (link) => !!nodes.get(link.from[0])?.def.stateful &&
232
+ inLoop.has(link.from[0]) &&
233
+ inLoop.get(link.from[0]) === inLoop.get(link.to[0]);
234
+ const orderEdges = buildEdges(ids, links, breakable);
235
+ for (const component of findComponents(ids, orderEdges)) {
236
+ if (isLoop(component, orderEdges)) {
237
+ const names = component
238
+ .map(id => nodes.get(id).block)
239
+ .sort(byPosition)
240
+ .map(block => block.name || block.id);
241
+ issues.push({
242
+ severity: 'error',
243
+ message: 'Loop without a block that stores a value: %s',
244
+ args: [names.join(', ')],
245
+ blockIds: component,
246
+ });
247
+ }
248
+ }
249
+ // topological order; blocks that are ready at the same time run from left to right, then top down
250
+ const incoming = new Map(ids.map(id => [id, 0]));
251
+ orderEdges.forEach(targets => targets.forEach(id => incoming.set(id, incoming.get(id) + 1)));
252
+ const ready = ids.filter(id => !incoming.get(id));
253
+ const sequence = [];
254
+ while (ready.length) {
255
+ ready.sort((a, b) => byPosition(nodes.get(a).block, nodes.get(b).block));
256
+ const id = ready.shift();
257
+ sequence.push(id);
258
+ for (const next of orderEdges.get(id)) {
259
+ const count = incoming.get(next) - 1;
260
+ incoming.set(next, count);
261
+ if (!count) {
262
+ ready.push(next);
263
+ }
264
+ }
265
+ }
266
+ // blocks of an unsolvable loop still get a number, so the editor can show one
267
+ ids.filter(id => !sequence.includes(id))
268
+ .sort((a, b) => byPosition(nodes.get(a).block, nodes.get(b).block))
269
+ .forEach(id => sequence.push(id));
270
+ const order = {};
271
+ sequence.forEach((id, i) => (order[id] = (i + 1) * 10));
272
+ const feedback = links.filter(link => order[link.from[0]] >= order[link.to[0]]).map(link => link.id);
273
+ const timeDependent = [...nodes.values()].some(node => node.def.timeDependent);
274
+ const mode = graph.cycle.mode === 'auto' ? (timeDependent ? 'cyclic' : 'event') : graph.cycle.mode;
275
+ const ms = Math.max(graph_1.FB_CYCLE_MIN_MS, Math.min(graph_1.FB_CYCLE_MAX_MS, Math.round(Number(graph.cycle.ms) || graph_1.FB_CYCLE_DEFAULT_MS)));
276
+ // the mode of a block is the one of the diagram it is used in
277
+ if (mode === 'event' && timeDependent && !options.isBlock) {
278
+ issues.push({
279
+ severity: 'warning',
280
+ message: 'Timers need the cyclic mode, in the event mode they only run when an input changes',
281
+ });
282
+ }
283
+ return { issues, order, feedback, links, mode, ms };
284
+ }
285
+ function byPosition(a, b) {
286
+ return a.pos[0] - b.pos[0] || a.pos[1] - b.pos[1] || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
287
+ }
288
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../src/lib/fb/analyze.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FB_CYCLE_DEFAULT_MS,\n FB_CYCLE_MAX_MS,\n FB_CYCLE_MIN_MS,\n FB_ID_PATTERN,\n getInputs,\n getOutputs,\n isCompatible,\n} from './graph';\nimport { checkJsCode, jsCode, jsParameters } from './js';\nimport { getBlockDef } from './library';\nimport type { FbBlock, FbBlockDef, FbGraph, FbIssue, FbLink, FbUserBlock } from './types';\nimport { FB_USER_TYPE_PATTERN } from './user';\n\nexport interface FbAnalysis {\n issues: FbIssue[];\n /** Position of each block in the execution order: 10, 20, 30, ... */\n order: Record<string, number>;\n /** The links that carry the value of the previous cycle, because they lead back in the order */\n feedback: string[];\n /** The links that can be turned into code, in the order of `graph.links` */\n links: FbLink[];\n /** How the diagram runs: `auto` resolved */\n mode: 'cyclic' | 'event';\n /** Interval of the cyclic mode in ms */\n ms: number;\n}\n\ninterface Node {\n block: FbBlock;\n def: FbBlockDef;\n}\n\n/**\n * Strongly connected components (Tarjan). A component of one node only counts as a loop when the\n * node feeds itself - the caller checks that.\n */\nfunction findComponents(ids: string[], edges: Map<string, Set<string>>): string[][] {\n let index = 0;\n const indices = new Map<string, number>();\n const lowLinks = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const components: string[][] = [];\n\n const visit = (id: string): void => {\n indices.set(id, index);\n lowLinks.set(id, index);\n index++;\n stack.push(id);\n onStack.add(id);\n\n for (const next of edges.get(id) || []) {\n if (!indices.has(next)) {\n visit(next);\n lowLinks.set(id, Math.min(lowLinks.get(id)!, lowLinks.get(next)!));\n } else if (onStack.has(next)) {\n lowLinks.set(id, Math.min(lowLinks.get(id)!, indices.get(next)!));\n }\n }\n\n if (lowLinks.get(id) === indices.get(id)) {\n const component: string[] = [];\n let member: string;\n do {\n member = stack.pop()!;\n onStack.delete(member);\n component.push(member);\n } while (member !== id);\n components.push(component);\n }\n };\n\n ids.forEach(id => !indices.has(id) && visit(id));\n return components;\n}\n\nfunction isLoop(component: string[], edges: Map<string, Set<string>>): boolean {\n return component.length > 1 || !!edges.get(component[0])?.has(component[0]);\n}\n\nfunction buildEdges(ids: string[], links: FbLink[], skip?: (link: FbLink) => boolean): Map<string, Set<string>> {\n const edges = new Map<string, Set<string>>(ids.map(id => [id, new Set<string>()]));\n for (const link of links) {\n if (!skip?.(link)) {\n edges.get(link.from[0])?.add(link.to[0]);\n }\n }\n return edges;\n}\n\nexport interface FbAnalyzeOptions {\n /** The user blocks the diagram may use - by default its own copies */\n userBlocks?: Record<string, FbUserBlock>;\n /** The diagram is a block: FB_IN and FB_OUT make its pins, states are not reachable */\n isBlock?: boolean;\n /** The user blocks being checked further up, against a block that contains itself */\n visiting?: Set<string>;\n /** Results of user blocks checked already */\n cache?: Map<string, FbAnalysis>;\n}\n\n/** A pin name becomes a property of the instance next to `run()` */\nconst PIN_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst RESERVED_PINS = ['run', 'self', 'constructor', 'prototype', '__proto__'];\n\nexport function formatIssue(issue: FbIssue): string {\n let index = 0;\n return issue.message.replace(/%s/g, () => issue.args?.[index++] ?? '');\n}\n\n/** Checks a user block once per analysis; its errors become one error of the instance */\nfunction checkUserBlock(user: FbUserBlock, options: Required<FbAnalyzeOptions>): string | null {\n if (options.visiting.has(user.type)) {\n return 'The block %s contains itself';\n }\n let analysis = options.cache.get(user.type);\n if (!analysis) {\n options.visiting.add(user.type);\n analysis = analyzeGraph(user.graph, { ...options, isBlock: true });\n options.visiting.delete(user.type);\n options.cache.set(user.type, analysis);\n }\n return analysis.issues.some(issue => issue.severity === 'error') ? 'The block %s has errors: %s' : null;\n}\n\n/**\n * Checks a diagram and works out the execution order.\n *\n * The order follows the data flow. A loop is only allowed when it runs through a block with a\n * state (a timer, a flip-flop, a user block, ...): the link that leads back reads what that block\n * put out in the previous cycle, as in any SPS. A loop without such a block has no solution and is\n * an error.\n */\nexport function analyzeGraph(graph: FbGraph, analyzeOptions?: FbAnalyzeOptions): FbAnalysis {\n const options: Required<FbAnalyzeOptions> = {\n userBlocks: analyzeOptions?.userBlocks || graph.userBlocks || {},\n isBlock: analyzeOptions?.isBlock ?? !!graph.block,\n visiting: analyzeOptions?.visiting || new Set(graph.block ? [graph.block.type] : []),\n cache: analyzeOptions?.cache || new Map(),\n };\n const issues: FbIssue[] = [];\n const nodes = new Map<string, Node>();\n\n if (graph.block && !FB_USER_TYPE_PATTERN.test(graph.block.type)) {\n issues.push({ severity: 'error', message: 'Invalid block type %s', args: [graph.block.type] });\n }\n const pins = new Set<string>();\n\n for (const block of graph.blocks) {\n if (!FB_ID_PATTERN.test(block.id || '')) {\n issues.push({ severity: 'error', message: 'Invalid block ID %s', args: [String(block.id)] });\n continue;\n }\n if (nodes.has(block.id)) {\n issues.push({ severity: 'error', message: 'Duplicate block ID %s', args: [block.id], blockId: block.id });\n continue;\n }\n const def = getBlockDef(block.type, options.userBlocks);\n if (!def) {\n issues.push({ severity: 'error', message: 'Unknown block type %s', args: [block.type], blockId: block.id });\n continue;\n }\n nodes.set(block.id, { block, def });\n\n for (const param of def.params || []) {\n const value = block.params?.[param.id] ?? param.default;\n if (param.required && (value === undefined || value === null || value === '')) {\n issues.push({\n severity: 'error',\n message: 'Parameter \"%s\" is not set',\n args: [param.id],\n blockId: block.id,\n });\n }\n }\n\n if (def.category === 'interface') {\n if (!options.isBlock) {\n issues.push({\n severity: 'error',\n message: 'Inputs and outputs of a block only work in the diagram of a block',\n blockId: block.id,\n });\n }\n const pin = String(block.params?.pin ?? '');\n if (pin && (!PIN_NAME.test(pin) || RESERVED_PINS.includes(pin))) {\n issues.push({ severity: 'error', message: 'Invalid pin name %s', args: [pin], blockId: block.id });\n } else if (pin && pins.has(pin)) {\n issues.push({\n severity: 'error',\n message: 'The pin name %s is used twice',\n args: [pin],\n blockId: block.id,\n });\n }\n pins.add(pin);\n } else if (options.isBlock && (block.type === 'STATE_IN' || block.type === 'STATE_OUT')) {\n issues.push({\n severity: 'error',\n message: 'A block cannot read or write states - give it inputs and outputs instead',\n blockId: block.id,\n });\n }\n\n if (def.user) {\n const problem = checkUserBlock(def.user, options);\n if (problem) {\n const inner = options.cache.get(def.user.type)?.issues.find(issue => issue.severity === 'error');\n issues.push({\n severity: 'error',\n message: problem,\n args: inner ? [def.user.name, formatIssue(inner)] : [def.user.name],\n blockId: block.id,\n });\n }\n }\n\n if (block.type === 'JS') {\n const problem = checkJsCode(jsCode(block, def), jsParameters(block, def));\n if (problem) {\n issues.push({\n severity: 'error',\n message: 'The code has an error: %s',\n args: [problem],\n blockId: block.id,\n });\n }\n }\n }\n\n // links: both ends must exist, the types must fit, and an input takes one signal only\n const links: FbLink[] = [];\n const driven = new Set<string>();\n for (const link of graph.links) {\n const from = nodes.get(link.from[0]);\n const to = nodes.get(link.to[0]);\n const output = from && getOutputs(from.block, from.def).find(pin => pin.id === link.from[1]);\n const input = to && getInputs(to.block, to.def).find(pin => pin.id === link.to[1]);\n if (!output || !input) {\n issues.push({\n severity: 'error',\n message: 'The connection refers to a missing block or pin',\n linkId: link.id,\n blockId: to ? link.to[0] : from ? link.from[0] : undefined,\n });\n continue;\n }\n if (!isCompatible(output.type, input.type)) {\n issues.push({\n severity: 'error',\n message: 'Types do not match: %s cannot be connected to %s',\n args: [output.type, input.type],\n linkId: link.id,\n blockId: link.to[0],\n });\n continue;\n }\n const key = `${link.to[0]}.${link.to[1]}`;\n if (driven.has(key)) {\n issues.push({\n severity: 'error',\n message: 'Input %s is connected more than once',\n args: [link.to[1]],\n linkId: link.id,\n blockId: link.to[0],\n });\n continue;\n }\n driven.add(key);\n links.push(link);\n }\n\n // Loops: inside a loop, the links that start at a block with a state may lead back. Whatever\n // is still a loop without them has no block to take the value of the previous cycle from.\n const ids = [...nodes.keys()];\n const allEdges = buildEdges(ids, links);\n const inLoop = new Map<string, number>();\n findComponents(ids, allEdges)\n .filter(component => isLoop(component, allEdges))\n .forEach((component, i) => component.forEach(id => inLoop.set(id, i)));\n\n const breakable = (link: FbLink): boolean =>\n !!nodes.get(link.from[0])?.def.stateful &&\n inLoop.has(link.from[0]) &&\n inLoop.get(link.from[0]) === inLoop.get(link.to[0]);\n\n const orderEdges = buildEdges(ids, links, breakable);\n for (const component of findComponents(ids, orderEdges)) {\n if (isLoop(component, orderEdges)) {\n const names = component\n .map(id => nodes.get(id)!.block)\n .sort(byPosition)\n .map(block => block.name || block.id);\n issues.push({\n severity: 'error',\n message: 'Loop without a block that stores a value: %s',\n args: [names.join(', ')],\n blockIds: component,\n });\n }\n }\n\n // topological order; blocks that are ready at the same time run from left to right, then top down\n const incoming = new Map<string, number>(ids.map(id => [id, 0]));\n orderEdges.forEach(targets => targets.forEach(id => incoming.set(id, incoming.get(id)! + 1)));\n const ready = ids.filter(id => !incoming.get(id));\n const sequence: string[] = [];\n while (ready.length) {\n ready.sort((a, b) => byPosition(nodes.get(a)!.block, nodes.get(b)!.block));\n const id = ready.shift()!;\n sequence.push(id);\n for (const next of orderEdges.get(id)!) {\n const count = incoming.get(next)! - 1;\n incoming.set(next, count);\n if (!count) {\n ready.push(next);\n }\n }\n }\n // blocks of an unsolvable loop still get a number, so the editor can show one\n ids.filter(id => !sequence.includes(id))\n .sort((a, b) => byPosition(nodes.get(a)!.block, nodes.get(b)!.block))\n .forEach(id => sequence.push(id));\n\n const order: Record<string, number> = {};\n sequence.forEach((id, i) => (order[id] = (i + 1) * 10));\n\n const feedback = links.filter(link => order[link.from[0]] >= order[link.to[0]]).map(link => link.id);\n\n const timeDependent = [...nodes.values()].some(node => node.def.timeDependent);\n const mode = graph.cycle.mode === 'auto' ? (timeDependent ? 'cyclic' : 'event') : graph.cycle.mode;\n const ms = Math.max(\n FB_CYCLE_MIN_MS,\n Math.min(FB_CYCLE_MAX_MS, Math.round(Number(graph.cycle.ms) || FB_CYCLE_DEFAULT_MS)),\n );\n // the mode of a block is the one of the diagram it is used in\n if (mode === 'event' && timeDependent && !options.isBlock) {\n issues.push({\n severity: 'warning',\n message: 'Timers need the cyclic mode, in the event mode they only run when an input changes',\n });\n }\n\n return { issues, order, feedback, links, mode, ms };\n}\n\nfunction byPosition(a: FbBlock, b: FbBlock): number {\n return a.pos[0] - b.pos[0] || a.pos[1] - b.pos[1] || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);\n}\n"]}