iobroker.javascript 10.2.4 → 10.3.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 (186) hide show
  1. package/README.md +10 -21
  2. package/admin/assets/AiChatPanel-CpY4Bun-.js +27 -0
  3. package/admin/assets/{AiChatService-Bsp_rhVx.js → AiChatService-QKcpW4jQ.js} +1 -1
  4. package/admin/assets/AiDiffView-DscPfZ38.js +1 -0
  5. package/admin/assets/{AiInlineProvider-BxOW88xU.js → AiInlineProvider-DTSPKoXG.js} +1 -1
  6. package/admin/assets/BlocklyEditor-BZyoN1JE.js +301 -0
  7. package/admin/assets/Compile-B0uHjrBE.css +1 -0
  8. package/admin/assets/Compile-Oss28m_f.js +165 -0
  9. package/admin/assets/Debugger-teJTIU-2.js +15 -0
  10. package/admin/assets/Documentation-uz4Ut9pT.js +4339 -0
  11. package/admin/assets/ExpandMore-V5Ix1QB1.js +1 -0
  12. package/admin/assets/FbEditor-B8S-mCvc.css +1 -0
  13. package/admin/assets/FbEditor-aOZ-RpPV.js +15 -0
  14. package/admin/assets/Import-BIC5Ee9z.js +1 -0
  15. package/admin/assets/NotificationsActive-BdzH44gp.js +1 -0
  16. package/admin/assets/RulesEditor-CcDVthPa.js +1 -0
  17. package/admin/assets/{ScriptEditor-BCGahS47.js → ScriptEditor-CviB6Hif.js} +2 -2
  18. package/admin/assets/ScriptEditorVanillaMonaco-ByuXwybM.js +3 -0
  19. package/admin/assets/Send-Mdo32jMb.js +1 -0
  20. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-BnL93BgK.js +1 -0
  21. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-NI-3Uv4e.js +1217 -0
  22. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__180366418666083__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DVaMIZyC.js +9 -0
  23. package/admin/assets/aiPromptBuilder-yTB6N2VT.js +1 -0
  24. package/admin/assets/blocklyTheme-CGhU3cEO.js +1 -0
  25. package/admin/assets/blocks_action-BQ4O7NRE.js +1 -0
  26. package/admin/assets/{blocks_convert-CLb0DLyc.js → blocks_convert-0VBjfhqQ.js} +1 -1
  27. package/admin/assets/blocks_logic-DAo99xDr.js +1 -0
  28. package/admin/assets/blocks_number-C--e19NU.js +1 -0
  29. package/admin/assets/blocks_object-D0TPhdzg.js +1 -0
  30. package/admin/assets/blocks_procedures-DgTxgXdB.js +3 -0
  31. package/admin/assets/{blocks_sendto-BxCMg9X4.js → blocks_sendto-CRIrtOUc.js} +1 -1
  32. package/admin/assets/blocks_switch-b_GYfF0n.js +2 -0
  33. package/admin/assets/blocks_system-BdkUdSsc.js +1 -0
  34. package/admin/assets/blocks_text-DSFhEVOf.js +1 -0
  35. package/admin/assets/blocks_time-CqALAiJp.js +1 -0
  36. package/admin/assets/blocks_timeout-yrOD2pZp.js +1 -0
  37. package/admin/assets/{blocks_trigger-XivsIDE5.js → blocks_trigger-DeAYXpSS.js} +1 -1
  38. package/admin/assets/{cronHoverProvider-BZMOFHO_.js → cronHoverProvider-DoO2_tpG.js} +1 -1
  39. package/admin/assets/{field_cron-CxKi__cX.js → field_cron-D3Ts_s9M.js} +1 -1
  40. package/admin/assets/{field_oid-CJIYiGxY.js → field_oid-BRNXredg.js} +1 -1
  41. package/admin/assets/{field_script-CehplM7L.js → field_script-3X6ousg7.js} +1 -1
  42. package/admin/assets/helpers-BTGaTdnT.js +1 -0
  43. package/admin/assets/{hostInit-Bs-bStPF.js → hostInit-2h2Cbsaw.js} +1 -1
  44. package/admin/assets/iconLibraryData-B5fiyl1C.js +1 -0
  45. package/admin/assets/index-BHhhebRG.css +1 -0
  46. package/admin/assets/index-CbQlIKB_.js +156 -0
  47. package/admin/assets/{inlineChatWidget-NIvdRGmU.js → inlineChatWidget-BwQVCPkx.js} +1 -1
  48. package/admin/assets/{inlineDiffController-Qh402KhK.js → inlineDiffController-BsDDsDHn.js} +1 -1
  49. package/admin/assets/loadBlocks-RpqM7PwO.js +117 -0
  50. package/admin/assets/loadMonaco-BVOEahNw.js +1 -0
  51. package/admin/assets/{mf-entry-bootstrap-0-019228a5.js → mf-entry-bootstrap-0-0deef484.js} +3 -3
  52. package/admin/assets/{pendingShares-B3t6ewri.js → pendingShares-BaAadIDv.js} +1 -1
  53. package/admin/assets/reactour.esm-DX9ZgYlx.js +137 -0
  54. package/admin/assets/stateHoverProvider-BEadnSXp.js +11 -0
  55. package/admin/assets/tileFbd-CWAy7R7m.svg +85 -0
  56. package/admin/custom/assets/Components-FkGDIlId.js +1 -0
  57. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-Cs88NUCd.js +1 -0
  58. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-P-8ffWXw.js +1 -0
  59. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-ZVuRAEo5.js +1 -0
  60. 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 +1217 -0
  61. 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 +1 -0
  62. 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 +1 -0
  63. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__date_mf_2_fns__loadShare__.js-CnhNjamd.js +1 -0
  64. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__leaflet__loadShare__.js-im3edKNs.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__leaflet__loadShare__.js-Dz7BFJqH.js} +1 -1
  65. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-B0olko2J.js +1 -0
  66. 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 +122 -0
  67. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__leaflet__prebuild__-xPcBaiTQ.js +1 -0
  68. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-Byvr2uOb.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-CXpyx8Vy.js} +1 -1
  69. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__37453319418803__prebuild__react_mf_2_dom_mf_1_client__prebuild__-DuFBE5fh.js +9 -0
  70. package/admin/custom/assets/{hostInit-IJMNq3h7.js → hostInit-6oh4UY2k.js} +1 -1
  71. package/admin/custom/assets/iconLibraryData-B5fiyl1C.js +1 -0
  72. package/admin/custom/assets/index-BrlyEy6J.js +1 -0
  73. package/admin/custom/assets/lib-BmDlTzq2.js +3 -0
  74. package/admin/custom/assets/{mf-entry-bootstrap-0-f149bc46.js → mf-entry-bootstrap-0-ad1cd7d0.js} +3 -3
  75. package/admin/custom/assets/pendingShares-kV2kwejX.js +1 -0
  76. package/admin/custom/assets/rolldown-runtime-Dqfqb5bO.js +1 -0
  77. package/admin/custom/customComponents.js +3 -2
  78. package/admin/custom/i18n/en.json +39 -39
  79. package/admin/custom/i18n/es.json +1 -1
  80. package/admin/custom/i18n/fr.json +2 -2
  81. package/admin/custom/i18n/it.json +1 -1
  82. package/admin/custom/i18n/nl.json +2 -2
  83. package/admin/custom/i18n/pt.json +2 -2
  84. package/admin/custom/i18n/ru.json +2 -2
  85. package/admin/custom/i18n/uk.json +1 -1
  86. package/admin/custom/i18n/zh-cn.json +1 -1
  87. package/admin/i18n/de.json +1 -1
  88. package/admin/i18n/en.json +115 -115
  89. package/admin/i18n/fr.json +115 -115
  90. package/admin/i18n/it.json +115 -115
  91. package/admin/i18n/nl.json +115 -115
  92. package/admin/i18n/pl.json +1 -1
  93. package/admin/i18n/pt.json +115 -115
  94. package/admin/i18n/ru.json +115 -115
  95. package/admin/i18n/uk.json +115 -115
  96. package/admin/i18n/zh-cn.json +115 -115
  97. package/admin/mf-manifest.json +1 -1
  98. package/admin/mf-stats.json +1 -1
  99. package/admin/remoteEntry.js +2 -2
  100. package/admin/tab.html +3 -7
  101. package/build/lib/fb/analyze.js +288 -0
  102. package/build/lib/fb/analyze.js.map +1 -0
  103. package/build/lib/fb/blocks.js +605 -0
  104. package/build/lib/fb/blocks.js.map +1 -0
  105. package/build/lib/fb/generate.js +326 -0
  106. package/build/lib/fb/generate.js.map +1 -0
  107. package/build/lib/fb/graph.js +240 -0
  108. package/build/lib/fb/graph.js.map +1 -0
  109. package/build/lib/fb/index.js +57 -0
  110. package/build/lib/fb/index.js.map +1 -0
  111. package/build/lib/fb/js.js +33 -0
  112. package/build/lib/fb/js.js.map +1 -0
  113. package/build/lib/fb/layout.js +205 -0
  114. package/build/lib/fb/layout.js.map +1 -0
  115. package/build/lib/fb/library.js +657 -0
  116. package/build/lib/fb/library.js.map +1 -0
  117. package/build/lib/fb/runtime.js +793 -0
  118. package/build/lib/fb/runtime.js.map +1 -0
  119. package/build/lib/fb/time.js +76 -0
  120. package/build/lib/fb/time.js.map +1 -0
  121. package/build/lib/fb/types.js +19 -0
  122. package/build/lib/fb/types.js.map +1 -0
  123. package/build/lib/fb/user.js +78 -0
  124. package/build/lib/fb/user.js.map +1 -0
  125. package/build/lib/javascript.d.ts +17 -17
  126. package/build/lib/mirror.js +4 -0
  127. package/build/lib/mirror.js.map +1 -1
  128. package/build/lib/sandbox.js +44 -0
  129. package/build/lib/sandbox.js.map +1 -1
  130. package/build/main.js +66 -1
  131. package/build/main.js.map +1 -1
  132. package/build/types.d.ts +9 -1
  133. package/docs/de/README.md +1 -1
  134. package/docs/de/fbd.md +274 -0
  135. package/docs/de/javascript.md +2167 -0
  136. package/docs/en/fbd.md +260 -0
  137. package/docs/en/javascript.md +28 -30
  138. package/io-package.json +52 -28
  139. package/package.json +8 -8
  140. package/admin/assets/AiChatPanel-AZko1BsN.js +0 -27
  141. package/admin/assets/AiDiffView-Cri2QFh1.js +0 -1
  142. package/admin/assets/Debugger-CVDh3kgz.js +0 -15
  143. package/admin/assets/Error-Ch2nx5uC.js +0 -1
  144. package/admin/assets/Import-XKVP-nRT.js +0 -1
  145. package/admin/assets/RulesEditor-DsO4cKIk.js +0 -1
  146. package/admin/assets/ScriptEditorVanillaMonaco-BDAw2Bw6.js +0 -3
  147. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-BMAFQFoH.js +0 -1
  148. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__113183188387379__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-D-gi6Mty.js +0 -1217
  149. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__113183188387379__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-CDctpiPx.js +0 -9
  150. package/admin/assets/aiPromptBuilder-aJfHa-eU.js +0 -1
  151. package/admin/assets/blocks_action-DS28sNfo.js +0 -1
  152. package/admin/assets/blocks_logic-Dpp2fwlu.js +0 -1
  153. package/admin/assets/blocks_number-N4IfHvYD.js +0 -1
  154. package/admin/assets/blocks_object-uK7FHVhS.js +0 -1
  155. package/admin/assets/blocks_procedures-C_KD0Bm6.js +0 -3
  156. package/admin/assets/blocks_switch-C8Rls5pK.js +0 -2
  157. package/admin/assets/blocks_system-CEQnj5fY.js +0 -1
  158. package/admin/assets/blocks_text-BDEWxu0P.js +0 -1
  159. package/admin/assets/blocks_time-aHQQK1Uz.js +0 -1
  160. package/admin/assets/blocks_timeout-C2L93TLJ.js +0 -1
  161. package/admin/assets/helpers-DiwtLpXM.js +0 -1
  162. package/admin/assets/index-B0FfAYMN.js +0 -871
  163. package/admin/assets/index-CXaeqPRy.css +0 -1
  164. package/admin/assets/stateHoverProvider-BkW15VMN.js +0 -9
  165. package/admin/assets/virtualExposes-DgGB-18P.js +0 -1
  166. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-CiidVc0O.js +0 -1
  167. package/admin/assets/virtual_mf-exposes___mfe_internal__iobroker_javascript__remoteEntry_js-ChiJXsfH.js +0 -1
  168. package/admin/custom/assets/Components-SGnA-3W_.js +0 -1
  169. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-Bx3X6TLB.js +0 -1
  170. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-Cc7MmJgr.js +0 -1
  171. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-CdvEvX5z.js +0 -1
  172. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-D5NaoeJi.js +0 -1217
  173. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-Cbgw_a3f.js +0 -1
  174. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare___mf_0_mui_mf_1_x_mf_2_date_mf_2_pickers__loadShare__.js-CMr75K5w.js +0 -1
  175. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__date_mf_2_fns__loadShare__.js-DKIvPDPX.js +0 -1
  176. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-D50aYKgD.js +0 -1
  177. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-CEzQFX7o.js +0 -122
  178. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__leaflet__prebuild__-BUDmnAcx.js +0 -1
  179. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__136440932828998__prebuild__react_mf_2_dom_mf_1_client__prebuild__-D_1oh1WH.js +0 -9
  180. package/admin/custom/assets/index-7r4VklMp.js +0 -1
  181. package/admin/custom/assets/lib-DDU2Oe48.js +0 -3
  182. package/admin/custom/assets/pendingShares-DarGQhyO.js +0 -1
  183. package/admin/custom/assets/rolldown-runtime-BcKkbAw3.js +0 -1
  184. package/admin/custom/assets/virtualExposes-C0IqMANQ.js +0 -1
  185. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-CqKvh5Xu.js +0 -1
  186. package/admin/custom/assets/virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-DrBN_S5o.js +0 -2
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toLiteral = toLiteral;
4
+ exports.generateSource = generateSource;
5
+ const analyze_1 = require("./analyze");
6
+ const graph_1 = require("./graph");
7
+ const library_1 = require("./library");
8
+ const types_1 = require("./types");
9
+ const js_1 = require("./js");
10
+ const time_1 = require("./time");
11
+ const user_1 = require("./user");
12
+ /** A value as a JavaScript literal of the given type */
13
+ function toLiteral(value, type) {
14
+ switch (type) {
15
+ case 'BOOL':
16
+ return value === true || value === 'true' || value === 1 || value === '1' ? 'true' : 'false';
17
+ case 'INT':
18
+ return String(Math.round(Number(value)) || 0);
19
+ case 'REAL': {
20
+ const number = Number(value);
21
+ return String(Number.isFinite(number) ? number : 0);
22
+ }
23
+ case 'TIME':
24
+ return String((0, time_1.parseTime)(value) ?? 0);
25
+ case 'STRING':
26
+ return JSON.stringify(value === null || value === undefined ? '' : String(value));
27
+ default:
28
+ // ANY: whatever the text looks like
29
+ if (typeof value === 'boolean' || typeof value === 'number') {
30
+ return String(value);
31
+ }
32
+ if (value === null || value === undefined) {
33
+ return '0';
34
+ }
35
+ if (value === 'true' || value === 'false') {
36
+ return value;
37
+ }
38
+ if (value.trim() !== '' && Number.isFinite(Number(value))) {
39
+ return String(Number(value));
40
+ }
41
+ return JSON.stringify(value);
42
+ }
43
+ }
44
+ /** The functions of the script sandbox the runtime gets */
45
+ const SANDBOX_FUNCTIONS = [
46
+ 'getStateAsync',
47
+ 'setState',
48
+ 'on',
49
+ 'onStop',
50
+ 'setInterval',
51
+ 'clearInterval',
52
+ 'setTimeout',
53
+ 'clearTimeout',
54
+ 'log',
55
+ 'schedule',
56
+ 'getAstroDate',
57
+ 'sendTo',
58
+ 'registerNotification',
59
+ ];
60
+ /** Parameters that are texts, whatever they look like */
61
+ const TEXT_PARAMS = ['OID', 'ENUM', 'NAME', 'INSTANCE', 'CRON', 'CODE'];
62
+ function initialValue(type) {
63
+ return type === 'BOOL' ? 'false' : type === 'STRING' ? "''" : '0';
64
+ }
65
+ function signal(blockId, pinId) {
66
+ return `S['${blockId}.${pinId}']`;
67
+ }
68
+ class BlockWriter {
69
+ block;
70
+ def;
71
+ drivers;
72
+ inputs;
73
+ outputs;
74
+ constructor(block, def,
75
+ /** `blockId.pinId` of an input to the signal that drives it */
76
+ drivers) {
77
+ this.block = block;
78
+ this.def = def;
79
+ this.drivers = drivers;
80
+ this.inputs = (0, graph_1.getInputs)(block, def);
81
+ this.outputs = (0, graph_1.getOutputs)(block, def);
82
+ }
83
+ input(pin) {
84
+ const inverted = pin.type === 'BOOL' && !!this.block.pins?.[pin.id]?.inverted;
85
+ const driver = this.drivers.get(`${this.block.id}.${pin.id}`);
86
+ if (driver) {
87
+ return inverted ? `!${driver}` : driver;
88
+ }
89
+ const literal = toLiteral(this.block.params?.[pin.id] ?? pin.default, pin.type);
90
+ return inverted ? (literal === 'true' ? 'false' : 'true') : literal;
91
+ }
92
+ param(id) {
93
+ const def = this.def.params?.find(param => param.id === id);
94
+ if (!def) {
95
+ throw new Error(`Block type ${this.def.type} has no parameter "${id}"`);
96
+ }
97
+ const value = this.block.params?.[id] ?? def.default;
98
+ const type = def.type;
99
+ if (typeof type === 'string' && TEXT_PARAMS.includes(type)) {
100
+ return JSON.stringify(value === undefined ? '' : String(value));
101
+ }
102
+ return toLiteral(value, (0, graph_1.resolvePinType)(this.block, type, this.def));
103
+ }
104
+ /** An instance of a user block: its inputs in, one run, its outputs out - all on one line */
105
+ userLine() {
106
+ const instance = `I.${this.block.id}`;
107
+ return [
108
+ ...this.inputs.map(pin => `${instance}.${pin.id} = ${this.input(pin)};`),
109
+ `${instance}.run(dt, firstScan);`,
110
+ ...this.outputs.map(pin => `${signal(this.block.id, pin.id)} = ${instance}.${pin.id};`),
111
+ ].join(' ');
112
+ }
113
+ /** A JS block: the function gets the inputs, its outputs keep what it did not give */
114
+ jsLine() {
115
+ const instance = `I.${this.block.id}`;
116
+ return [
117
+ `${instance}.run([${this.inputs.map(pin => this.input(pin)).join(', ')}], dt, firstScan);`,
118
+ ...this.outputs.map((pin, i) => `${signal(this.block.id, pin.id)} = ${instance}.out[${i}];`),
119
+ ].join(' ');
120
+ }
121
+ line() {
122
+ if (this.def.user) {
123
+ return `${this.userLine()} /*#fb:${this.block.id}*/`;
124
+ }
125
+ if (this.def.type === 'JS') {
126
+ return `${this.jsLine()} /*#fb:${this.block.id}*/`;
127
+ }
128
+ const code = this.def.code.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\*:([^}]*)\}|\{(\$?)([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, prefix, separator, dollar, name) => {
129
+ if (prefix) {
130
+ return this.inputs
131
+ .filter(pin => pin.id.startsWith(prefix))
132
+ .map(pin => this.input(pin))
133
+ .join(separator);
134
+ }
135
+ if (dollar) {
136
+ return this.param(name);
137
+ }
138
+ if (name === 'I') {
139
+ return `I.${this.block.id}`;
140
+ }
141
+ const output = this.outputs.find(pin => pin.id === name);
142
+ if (output) {
143
+ return signal(this.block.id, output.id);
144
+ }
145
+ const input = this.inputs.find(pin => pin.id === name);
146
+ if (input) {
147
+ return this.input(input);
148
+ }
149
+ throw new Error(`Block type ${this.def.type} has no pin "${name}"`);
150
+ });
151
+ return `${code} /*#fb:${this.block.id}*/`;
152
+ }
153
+ }
154
+ /** The code of the blocks of a diagram - of the diagram itself, or of a user block */
155
+ function writeBlocks(graph, analysis, userBlocks) {
156
+ const drivers = new Map();
157
+ analysis.links.forEach(link => drivers.set(`${link.to[0]}.${link.to[1]}`, signal(link.from[0], link.from[1])));
158
+ const code = {
159
+ blocks: graph.blocks
160
+ .filter(block => analysis.order[block.id] !== undefined)
161
+ .sort((a, b) => analysis.order[a.id] - analysis.order[b.id]),
162
+ instances: [],
163
+ signals: [],
164
+ body: [],
165
+ inputs: new Set(),
166
+ outputs: new Set(),
167
+ };
168
+ for (const block of code.blocks) {
169
+ const def = (0, library_1.getBlockDef)(block.type, userBlocks);
170
+ if (def.user) {
171
+ code.instances.push(`${block.id}: ${(0, user_1.factoryName)(def.type)}(), /*#fb:${block.id}*/`);
172
+ }
173
+ else if (def.type === 'JS') {
174
+ // The code as it is, not indented - that would change a text over several lines. Its
175
+ // syntax was checked on its own, so it cannot close the function early.
176
+ code.instances.push(`${block.id}: fb.JS(function (${(0, js_1.jsParameters)(block, def).join(', ')}) { /*#fb:${block.id}*/\n${(0, js_1.jsCode)(block, def)}`, `}, ${(0, graph_1.getOutputs)(block, def).length}),`);
177
+ }
178
+ else if (def.stateful) {
179
+ code.instances.push(`${block.id}: fb.${def.type}(), /*#fb:${block.id}*/`);
180
+ }
181
+ (0, graph_1.getOutputs)(block, def).forEach(pin => code.signals.push(`'${block.id}.${pin.id}': ${initialValue(pin.type)},`));
182
+ code.body.push(new BlockWriter(block, def, drivers).line());
183
+ if (block.type === 'STATE_IN') {
184
+ code.inputs.add(String(block.params?.oid));
185
+ }
186
+ else if (block.type === 'STATE_OUT') {
187
+ code.outputs.add(String(block.params?.oid));
188
+ }
189
+ }
190
+ return code;
191
+ }
192
+ function indent(lines, depth) {
193
+ const spaces = ' '.repeat(depth * 4);
194
+ return lines.map(line => `${spaces}${line}`);
195
+ }
196
+ /** `const name = { ... };`, one entry per line; `depth` 1 is the top level */
197
+ function objectLiteral(name, entries, depth) {
198
+ const lines = entries.length ? [`const ${name} = {`, ...indent(entries, 1), '};'] : [`const ${name} = {};`];
199
+ return indent(lines, depth - 1);
200
+ }
201
+ /**
202
+ * The factory of a user block: every call makes an instance with a state of its own. The inputs and
203
+ * outputs are properties of the instance; `run()` is one cycle of the diagram of the block.
204
+ */
205
+ function writeFactory(user, userBlocks) {
206
+ const analysis = (0, analyze_1.analyzeGraph)(user.graph, { userBlocks, isBlock: true });
207
+ const code = writeBlocks(user.graph, analysis, userBlocks);
208
+ const def = (0, library_1.getBlockDef)(user.type, userBlocks);
209
+ // the pins of a user block always have a signal type of their own
210
+ const pins = [
211
+ ...def.inputs.map(pin => `${pin.id}: ${toLiteral(pin.default, pin.type)}`),
212
+ ...def.outputs.map(pin => `${pin.id}: ${initialValue(pin.type)}`),
213
+ ];
214
+ return [
215
+ `// user block ${JSON.stringify(user.name)} (${user.type}), version ${user.version}`,
216
+ `function ${(0, user_1.factoryName)(user.type)}() {`,
217
+ ` const self = { ${pins.join(', ')} };`,
218
+ ...objectLiteral('I', code.instances, 2),
219
+ ...objectLiteral('S', code.signals, 2),
220
+ // `$` is no part of a pin name, so these never meet a pin
221
+ ' // for the online view: the inside of the instance',
222
+ ' self.$I = I;',
223
+ ' self.$S = S;',
224
+ ' self.run = function (dt, firstScan) {',
225
+ ...indent(code.body, 2),
226
+ ' };',
227
+ ' return self;',
228
+ '}',
229
+ '',
230
+ ];
231
+ }
232
+ /**
233
+ * Turns a diagram into the source of a script.
234
+ *
235
+ * The code keeps no copy of the library blocks: the runtime module implements them, so a fix there
236
+ * reaches every diagram without generating it again. User blocks become factory functions in the
237
+ * code - made of the copies the diagram carries, so a diagram keeps the version it copied. The marks
238
+ * `/*#fb:<id>*\/` lead from a line of the code back to its block.
239
+ *
240
+ * A diagram with errors is stored as well, so no work is lost - but its code only reports the
241
+ * errors instead of running. The diagram of a user block does not run on its own.
242
+ */
243
+ function generateSource(input) {
244
+ const analysis = (0, analyze_1.analyzeGraph)(input);
245
+ // only the copies of the blocks that are used are kept
246
+ const used = (0, user_1.usedUserBlocks)(input, input.userBlocks);
247
+ const userBlocks = {};
248
+ used.forEach(type => (userBlocks[type] = input.userBlocks[type]));
249
+ const graph = {
250
+ ...input,
251
+ format: types_1.FB_FORMAT,
252
+ runtime: types_1.FB_RUNTIME_VERSION,
253
+ blocks: input.blocks.map(block => analysis.order[block.id] !== undefined ? { ...block, order: analysis.order[block.id] } : block),
254
+ };
255
+ if (used.length) {
256
+ graph.userBlocks = userBlocks;
257
+ }
258
+ else {
259
+ delete graph.userBlocks;
260
+ }
261
+ const header = `/*#fb format:${types_1.FB_FORMAT} runtime:${types_1.FB_RUNTIME_VERSION}*/`;
262
+ if (graph.block) {
263
+ const lines = [
264
+ header,
265
+ `// The diagram of the block ${JSON.stringify(graph.block.name)} (${graph.block.type}), version ${graph.block.version}.`,
266
+ '// It does not run on its own: the diagrams that use it carry a copy of it.',
267
+ (0, graph_1.serializeGraph)(graph),
268
+ ];
269
+ return { source: lines.join('\n'), graph, analysis };
270
+ }
271
+ const errors = analysis.issues.filter(issue => issue.severity === 'error');
272
+ if (errors.length) {
273
+ const list = errors.map(analyze_1.formatIssue);
274
+ const lines = [
275
+ header,
276
+ '// This function block diagram has errors and is not executed:',
277
+ ...list.map(text => `// - ${text}`),
278
+ `log(${JSON.stringify(`The function block diagram has errors and is not executed: ${list.join('; ')}`)}, 'error');`,
279
+ (0, graph_1.serializeGraph)(graph),
280
+ ];
281
+ return { source: lines.join('\n'), graph, analysis };
282
+ }
283
+ const code = writeBlocks(graph, analysis, userBlocks);
284
+ const top = [
285
+ header,
286
+ '// Generated from a function block diagram - changes made here are overwritten by the editor',
287
+ `const fb = require('${types_1.FB_RUNTIME_MODULE}');`,
288
+ `const rt = fb.runtime({ ${SANDBOX_FUNCTIONS.join(', ')} }, '${types_1.FB_RUNTIME_VERSION}');`,
289
+ '',
290
+ ...used.flatMap(type => writeFactory(userBlocks[type], userBlocks)),
291
+ '// the blocks that keep a state from one cycle to the next',
292
+ ...objectLiteral('I', code.instances, 1),
293
+ '// signals `<block>.<pin>`: they keep their value too, so a link leading back reads the previous cycle',
294
+ ...objectLiteral('S', code.signals, 1),
295
+ '// breakpoints of the online view: the cycle stops in front of a block whose entry is set',
296
+ 'const B = [];',
297
+ '',
298
+ '// one cycle, from block `at` on - after a stop the runtime goes on there, `go` passes that breakpoint',
299
+ 'function cycle(dt, firstScan, at = 0, go = -1) {',
300
+ ' switch (at) {',
301
+ ]
302
+ .join('\n')
303
+ .split('\n');
304
+ // With these, the runtime finds the block of an error: one line per block, in this order
305
+ const bodyLine = top.length + 1;
306
+ const options = {
307
+ mode: analysis.mode,
308
+ ms: analysis.ms,
309
+ inputs: [...code.inputs],
310
+ outputs: [...code.outputs],
311
+ lines: { start: bodyLine + code.body.length + 4, body: bodyLine },
312
+ blocks: code.blocks.map(block => block.id),
313
+ };
314
+ const lines = [
315
+ ...top,
316
+ ...indent(code.body.map((line, i) => `case ${i}: if (B[${i}] && go !== ${i}) return ${i}; ${line}`), 2),
317
+ ' }',
318
+ ' return -1;',
319
+ '}',
320
+ '',
321
+ `rt.start(cycle, ${JSON.stringify(options)}, S, B, I);`,
322
+ (0, graph_1.serializeGraph)(graph),
323
+ ];
324
+ return { source: lines.join('\n'), graph, analysis };
325
+ }
326
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/lib/fb/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { analyzeGraph, formatIssue, type FbAnalysis } from './analyze';\nimport { getInputs, getOutputs, resolvePinType, serializeGraph, type FbPin } from './graph';\nimport { getBlockDef } from './library';\nimport {\n FB_FORMAT,\n FB_RUNTIME_MODULE,\n FB_RUNTIME_VERSION,\n type FbBlock,\n type FbBlockDef,\n type FbGraph,\n type FbPinType,\n type FbSignalType,\n type FbUserBlock,\n type FbValue,\n} from './types';\nimport { jsCode, jsParameters } from './js';\nimport { parseTime } from './time';\nimport { factoryName, usedUserBlocks } from './user';\n\nexport interface FbGenerated {\n /** The whole script source: the code, and the graph in the last line */\n source: string;\n /** The graph with the execution order written back */\n graph: FbGraph;\n analysis: FbAnalysis;\n}\n\n/** A value as a JavaScript literal of the given type */\nexport function toLiteral(value: FbValue | null | undefined, type: FbSignalType | 'ANY'): string {\n switch (type) {\n case 'BOOL':\n return value === true || value === 'true' || value === 1 || value === '1' ? 'true' : 'false';\n case 'INT':\n return String(Math.round(Number(value)) || 0);\n case 'REAL': {\n const number = Number(value);\n return String(Number.isFinite(number) ? number : 0);\n }\n case 'TIME':\n return String(parseTime(value) ?? 0);\n case 'STRING':\n return JSON.stringify(value === null || value === undefined ? '' : String(value));\n default:\n // ANY: whatever the text looks like\n if (typeof value === 'boolean' || typeof value === 'number') {\n return String(value);\n }\n if (value === null || value === undefined) {\n return '0';\n }\n if (value === 'true' || value === 'false') {\n return value;\n }\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return String(Number(value));\n }\n return JSON.stringify(value);\n }\n}\n\n/** The functions of the script sandbox the runtime gets */\nconst SANDBOX_FUNCTIONS = [\n 'getStateAsync',\n 'setState',\n 'on',\n 'onStop',\n 'setInterval',\n 'clearInterval',\n 'setTimeout',\n 'clearTimeout',\n 'log',\n 'schedule',\n 'getAstroDate',\n 'sendTo',\n 'registerNotification',\n];\n\n/** Parameters that are texts, whatever they look like */\nconst TEXT_PARAMS: string[] = ['OID', 'ENUM', 'NAME', 'INSTANCE', 'CRON', 'CODE'];\n\nfunction initialValue(type: FbSignalType | 'ANY'): string {\n return type === 'BOOL' ? 'false' : type === 'STRING' ? \"''\" : '0';\n}\n\nfunction signal(blockId: string, pinId: string): string {\n return `S['${blockId}.${pinId}']`;\n}\n\nclass BlockWriter {\n private readonly inputs: FbPin[];\n private readonly outputs: FbPin[];\n\n constructor(\n private readonly block: FbBlock,\n private readonly def: FbBlockDef,\n /** `blockId.pinId` of an input to the signal that drives it */\n private readonly drivers: Map<string, string>,\n ) {\n this.inputs = getInputs(block, def);\n this.outputs = getOutputs(block, def);\n }\n\n private input(pin: FbPin): string {\n const inverted = pin.type === 'BOOL' && !!this.block.pins?.[pin.id]?.inverted;\n const driver = this.drivers.get(`${this.block.id}.${pin.id}`);\n if (driver) {\n return inverted ? `!${driver}` : driver;\n }\n const literal = toLiteral(this.block.params?.[pin.id] ?? pin.default, pin.type);\n return inverted ? (literal === 'true' ? 'false' : 'true') : literal;\n }\n\n private param(id: string): string {\n const def = this.def.params?.find(param => param.id === id);\n if (!def) {\n throw new Error(`Block type ${this.def.type} has no parameter \"${id}\"`);\n }\n const value = this.block.params?.[id] ?? def.default;\n const type = def.type;\n if (typeof type === 'string' && TEXT_PARAMS.includes(type)) {\n return JSON.stringify(value === undefined ? '' : String(value));\n }\n return toLiteral(value, resolvePinType(this.block, type as FbPinType, this.def));\n }\n\n /** An instance of a user block: its inputs in, one run, its outputs out - all on one line */\n private userLine(): string {\n const instance = `I.${this.block.id}`;\n return [\n ...this.inputs.map(pin => `${instance}.${pin.id} = ${this.input(pin)};`),\n `${instance}.run(dt, firstScan);`,\n ...this.outputs.map(pin => `${signal(this.block.id, pin.id)} = ${instance}.${pin.id};`),\n ].join(' ');\n }\n\n /** A JS block: the function gets the inputs, its outputs keep what it did not give */\n private jsLine(): string {\n const instance = `I.${this.block.id}`;\n return [\n `${instance}.run([${this.inputs.map(pin => this.input(pin)).join(', ')}], dt, firstScan);`,\n ...this.outputs.map((pin, i) => `${signal(this.block.id, pin.id)} = ${instance}.out[${i}];`),\n ].join(' ');\n }\n\n line(): string {\n if (this.def.user) {\n return `${this.userLine()} /*#fb:${this.block.id}*/`;\n }\n if (this.def.type === 'JS') {\n return `${this.jsLine()} /*#fb:${this.block.id}*/`;\n }\n const code = this.def.code.replace(\n /\\{([A-Za-z_][A-Za-z0-9_]*)\\*:([^}]*)\\}|\\{(\\$?)([A-Za-z_][A-Za-z0-9_]*)\\}/g,\n (_match, prefix?: string, separator?: string, dollar?: string, name?: string): string => {\n if (prefix) {\n return this.inputs\n .filter(pin => pin.id.startsWith(prefix))\n .map(pin => this.input(pin))\n .join(separator);\n }\n if (dollar) {\n return this.param(name!);\n }\n if (name === 'I') {\n return `I.${this.block.id}`;\n }\n const output = this.outputs.find(pin => pin.id === name);\n if (output) {\n return signal(this.block.id, output.id);\n }\n const input = this.inputs.find(pin => pin.id === name);\n if (input) {\n return this.input(input);\n }\n throw new Error(`Block type ${this.def.type} has no pin \"${name}\"`);\n },\n );\n return `${code} /*#fb:${this.block.id}*/`;\n }\n}\n\ninterface Code {\n /** The blocks in the order they run */\n blocks: FbBlock[];\n /** Entries of `I` */\n instances: string[];\n /** Entries of `S` */\n signals: string[];\n /** The lines of a cycle, one per block */\n body: string[];\n inputs: Set<string>;\n outputs: Set<string>;\n}\n\n/** The code of the blocks of a diagram - of the diagram itself, or of a user block */\nfunction writeBlocks(graph: FbGraph, analysis: FbAnalysis, userBlocks: Record<string, FbUserBlock>): Code {\n const drivers = new Map<string, string>();\n analysis.links.forEach(link => drivers.set(`${link.to[0]}.${link.to[1]}`, signal(link.from[0], link.from[1])));\n\n const code: Code = {\n blocks: graph.blocks\n .filter(block => analysis.order[block.id] !== undefined)\n .sort((a, b) => analysis.order[a.id] - analysis.order[b.id]),\n instances: [],\n signals: [],\n body: [],\n inputs: new Set(),\n outputs: new Set(),\n };\n\n for (const block of code.blocks) {\n const def = getBlockDef(block.type, userBlocks)!;\n if (def.user) {\n code.instances.push(`${block.id}: ${factoryName(def.type)}(), /*#fb:${block.id}*/`);\n } else if (def.type === 'JS') {\n // The code as it is, not indented - that would change a text over several lines. Its\n // syntax was checked on its own, so it cannot close the function early.\n code.instances.push(\n `${block.id}: fb.JS(function (${jsParameters(block, def).join(', ')}) { /*#fb:${block.id}*/\\n${jsCode(block, def)}`,\n `}, ${getOutputs(block, def).length}),`,\n );\n } else if (def.stateful) {\n code.instances.push(`${block.id}: fb.${def.type}(), /*#fb:${block.id}*/`);\n }\n getOutputs(block, def).forEach(pin => code.signals.push(`'${block.id}.${pin.id}': ${initialValue(pin.type)},`));\n code.body.push(new BlockWriter(block, def, drivers).line());\n if (block.type === 'STATE_IN') {\n code.inputs.add(String(block.params?.oid));\n } else if (block.type === 'STATE_OUT') {\n code.outputs.add(String(block.params?.oid));\n }\n }\n return code;\n}\n\nfunction indent(lines: string[], depth: number): string[] {\n const spaces = ' '.repeat(depth * 4);\n return lines.map(line => `${spaces}${line}`);\n}\n\n/** `const name = { ... };`, one entry per line; `depth` 1 is the top level */\nfunction objectLiteral(name: string, entries: string[], depth: number): string[] {\n const lines = entries.length ? [`const ${name} = {`, ...indent(entries, 1), '};'] : [`const ${name} = {};`];\n return indent(lines, depth - 1);\n}\n\n/**\n * The factory of a user block: every call makes an instance with a state of its own. The inputs and\n * outputs are properties of the instance; `run()` is one cycle of the diagram of the block.\n */\nfunction writeFactory(user: FbUserBlock, userBlocks: Record<string, FbUserBlock>): string[] {\n const analysis = analyzeGraph(user.graph, { userBlocks, isBlock: true });\n const code = writeBlocks(user.graph, analysis, userBlocks);\n const def = getBlockDef(user.type, userBlocks)!;\n // the pins of a user block always have a signal type of their own\n const pins = [\n ...def.inputs.map(pin => `${pin.id}: ${toLiteral(pin.default, pin.type as FbSignalType)}`),\n ...def.outputs.map(pin => `${pin.id}: ${initialValue(pin.type as FbSignalType)}`),\n ];\n return [\n `// user block ${JSON.stringify(user.name)} (${user.type}), version ${user.version}`,\n `function ${factoryName(user.type)}() {`,\n ` const self = { ${pins.join(', ')} };`,\n ...objectLiteral('I', code.instances, 2),\n ...objectLiteral('S', code.signals, 2),\n // `$` is no part of a pin name, so these never meet a pin\n ' // for the online view: the inside of the instance',\n ' self.$I = I;',\n ' self.$S = S;',\n ' self.run = function (dt, firstScan) {',\n ...indent(code.body, 2),\n ' };',\n ' return self;',\n '}',\n '',\n ];\n}\n\n/**\n * Turns a diagram into the source of a script.\n *\n * The code keeps no copy of the library blocks: the runtime module implements them, so a fix there\n * reaches every diagram without generating it again. User blocks become factory functions in the\n * code - made of the copies the diagram carries, so a diagram keeps the version it copied. The marks\n * `/*#fb:<id>*\\/` lead from a line of the code back to its block.\n *\n * A diagram with errors is stored as well, so no work is lost - but its code only reports the\n * errors instead of running. The diagram of a user block does not run on its own.\n */\nexport function generateSource(input: FbGraph): FbGenerated {\n const analysis = analyzeGraph(input);\n // only the copies of the blocks that are used are kept\n const used = usedUserBlocks(input, input.userBlocks);\n const userBlocks: Record<string, FbUserBlock> = {};\n used.forEach(type => (userBlocks[type] = input.userBlocks![type]));\n\n const graph: FbGraph = {\n ...input,\n format: FB_FORMAT,\n runtime: FB_RUNTIME_VERSION,\n blocks: input.blocks.map(block =>\n analysis.order[block.id] !== undefined ? { ...block, order: analysis.order[block.id] } : block,\n ),\n };\n if (used.length) {\n graph.userBlocks = userBlocks;\n } else {\n delete graph.userBlocks;\n }\n\n const header = `/*#fb format:${FB_FORMAT} runtime:${FB_RUNTIME_VERSION}*/`;\n\n if (graph.block) {\n const lines = [\n header,\n `// The diagram of the block ${JSON.stringify(graph.block.name)} (${graph.block.type}), version ${graph.block.version}.`,\n '// It does not run on its own: the diagrams that use it carry a copy of it.',\n serializeGraph(graph),\n ];\n return { source: lines.join('\\n'), graph, analysis };\n }\n\n const errors = analysis.issues.filter(issue => issue.severity === 'error');\n if (errors.length) {\n const list = errors.map(formatIssue);\n const lines = [\n header,\n '// This function block diagram has errors and is not executed:',\n ...list.map(text => `// - ${text}`),\n `log(${JSON.stringify(`The function block diagram has errors and is not executed: ${list.join('; ')}`)}, 'error');`,\n serializeGraph(graph),\n ];\n return { source: lines.join('\\n'), graph, analysis };\n }\n\n const code = writeBlocks(graph, analysis, userBlocks);\n\n const top = [\n header,\n '// Generated from a function block diagram - changes made here are overwritten by the editor',\n `const fb = require('${FB_RUNTIME_MODULE}');`,\n `const rt = fb.runtime({ ${SANDBOX_FUNCTIONS.join(', ')} }, '${FB_RUNTIME_VERSION}');`,\n '',\n ...used.flatMap(type => writeFactory(userBlocks[type], userBlocks)),\n '// the blocks that keep a state from one cycle to the next',\n ...objectLiteral('I', code.instances, 1),\n '// signals `<block>.<pin>`: they keep their value too, so a link leading back reads the previous cycle',\n ...objectLiteral('S', code.signals, 1),\n '// breakpoints of the online view: the cycle stops in front of a block whose entry is set',\n 'const B = [];',\n '',\n '// one cycle, from block `at` on - after a stop the runtime goes on there, `go` passes that breakpoint',\n 'function cycle(dt, firstScan, at = 0, go = -1) {',\n ' switch (at) {',\n ]\n .join('\\n')\n .split('\\n');\n\n // With these, the runtime finds the block of an error: one line per block, in this order\n const bodyLine = top.length + 1;\n const options = {\n mode: analysis.mode,\n ms: analysis.ms,\n inputs: [...code.inputs],\n outputs: [...code.outputs],\n lines: { start: bodyLine + code.body.length + 4, body: bodyLine },\n blocks: code.blocks.map(block => block.id),\n };\n\n const lines = [\n ...top,\n ...indent(\n code.body.map((line, i) => `case ${i}: if (B[${i}] && go !== ${i}) return ${i}; ${line}`),\n 2,\n ),\n ' }',\n ' return -1;',\n '}',\n '',\n `rt.start(cycle, ${JSON.stringify(options)}, S, B, I);`,\n serializeGraph(graph),\n ];\n\n return { source: lines.join('\\n'), graph, analysis };\n}\n"]}
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FB_ID_PATTERN = exports.FB_CYCLE_MAX_MS = exports.FB_CYCLE_MIN_MS = exports.FB_CYCLE_DEFAULT_MS = exports.FB_GRAPH_MARKER = void 0;
4
+ exports.createGraph = createGraph;
5
+ exports.resolvePinType = resolvePinType;
6
+ exports.getInputCount = getInputCount;
7
+ exports.getInputs = getInputs;
8
+ exports.getOutputCount = getOutputCount;
9
+ exports.getOutputs = getOutputs;
10
+ exports.isCompatible = isCompatible;
11
+ exports.createId = createId;
12
+ exports.createBlock = createBlock;
13
+ exports.normalizeGraph = normalizeGraph;
14
+ exports.parseGraph = parseGraph;
15
+ exports.isFbdSource = isFbdSource;
16
+ exports.serializeGraph = serializeGraph;
17
+ const library_1 = require("./library");
18
+ const types_1 = require("./types");
19
+ /** The last line of the script source holds the graph: `//#fbd:{...}` */
20
+ exports.FB_GRAPH_MARKER = '//#fbd:';
21
+ exports.FB_CYCLE_DEFAULT_MS = 200;
22
+ exports.FB_CYCLE_MIN_MS = 50;
23
+ exports.FB_CYCLE_MAX_MS = 10000;
24
+ /** IDs end up in the generated code (`I.b7`, `S['b7.Q']`), so they are restricted to identifiers */
25
+ exports.FB_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
26
+ const SIGNAL_TYPES = ['BOOL', 'INT', 'REAL', 'TIME', 'STRING'];
27
+ function createGraph() {
28
+ return {
29
+ format: types_1.FB_FORMAT,
30
+ runtime: types_1.FB_RUNTIME_VERSION,
31
+ cycle: { mode: 'auto', ms: exports.FB_CYCLE_DEFAULT_MS },
32
+ blocks: [],
33
+ links: [],
34
+ comments: [],
35
+ };
36
+ }
37
+ /** The signal type a pin has in this block */
38
+ function resolvePinType(block, type, def) {
39
+ if (typeof type === 'object') {
40
+ const value = block.params?.[type.param] ?? def?.params?.find(param => param.id === type.param)?.default;
41
+ return SIGNAL_TYPES.includes(value) ? value : 'REAL';
42
+ }
43
+ return type;
44
+ }
45
+ /** Number of inputs of an extensible block */
46
+ function getInputCount(block, def) {
47
+ if (!def.extensible) {
48
+ return def.inputs.length;
49
+ }
50
+ const count = Number(block.params?.inputs);
51
+ return Math.max(def.extensible.min, Math.min(def.extensible.max, Math.round(Number.isFinite(count) ? count : def.extensible.min)));
52
+ }
53
+ function getInputs(block, def = (0, library_1.getBlockDef)(block.type)) {
54
+ if (!def) {
55
+ return [];
56
+ }
57
+ const inputs = def.inputs.map(pin => ({ ...pin, type: resolvePinType(block, pin.type, def) }));
58
+ if (def.extensible) {
59
+ const { prefix, type, default: defaultValue } = def.extensible;
60
+ for (let i = 1; i <= getInputCount(block, def); i++) {
61
+ inputs.push({ id: `${prefix}${i}`, type: resolvePinType(block, type, def), default: defaultValue });
62
+ }
63
+ }
64
+ return inputs;
65
+ }
66
+ /** Number of outputs of a block with a variable number of them */
67
+ function getOutputCount(block, def) {
68
+ if (!def.extensibleOutputs) {
69
+ return def.outputs.length;
70
+ }
71
+ const { min, max } = def.extensibleOutputs;
72
+ const count = Number(block.params?.outputs);
73
+ return Math.max(min, Math.min(max, Math.round(Number.isFinite(count) ? count : min)));
74
+ }
75
+ function getOutputs(block, def = (0, library_1.getBlockDef)(block.type)) {
76
+ if (!def) {
77
+ return [];
78
+ }
79
+ const outputs = def.outputs.map(pin => ({ ...pin, type: resolvePinType(block, pin.type, def) }));
80
+ if (def.extensibleOutputs) {
81
+ const { prefix, type } = def.extensibleOutputs;
82
+ for (let i = 1; i <= getOutputCount(block, def); i++) {
83
+ outputs.push({ id: `${prefix}${i}`, type: resolvePinType(block, type, def) });
84
+ }
85
+ }
86
+ return outputs;
87
+ }
88
+ /**
89
+ * Whether an output of type `from` may drive an input of type `to`.
90
+ *
91
+ * Only widening happens by itself (INT to REAL); everything else needs a block that converts. An
92
+ * output of the type ANY - of a JS block, whose code decides - goes anywhere.
93
+ */
94
+ function isCompatible(from, to) {
95
+ return to === 'ANY' || from === 'ANY' || from === to || (from === 'INT' && to === 'REAL');
96
+ }
97
+ /** A new ID that is not in `used`: `<prefix><n>` */
98
+ function createId(prefix, used) {
99
+ const taken = new Set(used);
100
+ let max = 0;
101
+ for (const id of taken) {
102
+ if (id.startsWith(prefix)) {
103
+ const n = Number(id.substring(prefix.length));
104
+ if (Number.isInteger(n) && n > max) {
105
+ max = n;
106
+ }
107
+ }
108
+ }
109
+ return `${prefix}${max + 1}`;
110
+ }
111
+ /**
112
+ * A new block of a type, with the parameters at their defaults. A user block must be in
113
+ * `graph.userBlocks` already.
114
+ */
115
+ function createBlock(type, pos, graph) {
116
+ const def = (0, library_1.getBlockDef)(type, graph.userBlocks);
117
+ const id = createId('b', graph.blocks.map(block => block.id));
118
+ // a user block is named after its name, not after `@userFb/...`
119
+ const name = createId(`${def?.user ? def.user.name.trim().replace(/\s+/g, '_') : type}_`, graph.blocks.map(block => block.name));
120
+ const block = { id, type, name, pos };
121
+ const params = {};
122
+ def?.params?.forEach(param => {
123
+ if (param.default !== undefined) {
124
+ params[param.id] = param.default;
125
+ }
126
+ });
127
+ if (def?.extensible) {
128
+ params.inputs = def.extensible.count ?? def.extensible.min;
129
+ }
130
+ if (def?.extensibleOutputs) {
131
+ params.outputs = def.extensibleOutputs.count ?? def.extensibleOutputs.min;
132
+ }
133
+ if (Object.keys(params).length) {
134
+ block.params = params;
135
+ }
136
+ return block;
137
+ }
138
+ /** Brings a parsed graph into shape: missing parts get their defaults */
139
+ function normalizeGraph(data) {
140
+ const graph = createGraph();
141
+ if (!data || typeof data !== 'object') {
142
+ return graph;
143
+ }
144
+ graph.format = typeof data.format === 'number' ? data.format : types_1.FB_FORMAT;
145
+ graph.runtime = typeof data.runtime === 'string' ? data.runtime : types_1.FB_RUNTIME_VERSION;
146
+ if (data.cycle && typeof data.cycle === 'object') {
147
+ graph.cycle = {
148
+ mode: ['auto', 'cyclic', 'event'].includes(data.cycle.mode) ? data.cycle.mode : 'auto',
149
+ ms: typeof data.cycle.ms === 'number' ? data.cycle.ms : exports.FB_CYCLE_DEFAULT_MS,
150
+ };
151
+ }
152
+ graph.blocks = Array.isArray(data.blocks) ? data.blocks.filter(block => block && typeof block === 'object') : [];
153
+ graph.blocks.forEach(block => {
154
+ if (!Array.isArray(block.pos) || block.pos.length !== 2) {
155
+ block.pos = [0, 0];
156
+ }
157
+ if (typeof block.name !== 'string') {
158
+ block.name = String(block.id);
159
+ }
160
+ });
161
+ graph.links = Array.isArray(data.links)
162
+ ? data.links
163
+ .filter(link => link && Array.isArray(link.from) && Array.isArray(link.to))
164
+ .map(link => {
165
+ const clean = { id: link.id, from: link.from, to: link.to };
166
+ if (link.mark === true) {
167
+ clean.mark = true;
168
+ }
169
+ if (typeof link.label === 'string' && link.label.trim()) {
170
+ clean.label = link.label.trim();
171
+ }
172
+ return clean;
173
+ })
174
+ : [];
175
+ graph.comments = Array.isArray(data.comments) ? data.comments.filter(comment => comment?.id) : [];
176
+ const info = (value) => value && typeof value.type === 'string' && typeof value.name === 'string'
177
+ ? {
178
+ type: value.type,
179
+ name: value.name,
180
+ version: typeof value.version === 'number' ? value.version : 1,
181
+ ...(typeof value.description === 'string' ? { description: value.description } : {}),
182
+ }
183
+ : null;
184
+ const block = info(data.block);
185
+ if (block) {
186
+ graph.block = block;
187
+ }
188
+ if (data.userBlocks && typeof data.userBlocks === 'object') {
189
+ const userBlocks = {};
190
+ for (const [type, user] of Object.entries(data.userBlocks)) {
191
+ const userInfo = info(user);
192
+ if (userInfo && userInfo.type === type && user.graph) {
193
+ // the copies carry neither a block info nor copies of their own
194
+ const { block: _block, userBlocks: _inner, ...inner } = normalizeGraph(user.graph);
195
+ userBlocks[type] = { ...userInfo, graph: inner };
196
+ }
197
+ }
198
+ if (Object.keys(userBlocks).length) {
199
+ graph.userBlocks = userBlocks;
200
+ }
201
+ }
202
+ return graph;
203
+ }
204
+ /**
205
+ * Reads the graph out of a script source. Returns `null` when the source holds no graph; an empty
206
+ * source gives an empty graph, which is what a new script starts with.
207
+ */
208
+ function parseGraph(source) {
209
+ if (!source?.trim()) {
210
+ return createGraph();
211
+ }
212
+ const pos = source.lastIndexOf(exports.FB_GRAPH_MARKER);
213
+ if (pos === -1 || (pos > 0 && source[pos - 1] !== '\n')) {
214
+ return null;
215
+ }
216
+ const line = source
217
+ .substring(pos + exports.FB_GRAPH_MARKER.length)
218
+ .split('\n')[0]
219
+ .trim();
220
+ try {
221
+ return normalizeGraph(JSON.parse(line));
222
+ }
223
+ catch {
224
+ return null;
225
+ }
226
+ }
227
+ /** Whether a script source was generated from a function block diagram */
228
+ function isFbdSource(source) {
229
+ if (!source) {
230
+ return false;
231
+ }
232
+ // no trimEnd(): the editor compiles this with an ES2018 library
233
+ const trimmed = source.replace(/\s+$/, '');
234
+ const pos = trimmed.lastIndexOf('\n');
235
+ return trimmed.substring(pos + 1).startsWith(exports.FB_GRAPH_MARKER);
236
+ }
237
+ function serializeGraph(graph) {
238
+ return `${exports.FB_GRAPH_MARKER}${JSON.stringify(graph)}`;
239
+ }
240
+ //# sourceMappingURL=graph.js.map