@synergenius/flow-weaver 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 (466) hide show
  1. package/LICENSE +122 -0
  2. package/README.md +315 -0
  3. package/dist/annotation-generator.d.ts +45 -0
  4. package/dist/annotation-generator.js +557 -0
  5. package/dist/api/builder.d.ts +223 -0
  6. package/dist/api/builder.js +345 -0
  7. package/dist/api/compile.d.ts +92 -0
  8. package/dist/api/compile.js +149 -0
  9. package/dist/api/extract-types.d.ts +29 -0
  10. package/dist/api/extract-types.js +57 -0
  11. package/dist/api/generate-in-place.d.ts +73 -0
  12. package/dist/api/generate-in-place.js +1353 -0
  13. package/dist/api/generate.d.ts +83 -0
  14. package/dist/api/generate.js +510 -0
  15. package/dist/api/helpers.d.ts +248 -0
  16. package/dist/api/helpers.js +285 -0
  17. package/dist/api/index.d.ts +46 -0
  18. package/dist/api/index.js +45 -0
  19. package/dist/api/inline-runtime.d.ts +27 -0
  20. package/dist/api/inline-runtime.js +551 -0
  21. package/dist/api/manipulation/connections.d.ts +79 -0
  22. package/dist/api/manipulation/connections.js +151 -0
  23. package/dist/api/manipulation/index.d.ts +34 -0
  24. package/dist/api/manipulation/index.js +41 -0
  25. package/dist/api/manipulation/node-types.d.ts +123 -0
  26. package/dist/api/manipulation/node-types.js +200 -0
  27. package/dist/api/manipulation/nodes.d.ts +144 -0
  28. package/dist/api/manipulation/nodes.js +333 -0
  29. package/dist/api/manipulation/ports.d.ts +59 -0
  30. package/dist/api/manipulation/ports.js +228 -0
  31. package/dist/api/manipulation/scopes.d.ts +52 -0
  32. package/dist/api/manipulation/scopes.js +156 -0
  33. package/dist/api/manipulation/validation.d.ts +6 -0
  34. package/dist/api/manipulation/validation.js +6 -0
  35. package/dist/api/manipulation/workflow.d.ts +81 -0
  36. package/dist/api/manipulation/workflow.js +116 -0
  37. package/dist/api/manipulation.d.ts +8 -0
  38. package/dist/api/manipulation.js +8 -0
  39. package/dist/api/parse.d.ts +48 -0
  40. package/dist/api/parse.js +110 -0
  41. package/dist/api/patterns.d.ts +112 -0
  42. package/dist/api/patterns.js +306 -0
  43. package/dist/api/query.d.ts +429 -0
  44. package/dist/api/query.js +816 -0
  45. package/dist/api/templates.d.ts +98 -0
  46. package/dist/api/templates.js +117 -0
  47. package/dist/api/transform.d.ts +31 -0
  48. package/dist/api/transform.js +40 -0
  49. package/dist/api/validate.d.ts +25 -0
  50. package/dist/api/validate.js +39 -0
  51. package/dist/api/workflow-file-operations.d.ts +29 -0
  52. package/dist/api/workflow-file-operations.js +180 -0
  53. package/dist/ast/builder.d.ts +210 -0
  54. package/dist/ast/builder.js +395 -0
  55. package/dist/ast/index.d.ts +5 -0
  56. package/dist/ast/index.js +5 -0
  57. package/dist/ast/serialization-node.d.ts +6 -0
  58. package/dist/ast/serialization-node.js +30 -0
  59. package/dist/ast/serialization.d.ts +43 -0
  60. package/dist/ast/serialization.js +134 -0
  61. package/dist/ast/types.d.ts +852 -0
  62. package/dist/ast/types.js +2 -0
  63. package/dist/ast/workflow-utils.d.ts +54 -0
  64. package/dist/ast/workflow-utils.js +114 -0
  65. package/dist/body-generator.d.ts +31 -0
  66. package/dist/body-generator.js +35 -0
  67. package/dist/built-in-nodes/delay.d.ts +11 -0
  68. package/dist/built-in-nodes/delay.js +29 -0
  69. package/dist/built-in-nodes/index.d.ts +5 -0
  70. package/dist/built-in-nodes/index.js +4 -0
  71. package/dist/built-in-nodes/invoke-workflow.d.ts +13 -0
  72. package/dist/built-in-nodes/invoke-workflow.js +25 -0
  73. package/dist/built-in-nodes/mock-types.d.ts +18 -0
  74. package/dist/built-in-nodes/mock-types.js +12 -0
  75. package/dist/built-in-nodes/wait-for-event.d.ts +13 -0
  76. package/dist/built-in-nodes/wait-for-event.js +25 -0
  77. package/dist/chevrotain-parser/connect-parser.d.ts +24 -0
  78. package/dist/chevrotain-parser/connect-parser.js +98 -0
  79. package/dist/chevrotain-parser/grammar-diagrams.d.ts +29 -0
  80. package/dist/chevrotain-parser/grammar-diagrams.js +264 -0
  81. package/dist/chevrotain-parser/index.d.ts +25 -0
  82. package/dist/chevrotain-parser/index.js +27 -0
  83. package/dist/chevrotain-parser/map-parser.d.ts +33 -0
  84. package/dist/chevrotain-parser/map-parser.js +130 -0
  85. package/dist/chevrotain-parser/node-parser.d.ts +36 -0
  86. package/dist/chevrotain-parser/node-parser.js +466 -0
  87. package/dist/chevrotain-parser/path-parser.d.ts +28 -0
  88. package/dist/chevrotain-parser/path-parser.js +118 -0
  89. package/dist/chevrotain-parser/port-parser.d.ts +36 -0
  90. package/dist/chevrotain-parser/port-parser.js +442 -0
  91. package/dist/chevrotain-parser/position-parser.d.ts +20 -0
  92. package/dist/chevrotain-parser/position-parser.js +83 -0
  93. package/dist/chevrotain-parser/scope-parser.d.ts +19 -0
  94. package/dist/chevrotain-parser/scope-parser.js +104 -0
  95. package/dist/chevrotain-parser/tokens.d.ts +78 -0
  96. package/dist/chevrotain-parser/tokens.js +384 -0
  97. package/dist/chevrotain-parser/trigger-cancel-parser.d.ts +50 -0
  98. package/dist/chevrotain-parser/trigger-cancel-parser.js +282 -0
  99. package/dist/cli/commands/changelog.d.ts +13 -0
  100. package/dist/cli/commands/changelog.js +135 -0
  101. package/dist/cli/commands/compile.d.ts +64 -0
  102. package/dist/cli/commands/compile.js +278 -0
  103. package/dist/cli/commands/create.d.ts +33 -0
  104. package/dist/cli/commands/create.js +147 -0
  105. package/dist/cli/commands/describe.d.ts +68 -0
  106. package/dist/cli/commands/describe.js +377 -0
  107. package/dist/cli/commands/dev.d.ts +32 -0
  108. package/dist/cli/commands/dev.js +384 -0
  109. package/dist/cli/commands/diagram.d.ts +13 -0
  110. package/dist/cli/commands/diagram.js +33 -0
  111. package/dist/cli/commands/diff.d.ts +11 -0
  112. package/dist/cli/commands/diff.js +59 -0
  113. package/dist/cli/commands/doctor.d.ts +57 -0
  114. package/dist/cli/commands/doctor.js +719 -0
  115. package/dist/cli/commands/export.d.ts +57 -0
  116. package/dist/cli/commands/export.js +163 -0
  117. package/dist/cli/commands/grammar.d.ts +9 -0
  118. package/dist/cli/commands/grammar.js +39 -0
  119. package/dist/cli/commands/init.d.ts +59 -0
  120. package/dist/cli/commands/init.js +435 -0
  121. package/dist/cli/commands/listen.d.ts +16 -0
  122. package/dist/cli/commands/listen.js +39 -0
  123. package/dist/cli/commands/market.d.ts +52 -0
  124. package/dist/cli/commands/market.js +436 -0
  125. package/dist/cli/commands/migrate.d.ts +13 -0
  126. package/dist/cli/commands/migrate.js +89 -0
  127. package/dist/cli/commands/openapi.d.ts +37 -0
  128. package/dist/cli/commands/openapi.js +67 -0
  129. package/dist/cli/commands/pattern.d.ts +34 -0
  130. package/dist/cli/commands/pattern.js +185 -0
  131. package/dist/cli/commands/plugin.d.ts +16 -0
  132. package/dist/cli/commands/plugin.js +176 -0
  133. package/dist/cli/commands/run.d.ts +49 -0
  134. package/dist/cli/commands/run.js +191 -0
  135. package/dist/cli/commands/serve.d.ts +45 -0
  136. package/dist/cli/commands/serve.js +81 -0
  137. package/dist/cli/commands/templates.d.ts +8 -0
  138. package/dist/cli/commands/templates.js +54 -0
  139. package/dist/cli/commands/ui.d.ts +16 -0
  140. package/dist/cli/commands/ui.js +130 -0
  141. package/dist/cli/commands/validate.d.ts +12 -0
  142. package/dist/cli/commands/validate.js +247 -0
  143. package/dist/cli/commands/watch.d.ts +9 -0
  144. package/dist/cli/commands/watch.js +70 -0
  145. package/dist/cli/flow-weaver.mjs +92924 -0
  146. package/dist/cli/index.d.ts +9 -0
  147. package/dist/cli/index.js +742 -0
  148. package/dist/cli/templates/ai/mock-provider.d.ts +7 -0
  149. package/dist/cli/templates/ai/mock-provider.js +64 -0
  150. package/dist/cli/templates/ai/types.d.ts +47 -0
  151. package/dist/cli/templates/ai/types.js +5 -0
  152. package/dist/cli/templates/approvals/index.d.ts +15 -0
  153. package/dist/cli/templates/approvals/index.js +241 -0
  154. package/dist/cli/templates/index.d.ts +102 -0
  155. package/dist/cli/templates/index.js +101 -0
  156. package/dist/cli/templates/nodes/agent-router.d.ts +3 -0
  157. package/dist/cli/templates/nodes/agent-router.js +114 -0
  158. package/dist/cli/templates/nodes/aggregator.d.ts +7 -0
  159. package/dist/cli/templates/nodes/aggregator.js +63 -0
  160. package/dist/cli/templates/nodes/conversation-memory.d.ts +3 -0
  161. package/dist/cli/templates/nodes/conversation-memory.js +85 -0
  162. package/dist/cli/templates/nodes/http.d.ts +7 -0
  163. package/dist/cli/templates/nodes/http.js +80 -0
  164. package/dist/cli/templates/nodes/human-approval.d.ts +3 -0
  165. package/dist/cli/templates/nodes/human-approval.js +110 -0
  166. package/dist/cli/templates/nodes/json-extractor.d.ts +3 -0
  167. package/dist/cli/templates/nodes/json-extractor.js +119 -0
  168. package/dist/cli/templates/nodes/llm-call.d.ts +3 -0
  169. package/dist/cli/templates/nodes/llm-call.js +106 -0
  170. package/dist/cli/templates/nodes/prompt-template.d.ts +3 -0
  171. package/dist/cli/templates/nodes/prompt-template.js +52 -0
  172. package/dist/cli/templates/nodes/rag-retriever.d.ts +3 -0
  173. package/dist/cli/templates/nodes/rag-retriever.js +128 -0
  174. package/dist/cli/templates/nodes/tool-executor.d.ts +3 -0
  175. package/dist/cli/templates/nodes/tool-executor.js +108 -0
  176. package/dist/cli/templates/nodes/transformer.d.ts +7 -0
  177. package/dist/cli/templates/nodes/transformer.js +68 -0
  178. package/dist/cli/templates/nodes/validator.d.ts +7 -0
  179. package/dist/cli/templates/nodes/validator.js +62 -0
  180. package/dist/cli/templates/providers/index.d.ts +14 -0
  181. package/dist/cli/templates/providers/index.js +239 -0
  182. package/dist/cli/templates/shared/approval-types.d.ts +9 -0
  183. package/dist/cli/templates/shared/approval-types.js +31 -0
  184. package/dist/cli/templates/shared/llm-types.d.ts +15 -0
  185. package/dist/cli/templates/shared/llm-types.js +104 -0
  186. package/dist/cli/templates/workflows/aggregator.d.ts +7 -0
  187. package/dist/cli/templates/workflows/aggregator.js +104 -0
  188. package/dist/cli/templates/workflows/ai-agent-durable.d.ts +8 -0
  189. package/dist/cli/templates/workflows/ai-agent-durable.js +338 -0
  190. package/dist/cli/templates/workflows/ai-agent.d.ts +31 -0
  191. package/dist/cli/templates/workflows/ai-agent.js +326 -0
  192. package/dist/cli/templates/workflows/ai-chat.d.ts +7 -0
  193. package/dist/cli/templates/workflows/ai-chat.js +169 -0
  194. package/dist/cli/templates/workflows/ai-pipeline-durable.d.ts +8 -0
  195. package/dist/cli/templates/workflows/ai-pipeline-durable.js +330 -0
  196. package/dist/cli/templates/workflows/ai-rag.d.ts +7 -0
  197. package/dist/cli/templates/workflows/ai-rag.js +186 -0
  198. package/dist/cli/templates/workflows/ai-react.d.ts +7 -0
  199. package/dist/cli/templates/workflows/ai-react.js +294 -0
  200. package/dist/cli/templates/workflows/conditional.d.ts +12 -0
  201. package/dist/cli/templates/workflows/conditional.js +142 -0
  202. package/dist/cli/templates/workflows/error-handler.d.ts +7 -0
  203. package/dist/cli/templates/workflows/error-handler.js +147 -0
  204. package/dist/cli/templates/workflows/foreach.d.ts +7 -0
  205. package/dist/cli/templates/workflows/foreach.js +143 -0
  206. package/dist/cli/templates/workflows/sequential.d.ts +7 -0
  207. package/dist/cli/templates/workflows/sequential.js +198 -0
  208. package/dist/cli/templates/workflows/webhook.d.ts +7 -0
  209. package/dist/cli/templates/workflows/webhook.js +161 -0
  210. package/dist/cli/utils/logger.d.ts +15 -0
  211. package/dist/cli/utils/logger.js +46 -0
  212. package/dist/constants.d.ts +100 -0
  213. package/dist/constants.js +125 -0
  214. package/dist/defaults.d.ts +3 -0
  215. package/dist/defaults.js +3 -0
  216. package/dist/deployment/config/defaults.d.ts +29 -0
  217. package/dist/deployment/config/defaults.js +98 -0
  218. package/dist/deployment/config/loader.d.ts +24 -0
  219. package/dist/deployment/config/loader.js +236 -0
  220. package/dist/deployment/config/types.d.ts +117 -0
  221. package/dist/deployment/config/types.js +5 -0
  222. package/dist/deployment/core/adapters.d.ts +90 -0
  223. package/dist/deployment/core/adapters.js +251 -0
  224. package/dist/deployment/core/executor.d.ts +62 -0
  225. package/dist/deployment/core/executor.js +197 -0
  226. package/dist/deployment/core/formatters.d.ts +57 -0
  227. package/dist/deployment/core/formatters.js +170 -0
  228. package/dist/deployment/index.d.ts +31 -0
  229. package/dist/deployment/index.js +48 -0
  230. package/dist/deployment/openapi/generator.d.ts +146 -0
  231. package/dist/deployment/openapi/generator.js +347 -0
  232. package/dist/deployment/openapi/schema-converter.d.ts +49 -0
  233. package/dist/deployment/openapi/schema-converter.js +192 -0
  234. package/dist/deployment/targets/base.d.ts +316 -0
  235. package/dist/deployment/targets/base.js +823 -0
  236. package/dist/deployment/targets/cloudflare.d.ts +23 -0
  237. package/dist/deployment/targets/cloudflare.js +1125 -0
  238. package/dist/deployment/targets/inngest.d.ts +38 -0
  239. package/dist/deployment/targets/inngest.js +926 -0
  240. package/dist/deployment/targets/lambda.d.ts +23 -0
  241. package/dist/deployment/targets/lambda.js +1289 -0
  242. package/dist/deployment/targets/vercel.d.ts +23 -0
  243. package/dist/deployment/targets/vercel.js +886 -0
  244. package/dist/deployment/types.d.ts +183 -0
  245. package/dist/deployment/types.js +8 -0
  246. package/dist/diagram/geometry.d.ts +26 -0
  247. package/dist/diagram/geometry.js +850 -0
  248. package/dist/diagram/index.d.ts +16 -0
  249. package/dist/diagram/index.js +42 -0
  250. package/dist/diagram/layout.d.ts +11 -0
  251. package/dist/diagram/layout.js +143 -0
  252. package/dist/diagram/orthogonal-router.d.ts +79 -0
  253. package/dist/diagram/orthogonal-router.js +568 -0
  254. package/dist/diagram/renderer.d.ts +3 -0
  255. package/dist/diagram/renderer.js +207 -0
  256. package/dist/diagram/theme.d.ts +20 -0
  257. package/dist/diagram/theme.js +189 -0
  258. package/dist/diagram/types.d.ts +70 -0
  259. package/dist/diagram/types.js +2 -0
  260. package/dist/diff/WorkflowDiffer.d.ts +13 -0
  261. package/dist/diff/WorkflowDiffer.js +429 -0
  262. package/dist/diff/formatDiff.d.ts +10 -0
  263. package/dist/diff/formatDiff.js +220 -0
  264. package/dist/diff/impact.d.ts +29 -0
  265. package/dist/diff/impact.js +119 -0
  266. package/dist/diff/index.d.ts +10 -0
  267. package/dist/diff/index.js +9 -0
  268. package/dist/diff/types.d.ts +138 -0
  269. package/dist/diff/types.js +35 -0
  270. package/dist/doc-metadata/extractors/annotations.d.ts +56 -0
  271. package/dist/doc-metadata/extractors/annotations.js +337 -0
  272. package/dist/doc-metadata/extractors/cli-commands.d.ts +17 -0
  273. package/dist/doc-metadata/extractors/cli-commands.js +355 -0
  274. package/dist/doc-metadata/extractors/mcp-tools.d.ts +16 -0
  275. package/dist/doc-metadata/extractors/mcp-tools.js +689 -0
  276. package/dist/doc-metadata/extractors/plugin-api.d.ts +19 -0
  277. package/dist/doc-metadata/extractors/plugin-api.js +279 -0
  278. package/dist/doc-metadata/index.d.ts +5 -0
  279. package/dist/doc-metadata/index.js +4 -0
  280. package/dist/doc-metadata/types.d.ts +120 -0
  281. package/dist/doc-metadata/types.js +5 -0
  282. package/dist/editor-completions/annotationValues.d.ts +12 -0
  283. package/dist/editor-completions/annotationValues.js +138 -0
  284. package/dist/editor-completions/contextParser.d.ts +40 -0
  285. package/dist/editor-completions/contextParser.js +410 -0
  286. package/dist/editor-completions/dataTypes.d.ts +16 -0
  287. package/dist/editor-completions/dataTypes.js +95 -0
  288. package/dist/editor-completions/goToDefinition.d.ts +27 -0
  289. package/dist/editor-completions/goToDefinition.js +112 -0
  290. package/dist/editor-completions/index.d.ts +39 -0
  291. package/dist/editor-completions/index.js +181 -0
  292. package/dist/editor-completions/jsDocAnnotations.d.ts +29 -0
  293. package/dist/editor-completions/jsDocAnnotations.js +357 -0
  294. package/dist/editor-completions/modifierCompletions.d.ts +17 -0
  295. package/dist/editor-completions/modifierCompletions.js +197 -0
  296. package/dist/editor-completions/types.d.ts +119 -0
  297. package/dist/editor-completions/types.js +8 -0
  298. package/dist/export/index.d.ts +68 -0
  299. package/dist/export/index.js +1074 -0
  300. package/dist/export/templates.d.ts +24 -0
  301. package/dist/export/templates.js +186 -0
  302. package/dist/friendly-errors.d.ts +35 -0
  303. package/dist/friendly-errors.js +375 -0
  304. package/dist/function-like.d.ts +38 -0
  305. package/dist/function-like.js +83 -0
  306. package/dist/generated-branding.d.ts +16 -0
  307. package/dist/generated-branding.js +22 -0
  308. package/dist/generator/async-detection.d.ts +27 -0
  309. package/dist/generator/async-detection.js +56 -0
  310. package/dist/generator/code-utils.d.ts +76 -0
  311. package/dist/generator/code-utils.js +410 -0
  312. package/dist/generator/control-flow.d.ts +54 -0
  313. package/dist/generator/control-flow.js +284 -0
  314. package/dist/generator/inngest.d.ts +53 -0
  315. package/dist/generator/inngest.js +1126 -0
  316. package/dist/generator/scope-function-generator.d.ts +78 -0
  317. package/dist/generator/scope-function-generator.js +360 -0
  318. package/dist/generator/unified.d.ts +42 -0
  319. package/dist/generator/unified.js +1504 -0
  320. package/dist/generator.d.ts +54 -0
  321. package/dist/generator.js +100 -0
  322. package/dist/index.d.ts +85 -0
  323. package/dist/index.js +89 -0
  324. package/dist/jsdoc-parser.d.ts +308 -0
  325. package/dist/jsdoc-parser.js +923 -0
  326. package/dist/jsdoc-port-sync/constants.d.ts +41 -0
  327. package/dist/jsdoc-port-sync/constants.js +103 -0
  328. package/dist/jsdoc-port-sync/diff.d.ts +76 -0
  329. package/dist/jsdoc-port-sync/diff.js +319 -0
  330. package/dist/jsdoc-port-sync/index.d.ts +42 -0
  331. package/dist/jsdoc-port-sync/index.js +45 -0
  332. package/dist/jsdoc-port-sync/port-parser.d.ts +68 -0
  333. package/dist/jsdoc-port-sync/port-parser.js +579 -0
  334. package/dist/jsdoc-port-sync/rename.d.ts +21 -0
  335. package/dist/jsdoc-port-sync/rename.js +256 -0
  336. package/dist/jsdoc-port-sync/signature-parser.d.ts +104 -0
  337. package/dist/jsdoc-port-sync/signature-parser.js +559 -0
  338. package/dist/jsdoc-port-sync/sync.d.ts +36 -0
  339. package/dist/jsdoc-port-sync/sync.js +644 -0
  340. package/dist/jsdoc-port-sync.d.ts +10 -0
  341. package/dist/jsdoc-port-sync.js +10 -0
  342. package/dist/marketplace/index.d.ts +11 -0
  343. package/dist/marketplace/index.js +10 -0
  344. package/dist/marketplace/manifest.d.ts +32 -0
  345. package/dist/marketplace/manifest.js +176 -0
  346. package/dist/marketplace/registry.d.ts +30 -0
  347. package/dist/marketplace/registry.js +100 -0
  348. package/dist/marketplace/types.d.ts +154 -0
  349. package/dist/marketplace/types.js +9 -0
  350. package/dist/marketplace/validator.d.ts +13 -0
  351. package/dist/marketplace/validator.js +131 -0
  352. package/dist/mcp/auto-registration.d.ts +3 -0
  353. package/dist/mcp/auto-registration.js +62 -0
  354. package/dist/mcp/editor-connection.d.ts +50 -0
  355. package/dist/mcp/editor-connection.js +125 -0
  356. package/dist/mcp/event-buffer.d.ts +62 -0
  357. package/dist/mcp/event-buffer.js +150 -0
  358. package/dist/mcp/index.d.ts +12 -0
  359. package/dist/mcp/index.js +11 -0
  360. package/dist/mcp/resources.d.ts +14 -0
  361. package/dist/mcp/resources.js +55 -0
  362. package/dist/mcp/response-utils.d.ts +63 -0
  363. package/dist/mcp/response-utils.js +89 -0
  364. package/dist/mcp/server.d.ts +4 -0
  365. package/dist/mcp/server.js +99 -0
  366. package/dist/mcp/tools-diagram.d.ts +8 -0
  367. package/dist/mcp/tools-diagram.js +53 -0
  368. package/dist/mcp/tools-editor.d.ts +5 -0
  369. package/dist/mcp/tools-editor.js +190 -0
  370. package/dist/mcp/tools-export.d.ts +9 -0
  371. package/dist/mcp/tools-export.js +180 -0
  372. package/dist/mcp/tools-marketplace.d.ts +9 -0
  373. package/dist/mcp/tools-marketplace.js +132 -0
  374. package/dist/mcp/tools-pattern.d.ts +3 -0
  375. package/dist/mcp/tools-pattern.js +783 -0
  376. package/dist/mcp/tools-query.d.ts +3 -0
  377. package/dist/mcp/tools-query.js +364 -0
  378. package/dist/mcp/tools-template.d.ts +10 -0
  379. package/dist/mcp/tools-template.js +119 -0
  380. package/dist/mcp/types.d.ts +70 -0
  381. package/dist/mcp/types.js +8 -0
  382. package/dist/mcp/workflow-executor.d.ts +47 -0
  383. package/dist/mcp/workflow-executor.js +133 -0
  384. package/dist/migration/registry.d.ts +30 -0
  385. package/dist/migration/registry.js +29 -0
  386. package/dist/node-types-generator.d.ts +49 -0
  387. package/dist/node-types-generator.js +139 -0
  388. package/dist/npm-packages.d.ts +56 -0
  389. package/dist/npm-packages.js +255 -0
  390. package/dist/parser.d.ts +204 -0
  391. package/dist/parser.js +2100 -0
  392. package/dist/plugin/PluginPanel.d.ts +12 -0
  393. package/dist/plugin/PluginPanel.js +5 -0
  394. package/dist/plugin/index.d.ts +13 -0
  395. package/dist/plugin/index.js +14 -0
  396. package/dist/plugin/types.d.ts +75 -0
  397. package/dist/plugin/types.js +8 -0
  398. package/dist/resolve-package-types.d.ts +17 -0
  399. package/dist/resolve-package-types.js +123 -0
  400. package/dist/runtime/CancellationError.d.ts +11 -0
  401. package/dist/runtime/CancellationError.js +20 -0
  402. package/dist/runtime/ExecutionContext.d.ts +146 -0
  403. package/dist/runtime/ExecutionContext.js +235 -0
  404. package/dist/runtime/builtin-functions.d.ts +8 -0
  405. package/dist/runtime/builtin-functions.js +549 -0
  406. package/dist/runtime/events.d.ts +50 -0
  407. package/dist/runtime/events.js +2 -0
  408. package/dist/runtime/function-registry.d.ts +59 -0
  409. package/dist/runtime/function-registry.js +66 -0
  410. package/dist/runtime/index.d.ts +7 -0
  411. package/dist/runtime/index.js +7 -0
  412. package/dist/runtime/parameter-resolver.d.ts +62 -0
  413. package/dist/runtime/parameter-resolver.js +113 -0
  414. package/dist/server/index.d.ts +7 -0
  415. package/dist/server/index.js +6 -0
  416. package/dist/server/types.d.ts +93 -0
  417. package/dist/server/types.js +5 -0
  418. package/dist/server/webhook-server.d.ts +50 -0
  419. package/dist/server/webhook-server.js +269 -0
  420. package/dist/server/workflow-registry.d.ts +61 -0
  421. package/dist/server/workflow-registry.js +202 -0
  422. package/dist/shared-project.d.ts +9 -0
  423. package/dist/shared-project.js +28 -0
  424. package/dist/sugar-optimizer.d.ts +40 -0
  425. package/dist/sugar-optimizer.js +387 -0
  426. package/dist/testing/assertions.d.ts +51 -0
  427. package/dist/testing/assertions.js +127 -0
  428. package/dist/testing/index.d.ts +30 -0
  429. package/dist/testing/index.js +24 -0
  430. package/dist/testing/mock-approval.d.ts +81 -0
  431. package/dist/testing/mock-approval.js +98 -0
  432. package/dist/testing/mock-llm.d.ts +124 -0
  433. package/dist/testing/mock-llm.js +119 -0
  434. package/dist/testing/recorder.d.ts +72 -0
  435. package/dist/testing/recorder.js +70 -0
  436. package/dist/testing/replayer.d.ts +56 -0
  437. package/dist/testing/replayer.js +143 -0
  438. package/dist/testing/token-tracker.d.ts +71 -0
  439. package/dist/testing/token-tracker.js +94 -0
  440. package/dist/type-checker.d.ts +42 -0
  441. package/dist/type-checker.js +190 -0
  442. package/dist/type-mappings.d.ts +29 -0
  443. package/dist/type-mappings.js +125 -0
  444. package/dist/types/branded-ports.d.ts +151 -0
  445. package/dist/types/branded-ports.js +121 -0
  446. package/dist/types/index.d.ts +5 -0
  447. package/dist/types/index.js +5 -0
  448. package/dist/types.d.ts +139 -0
  449. package/dist/types.js +15 -0
  450. package/dist/utils/error-utils.d.ts +15 -0
  451. package/dist/utils/error-utils.js +27 -0
  452. package/dist/utils/lru-cache.d.ts +15 -0
  453. package/dist/utils/lru-cache.js +40 -0
  454. package/dist/utils/port-ordering.d.ts +26 -0
  455. package/dist/utils/port-ordering.js +88 -0
  456. package/dist/utils/port-tag-utils.d.ts +23 -0
  457. package/dist/utils/port-tag-utils.js +41 -0
  458. package/dist/utils/string-distance.d.ts +14 -0
  459. package/dist/utils/string-distance.js +56 -0
  460. package/dist/validation/agent-detection.d.ts +33 -0
  461. package/dist/validation/agent-detection.js +115 -0
  462. package/dist/validation/agent-rules.d.ts +48 -0
  463. package/dist/validation/agent-rules.js +262 -0
  464. package/dist/validator.d.ts +92 -0
  465. package/dist/validator.js +970 -0
  466. package/package.json +109 -0
@@ -0,0 +1,970 @@
1
+ import { RESERVED_NODE_NAMES, isStartNode, isExitNode, isExecutePort, isReservedNodeName, } from './constants.js';
2
+ import { findClosestMatches } from './utils/string-distance.js';
3
+ import { parseFunctionSignature } from './jsdoc-port-sync/signature-parser.js';
4
+ export class WorkflowValidator {
5
+ errors = [];
6
+ warnings = [];
7
+ strictMode = false;
8
+ /** Look up instance sourceLocation by instance ID */
9
+ getInstanceLocation(workflow, instanceId) {
10
+ const instance = workflow.instances.find((inst) => inst.id === instanceId);
11
+ return instance?.sourceLocation;
12
+ }
13
+ /** Look up connection sourceLocation */
14
+ getConnectionLocation(conn) {
15
+ return conn.sourceLocation;
16
+ }
17
+ /**
18
+ * Validate a single node type for scoped port requirements
19
+ *
20
+ * Scoped Port Architecture Rules:
21
+ * - Scope names must be valid JavaScript identifiers
22
+ *
23
+ * Per-Port Scope Architecture:
24
+ * - Scoped OUTPUT ports become callback PARAMETERS (data flows to children)
25
+ * - Scoped INPUT ports become callback RETURN VALUES (data flows from children)
26
+ * - Scoped ports can be ANY data type - they're not functions themselves
27
+ * - The callback function is passed as a function parameter (e.g., forEach's itemProcessor)
28
+ *
29
+ * NOTE: execute/onSuccess/onFailure ports are mandatory base interface ports
30
+ * that are auto-added to ALL nodes - no validation needed for those.
31
+ */
32
+ validateNodeType(nodeType) {
33
+ const errors = [];
34
+ // Get all scoped ports (both INPUT and OUTPUT)
35
+ const scopedPorts = [
36
+ ...Object.entries(nodeType.inputs).filter(([_, portDef]) => portDef.scope !== undefined),
37
+ ...Object.entries(nodeType.outputs).filter(([_, portDef]) => portDef.scope !== undefined),
38
+ ];
39
+ // Rule: Validate scope names are valid JavaScript identifiers
40
+ const scopeNameRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
41
+ scopedPorts.forEach(([portName, portDef]) => {
42
+ if (portDef.scope && !scopeNameRegex.test(portDef.scope)) {
43
+ errors.push(`Port "${portName}" has invalid scope name "${portDef.scope}". Scope names must be valid JavaScript identifiers (letters, numbers, underscore, dollar sign; cannot start with number).`);
44
+ }
45
+ });
46
+ // Note: Scoped ports can be ANY data type in the per-port scope architecture
47
+ // They become callback parameters/returns, not functions themselves
48
+ return errors;
49
+ }
50
+ validate(workflow, options) {
51
+ this.errors = [];
52
+ this.warnings = [];
53
+ this.strictMode = options?.strictMode ?? false;
54
+ const nodeTypeMap = new Map();
55
+ // Map by both functionName and name to support npm nodes (name='npm/pkg/func', functionName='func')
56
+ workflow.nodeTypes.forEach((nodeType) => {
57
+ nodeTypeMap.set(nodeType.functionName, nodeType);
58
+ if (nodeType.name !== nodeType.functionName) {
59
+ nodeTypeMap.set(nodeType.name, nodeType);
60
+ }
61
+ });
62
+ // Build instance map: instance ID -> node type
63
+ const instanceMap = new Map();
64
+ workflow.instances.forEach((instance) => {
65
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
66
+ const nodeType = workflow.nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
67
+ if (nodeType) {
68
+ instanceMap.set(instance.id, nodeType);
69
+ }
70
+ else {
71
+ // Check if the function exists but is unannotated
72
+ const isUnannotatedFunction = workflow.availableFunctionNames?.includes(instance.nodeType) ?? false;
73
+ let hint;
74
+ if (isUnannotatedFunction) {
75
+ hint = ` Function "${instance.nodeType}" exists but has no @flowWeaver nodeType annotation. Add /** @flowWeaver nodeType */ above it.`;
76
+ }
77
+ else {
78
+ const availableTypes = workflow.nodeTypes.map((nt) => nt.functionName);
79
+ const suggestions = findClosestMatches(instance.nodeType, availableTypes);
80
+ hint = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
81
+ }
82
+ this.errors.push({
83
+ type: 'error',
84
+ code: 'UNKNOWN_NODE_TYPE',
85
+ message: `Node "${instance.id}" references unknown node type "${instance.nodeType}".${hint}`,
86
+ node: instance.id,
87
+ location: instance.sourceLocation,
88
+ });
89
+ }
90
+ });
91
+ // Info diagnostic for auto-inferred node types
92
+ workflow.instances.forEach((instance) => {
93
+ // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
94
+ const nodeType = workflow.nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
95
+ if (nodeType?.inferred) {
96
+ this.warnings.push({
97
+ type: 'warning',
98
+ code: 'INFERRED_NODE_TYPE',
99
+ message: `Node type "${instance.nodeType}" was auto-inferred from function signature (expression mode). Add @flowWeaver nodeType for explicit port control.`,
100
+ node: instance.id,
101
+ location: instance.sourceLocation,
102
+ });
103
+ }
104
+ });
105
+ // Structural validation
106
+ this.validateStructure(workflow);
107
+ this.validateDuplicateNodeNames(workflow);
108
+ this.validateMutableBindings(workflow);
109
+ // Connection and node validation
110
+ this.validateReservedNames(workflow, nodeTypeMap);
111
+ this.validateConnections(workflow, instanceMap);
112
+ this.validateNodeReferences(workflow, instanceMap);
113
+ this.validateTypeCompatibility(workflow, instanceMap);
114
+ this.validateRequiredInputs(workflow, instanceMap);
115
+ this.detectUnusedNodes(workflow, instanceMap);
116
+ this.validateStartAndExit(workflow);
117
+ this.validateDataFlow(workflow, instanceMap);
118
+ this.validateCycles(workflow);
119
+ this.validateMultipleInputConnections(workflow, instanceMap);
120
+ this.validateAnnotationSignatureConsistency(workflow);
121
+ // Deduplicate cascading errors: if a node has UNKNOWN_NODE_TYPE,
122
+ // suppress UNKNOWN_SOURCE_NODE, UNKNOWN_TARGET_NODE, and UNDEFINED_NODE
123
+ // that reference the same node IDs (they're just noise).
124
+ const unknownTypeInstanceIds = new Set(workflow.instances.filter((inst) => !nodeTypeMap.has(inst.nodeType)).map((inst) => inst.id));
125
+ if (unknownTypeInstanceIds.size > 0) {
126
+ this.errors = this.errors.filter((error) => {
127
+ if (error.code === 'UNKNOWN_NODE_TYPE')
128
+ return true; // Always keep root cause
129
+ // Suppress cascading errors that reference unknown-type instances
130
+ const cascadingCodes = new Set([
131
+ 'UNKNOWN_SOURCE_NODE',
132
+ 'UNKNOWN_TARGET_NODE',
133
+ 'UNDEFINED_NODE',
134
+ 'MISSING_REQUIRED_INPUT',
135
+ ]);
136
+ if (!cascadingCodes.has(error.code))
137
+ return true;
138
+ // Check if this error references an unknown-type instance
139
+ if (error.node && unknownTypeInstanceIds.has(error.node))
140
+ return false;
141
+ if (error.connection) {
142
+ if (unknownTypeInstanceIds.has(error.connection.from.node))
143
+ return false;
144
+ if (unknownTypeInstanceIds.has(error.connection.to.node))
145
+ return false;
146
+ }
147
+ return true;
148
+ });
149
+ }
150
+ return {
151
+ valid: this.errors.length === 0,
152
+ errors: this.errors,
153
+ warnings: this.warnings,
154
+ };
155
+ }
156
+ validateStructure(workflow) {
157
+ if (!workflow.name) {
158
+ this.errors.push({
159
+ type: 'error',
160
+ code: 'MISSING_WORKFLOW_NAME',
161
+ message: 'Workflow must have a name',
162
+ });
163
+ }
164
+ if (!workflow.functionName) {
165
+ this.errors.push({
166
+ type: 'error',
167
+ code: 'MISSING_FUNCTION_NAME',
168
+ message: 'Workflow must have a functionName',
169
+ });
170
+ }
171
+ }
172
+ validateDuplicateNodeNames(workflow) {
173
+ const nodeNames = new Set();
174
+ workflow.nodeTypes.forEach((nodeType) => {
175
+ if (nodeNames.has(nodeType.functionName)) {
176
+ this.errors.push({
177
+ type: 'error',
178
+ code: 'DUPLICATE_NODE_NAME',
179
+ message: `Duplicate node type name: "${nodeType.functionName}"`,
180
+ node: nodeType.functionName,
181
+ location: nodeType.sourceLocation,
182
+ });
183
+ }
184
+ nodeNames.add(nodeType.functionName);
185
+ });
186
+ }
187
+ validateMutableBindings(workflow) {
188
+ workflow.nodeTypes.forEach((nodeType) => {
189
+ if (nodeType.declarationKind && nodeType.declarationKind !== 'const') {
190
+ this.warnings.push({
191
+ type: 'warning',
192
+ code: 'MUTABLE_NODE_TYPE_BINDING',
193
+ message: `Node type "${nodeType.functionName}" is declared with "${nodeType.declarationKind}" instead of "const". Use "const" to prevent accidental reassignment.`,
194
+ node: nodeType.functionName,
195
+ location: nodeType.sourceLocation,
196
+ });
197
+ }
198
+ });
199
+ }
200
+ validateReservedNames(workflow, nodeTypeMap) {
201
+ // Check for node types with reserved names (Start, Exit)
202
+ // Note: Port name validation is done during parsing
203
+ nodeTypeMap.forEach((nodeType, nodeName) => {
204
+ if (isReservedNodeName(nodeName)) {
205
+ this.errors.push({
206
+ type: 'error',
207
+ code: 'RESERVED_NODE_NAME',
208
+ message: `Node type name "${nodeName}" is reserved. Reserved node names: ${Object.values(RESERVED_NODE_NAMES).join(', ')}`,
209
+ node: nodeName,
210
+ location: nodeType.sourceLocation,
211
+ });
212
+ }
213
+ });
214
+ // Check for instances with reserved IDs
215
+ workflow.instances.forEach((instance) => {
216
+ if (isReservedNodeName(instance.id)) {
217
+ this.errors.push({
218
+ type: 'error',
219
+ code: 'RESERVED_INSTANCE_ID',
220
+ message: `Instance ID "${instance.id}" is reserved. Reserved names: ${Object.values(RESERVED_NODE_NAMES).join(', ')}`,
221
+ node: instance.id,
222
+ location: instance.sourceLocation,
223
+ });
224
+ }
225
+ });
226
+ }
227
+ validateConnections(workflow, instanceMap) {
228
+ workflow.connections.forEach((conn, _index) => {
229
+ const fromNode = conn.from.node;
230
+ const fromPort = conn.from.port;
231
+ const connLocation = this.getConnectionLocation(conn);
232
+ if (!isStartNode(fromNode) && !instanceMap.has(fromNode)) {
233
+ const instanceIds = [...instanceMap.keys()];
234
+ const suggestions = findClosestMatches(fromNode, instanceIds);
235
+ const suggestion = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
236
+ this.errors.push({
237
+ type: 'error',
238
+ code: 'UNKNOWN_SOURCE_NODE',
239
+ message: `Connection references unknown source node: "${fromNode}"${suggestion}`,
240
+ connection: conn,
241
+ location: connLocation,
242
+ });
243
+ }
244
+ const toNode = conn.to.node;
245
+ const toPort = conn.to.port;
246
+ if (!isExitNode(toNode) && !instanceMap.has(toNode)) {
247
+ const instanceIds = [...instanceMap.keys()];
248
+ const suggestions = findClosestMatches(toNode, instanceIds);
249
+ const suggestion = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
250
+ this.errors.push({
251
+ type: 'error',
252
+ code: 'UNKNOWN_TARGET_NODE',
253
+ message: `Connection references unknown target node: "${toNode}"${suggestion}`,
254
+ connection: conn,
255
+ location: connLocation,
256
+ });
257
+ }
258
+ if (!isStartNode(fromNode)) {
259
+ const sourceNode = instanceMap.get(fromNode);
260
+ if (sourceNode && !sourceNode.outputs.hasOwnProperty(fromPort)) {
261
+ const portNames = Object.keys(sourceNode.outputs);
262
+ const suggestions = findClosestMatches(fromPort, portNames);
263
+ const suggestion = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
264
+ this.errors.push({
265
+ type: 'error',
266
+ code: 'UNKNOWN_SOURCE_PORT',
267
+ message: `Node "${fromNode}" does not have output port "${fromPort}"${suggestion}`,
268
+ node: fromNode,
269
+ connection: conn,
270
+ location: connLocation,
271
+ });
272
+ }
273
+ }
274
+ else {
275
+ // Validate Start output port against declared @param ports
276
+ // 'execute' is always implicit on Start
277
+ const validStartPorts = new Set(['execute', ...Object.keys(workflow.startPorts)]);
278
+ if (!validStartPorts.has(fromPort)) {
279
+ const portNames = Array.from(validStartPorts);
280
+ const suggestions = findClosestMatches(fromPort, portNames);
281
+ let hint;
282
+ if (suggestions.length > 0) {
283
+ hint = ` Did you mean "${suggestions[0]}"?`;
284
+ }
285
+ else {
286
+ hint = `\nAdd '@param ${fromPort}' to the workflow JSDoc and include it in the params object:\n(execute: boolean, params: { ${fromPort}: type, ... })`;
287
+ }
288
+ this.errors.push({
289
+ type: 'error',
290
+ code: 'UNKNOWN_SOURCE_PORT',
291
+ message: `Start node does not have output port "${fromPort}".${hint}`,
292
+ node: fromNode,
293
+ connection: conn,
294
+ location: connLocation,
295
+ });
296
+ }
297
+ }
298
+ if (!isExitNode(toNode)) {
299
+ const targetNode = instanceMap.get(toNode);
300
+ if (targetNode && !targetNode.inputs.hasOwnProperty(toPort)) {
301
+ const portNames = Object.keys(targetNode.inputs);
302
+ const suggestions = findClosestMatches(toPort, portNames);
303
+ const suggestion = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
304
+ this.errors.push({
305
+ type: 'error',
306
+ code: 'UNKNOWN_TARGET_PORT',
307
+ message: `Node "${toNode}" does not have input port "${toPort}"${suggestion}`,
308
+ node: toNode,
309
+ connection: conn,
310
+ location: connLocation,
311
+ });
312
+ }
313
+ }
314
+ else {
315
+ // Validate Exit input port against declared @returns ports
316
+ // 'onSuccess' and 'onFailure' are always implicit on Exit
317
+ const validExitPorts = new Set([
318
+ 'onSuccess',
319
+ 'onFailure',
320
+ ...Object.keys(workflow.exitPorts),
321
+ ]);
322
+ if (!validExitPorts.has(toPort)) {
323
+ const portNames = Array.from(validExitPorts);
324
+ const suggestions = findClosestMatches(toPort, portNames);
325
+ const suggestion = suggestions.length > 0 ? ` Did you mean "${suggestions[0]}"?` : '';
326
+ this.errors.push({
327
+ type: 'error',
328
+ code: 'UNKNOWN_TARGET_PORT',
329
+ message: `Exit node does not have input port "${toPort}"${suggestion}`,
330
+ node: toNode,
331
+ connection: conn,
332
+ location: connLocation,
333
+ });
334
+ }
335
+ }
336
+ });
337
+ }
338
+ /**
339
+ * Validate type compatibility for connections with coercion support
340
+ *
341
+ * Type coercion rules:
342
+ * - ANY type connections always allowed (no warning)
343
+ * - Safe coercions: NUMBER → STRING, BOOLEAN → STRING (no warning)
344
+ * - Lossy coercions: STRING → NUMBER, STRING → BOOLEAN, OBJECT → STRING (warning or error in strict mode)
345
+ * - Unusual coercions: NUMBER → BOOLEAN, BOOLEAN → NUMBER (warning or error in strict mode)
346
+ * - Same type connections always allowed (no warning)
347
+ *
348
+ * Strict types mode (@strictTypes):
349
+ * - When enabled, type incompatibilities are errors instead of warnings
350
+ */
351
+ validateTypeCompatibility(workflow, instanceMap) {
352
+ const strictTypes = this.strictMode || workflow.options?.strictTypes === true;
353
+ // Helper to push to errors or warnings based on strictTypes mode
354
+ const pushTypeIssue = (issue, isIncompatible = false) => {
355
+ if (strictTypes && isIncompatible) {
356
+ this.errors.push({ ...issue, type: 'error', code: 'TYPE_INCOMPATIBLE' });
357
+ }
358
+ else {
359
+ this.warnings.push(issue);
360
+ }
361
+ };
362
+ workflow.connections.forEach((conn) => {
363
+ const fromNode = conn.from.node;
364
+ const fromPort = conn.from.port;
365
+ const toNode = conn.to.node;
366
+ const toPort = conn.to.port;
367
+ const connLocation = this.getConnectionLocation(conn);
368
+ // Skip Start and Exit nodes (they handle types dynamically)
369
+ if (isStartNode(fromNode) || isExitNode(toNode)) {
370
+ return;
371
+ }
372
+ // Get source and target port types
373
+ const sourceNode = instanceMap.get(fromNode);
374
+ const targetNode = instanceMap.get(toNode);
375
+ if (!sourceNode || !targetNode) {
376
+ return; // Already caught by validateConnections
377
+ }
378
+ const sourcePortDef = sourceNode.outputs[fromPort];
379
+ const targetPortDef = targetNode.inputs[toPort];
380
+ if (!sourcePortDef || !targetPortDef) {
381
+ return; // Already caught by validateConnections
382
+ }
383
+ const sourceType = sourcePortDef.dataType;
384
+ const targetType = targetPortDef.dataType;
385
+ // Validate STEP port connections - STEP must connect to STEP only
386
+ if (sourceType === 'STEP' && targetType !== 'STEP') {
387
+ this.errors.push({
388
+ type: 'error',
389
+ code: 'STEP_PORT_TYPE_MISMATCH',
390
+ message: `STEP port "${fromPort}" on node "${fromNode}" cannot connect to non-STEP port "${toPort}" (${targetType}) on node "${toNode}"`,
391
+ connection: conn,
392
+ location: connLocation,
393
+ });
394
+ return;
395
+ }
396
+ if (targetType === 'STEP' && sourceType !== 'STEP') {
397
+ this.errors.push({
398
+ type: 'error',
399
+ code: 'STEP_PORT_TYPE_MISMATCH',
400
+ message: `Non-STEP port "${fromPort}" (${sourceType}) on node "${fromNode}" cannot connect to STEP port "${toPort}" on node "${toNode}"`,
401
+ connection: conn,
402
+ location: connLocation,
403
+ });
404
+ return;
405
+ }
406
+ // Skip further type checking for STEP-to-STEP (valid control flow)
407
+ if (sourceType === 'STEP' && targetType === 'STEP') {
408
+ return;
409
+ }
410
+ // Same type - check for structural compatibility if both are OBJECT
411
+ if (sourceType === targetType) {
412
+ // For OBJECT types, check if tsType differs (structural mismatch)
413
+ if (sourceType === 'OBJECT' && sourcePortDef.tsType && targetPortDef.tsType) {
414
+ // If both have tsType and they differ (after normalization), warn about potential mismatch
415
+ if (this.normalizeTypeString(sourcePortDef.tsType) !== this.normalizeTypeString(targetPortDef.tsType)) {
416
+ this.warnings.push({
417
+ type: 'warning',
418
+ code: 'OBJECT_TYPE_MISMATCH',
419
+ message: `Structural type mismatch: ${fromNode}.${fromPort} outputs "${sourcePortDef.tsType}" but ${toNode}.${toPort} expects "${targetPortDef.tsType}". Verify the object shapes are compatible.`,
420
+ connection: conn,
421
+ location: connLocation,
422
+ });
423
+ }
424
+ }
425
+ return;
426
+ }
427
+ // ANY type - always compatible (no warning)
428
+ if (sourceType === 'ANY' || targetType === 'ANY') {
429
+ return;
430
+ }
431
+ // Safe coercions (no warning)
432
+ const safeCoercions = [
433
+ ['NUMBER', 'STRING'],
434
+ ['BOOLEAN', 'STRING'],
435
+ ];
436
+ for (const [from, to] of safeCoercions) {
437
+ if (sourceType === from && targetType === to) {
438
+ return; // Safe coercion, no warning
439
+ }
440
+ }
441
+ // Lossy coercions (warning)
442
+ const lossyCoercions = [
443
+ ['STRING', 'NUMBER', 'May result in NaN if string is not a valid number'],
444
+ ['STRING', 'BOOLEAN', 'Will use JavaScript truthy/falsy conversion'],
445
+ ['OBJECT', 'STRING', 'Will use JSON.stringify()'],
446
+ ['ARRAY', 'STRING', 'Will use JSON.stringify()'],
447
+ ];
448
+ for (const [from, to, reason] of lossyCoercions) {
449
+ if (sourceType === from && targetType === to) {
450
+ pushTypeIssue({
451
+ type: 'warning',
452
+ code: 'LOSSY_TYPE_COERCION',
453
+ message: `Lossy type coercion from ${sourceType} to ${targetType} in connection ${fromNode}.${fromPort} → ${toNode}.${toPort}. ${reason}. Add @strictTypes to your workflow annotation to enforce type safety.`,
454
+ connection: conn,
455
+ location: connLocation,
456
+ }, true);
457
+ return;
458
+ }
459
+ }
460
+ // Unusual coercions (warning)
461
+ const unusualCoercions = [
462
+ [
463
+ 'NUMBER',
464
+ 'BOOLEAN',
465
+ 'Will use JavaScript truthy/falsy conversion (0 = false, non-zero = true)',
466
+ ],
467
+ ['BOOLEAN', 'NUMBER', 'Will convert false to 0, true to 1'],
468
+ ['STRING', 'OBJECT', 'May fail if string is not valid JSON'],
469
+ ['STRING', 'ARRAY', 'May fail if string is not valid JSON array'],
470
+ ];
471
+ for (const [from, to, reason] of unusualCoercions) {
472
+ if (sourceType === from && targetType === to) {
473
+ pushTypeIssue({
474
+ type: 'warning',
475
+ code: 'UNUSUAL_TYPE_COERCION',
476
+ message: `Unusual type coercion from ${sourceType} to ${targetType} in connection ${fromNode}.${fromPort} → ${toNode}.${toPort}. ${reason}.`,
477
+ connection: conn,
478
+ location: connLocation,
479
+ }, true);
480
+ return;
481
+ }
482
+ }
483
+ // All other type mismatches
484
+ pushTypeIssue({
485
+ type: 'warning',
486
+ code: 'TYPE_MISMATCH',
487
+ message: `Type mismatch in connection ${fromNode}.${fromPort} (${sourceType}) → ${toNode}.${toPort} (${targetType}). Runtime coercion will be attempted.`,
488
+ connection: conn,
489
+ location: connLocation,
490
+ }, true);
491
+ });
492
+ }
493
+ validateNodeReferences(workflow, instanceMap) {
494
+ const referencedNodes = new Set();
495
+ workflow.connections.forEach((conn) => {
496
+ const fromNode = conn.from.node;
497
+ const toNode = conn.to.node;
498
+ if (!isStartNode(fromNode) && !isExitNode(fromNode)) {
499
+ referencedNodes.add(fromNode);
500
+ }
501
+ if (!isStartNode(toNode) && !isExitNode(toNode)) {
502
+ referencedNodes.add(toNode);
503
+ }
504
+ });
505
+ referencedNodes.forEach((nodeName) => {
506
+ if (!instanceMap.has(nodeName)) {
507
+ this.errors.push({
508
+ type: 'error',
509
+ code: 'UNDEFINED_NODE',
510
+ message: `Workflow references undefined node: "${nodeName}"`,
511
+ node: nodeName,
512
+ });
513
+ }
514
+ });
515
+ }
516
+ validateRequiredInputs(workflow, instanceMap) {
517
+ instanceMap.forEach((nodeType, instanceId) => {
518
+ // Find the instance to check for port-level constant expressions
519
+ const instance = workflow.instances.find((inst) => inst.id === instanceId);
520
+ Object.entries(nodeType.inputs).forEach(([portName, portConfig]) => {
521
+ if (isExecutePort(portName))
522
+ return;
523
+ // Skip scoped INPUT ports - they're provided by scope function execution, not external connections
524
+ if (portConfig.scope)
525
+ return;
526
+ // Check if instance has an expression for this port
527
+ const instancePortConfig = instance?.config?.portConfigs?.find((pc) => pc.portName === portName && (pc.direction == null || pc.direction === 'INPUT'));
528
+ const hasInstanceExpression = instancePortConfig?.expression !== undefined;
529
+ const isRequired = !portConfig.optional &&
530
+ portConfig.default === undefined &&
531
+ !portConfig.expression &&
532
+ !hasInstanceExpression;
533
+ if (isRequired) {
534
+ const isConnected = workflow.connections.some((conn) => {
535
+ return conn.to.node === instanceId && conn.to.port === portName;
536
+ });
537
+ if (!isConnected) {
538
+ this.errors.push({
539
+ type: 'error',
540
+ code: 'MISSING_REQUIRED_INPUT',
541
+ message: `Node "${instanceId}" has unconnected required input port "${portName}". Connect a value to it, or mark it optional with @input [${portName}].`,
542
+ node: instanceId,
543
+ location: instance?.sourceLocation,
544
+ });
545
+ }
546
+ }
547
+ });
548
+ });
549
+ }
550
+ detectUnusedNodes(workflow, instanceMap) {
551
+ const usedNodes = new Set();
552
+ workflow.connections.forEach((conn) => {
553
+ const fromNode = conn.from.node;
554
+ const toNode = conn.to.node;
555
+ if (!isStartNode(fromNode) && !isExitNode(fromNode)) {
556
+ usedNodes.add(fromNode);
557
+ }
558
+ if (!isStartNode(toNode) && !isExitNode(toNode)) {
559
+ usedNodes.add(toNode);
560
+ }
561
+ });
562
+ instanceMap.forEach((_nodeType, instanceId) => {
563
+ if (!usedNodes.has(instanceId)) {
564
+ this.warnings.push({
565
+ type: 'warning',
566
+ code: 'UNUSED_NODE',
567
+ message: `Node "${instanceId}" is defined but never used in workflow`,
568
+ node: instanceId,
569
+ location: this.getInstanceLocation(workflow, instanceId),
570
+ });
571
+ }
572
+ });
573
+ }
574
+ validateStartAndExit(workflow) {
575
+ const hasStartConnections = workflow.connections.some((conn) => {
576
+ return isStartNode(conn.from.node);
577
+ });
578
+ if (!hasStartConnections) {
579
+ this.warnings.push({
580
+ type: 'warning',
581
+ code: 'NO_START_CONNECTIONS',
582
+ message: 'Workflow has no connections from Start node',
583
+ });
584
+ }
585
+ const hasExitConnections = workflow.connections.some((conn) => {
586
+ return isExitNode(conn.to.node);
587
+ });
588
+ if (!hasExitConnections) {
589
+ this.warnings.push({
590
+ type: 'warning',
591
+ code: 'NO_EXIT_CONNECTIONS',
592
+ message: 'Workflow has no connections to Exit node (no return value)',
593
+ });
594
+ }
595
+ // Validate that onSuccess and onFailure exit ports are STEP type
596
+ if (workflow.exitPorts.onSuccess) {
597
+ if (workflow.exitPorts.onSuccess.dataType !== 'STEP') {
598
+ this.errors.push({
599
+ type: 'error',
600
+ code: 'INVALID_EXIT_PORT_TYPE',
601
+ message: "Exit port 'onSuccess' must be of type STEP (control flow), found: " +
602
+ workflow.exitPorts.onSuccess.dataType,
603
+ });
604
+ }
605
+ }
606
+ if (workflow.exitPorts.onFailure) {
607
+ if (workflow.exitPorts.onFailure.dataType !== 'STEP') {
608
+ this.errors.push({
609
+ type: 'error',
610
+ code: 'INVALID_EXIT_PORT_TYPE',
611
+ message: "Exit port 'onFailure' must be of type STEP (control flow), found: " +
612
+ workflow.exitPorts.onFailure.dataType,
613
+ });
614
+ }
615
+ }
616
+ }
617
+ /**
618
+ * Validate data flow in the workflow
619
+ *
620
+ * Checks for:
621
+ * - Unused output ports (data produced but never consumed)
622
+ * - Unreachable Exit ports (Exit expects data but no connection provides it)
623
+ * - Dead-end data paths (data that never reaches Exit)
624
+ */
625
+ validateDataFlow(workflow, instanceMap) {
626
+ // Track which output ports are connected
627
+ const connectedOutputPorts = new Set();
628
+ workflow.connections.forEach((conn) => {
629
+ const portKey = `${conn.from.node}.${conn.from.port}`;
630
+ connectedOutputPorts.add(portKey);
631
+ });
632
+ // Check for unused output ports (excluding control flow ports and scoped ports)
633
+ instanceMap.forEach((nodeType, instanceId) => {
634
+ Object.keys(nodeType.outputs).forEach((portName) => {
635
+ const portKey = `${instanceId}.${portName}`;
636
+ const portDef = nodeType.outputs[portName];
637
+ // Skip control flow ports (onSuccess, onFailure)
638
+ if (portDef.isControlFlow || portDef.failure) {
639
+ return;
640
+ }
641
+ // Skip scoped output ports - they flow through the scope function, not external connections
642
+ if (portDef.scope) {
643
+ return;
644
+ }
645
+ if (!connectedOutputPorts.has(portKey)) {
646
+ this.warnings.push({
647
+ type: 'warning',
648
+ code: 'UNUSED_OUTPUT_PORT',
649
+ message: `Output port "${portName}" of node "${instanceId}" is never connected. Data will be discarded.`,
650
+ node: instanceId,
651
+ location: this.getInstanceLocation(workflow, instanceId),
652
+ });
653
+ }
654
+ });
655
+ });
656
+ // Check for unreachable Exit ports and multiple connections to same Exit port
657
+ // Build a map of Exit ports to their incoming connections
658
+ const exitPortConnections = new Map();
659
+ workflow.connections.forEach((conn) => {
660
+ if (isExitNode(conn.to.node)) {
661
+ const port = conn.to.port;
662
+ if (!exitPortConnections.has(port)) {
663
+ exitPortConnections.set(port, []);
664
+ }
665
+ exitPortConnections.get(port).push(conn);
666
+ }
667
+ });
668
+ // Check if all Exit ports have connections
669
+ Object.entries(workflow.exitPorts).forEach(([portName, portDef]) => {
670
+ // Skip control flow ports
671
+ if (portDef.isControlFlow) {
672
+ return;
673
+ }
674
+ if (!exitPortConnections.has(portName)) {
675
+ this.warnings.push({
676
+ type: 'warning',
677
+ code: 'UNREACHABLE_EXIT_PORT',
678
+ message: `Exit port "${portName}" has no incoming connection. Return value will be undefined.`,
679
+ });
680
+ }
681
+ });
682
+ // Check for multiple connections to the same Exit port
683
+ exitPortConnections.forEach((connections, portName) => {
684
+ if (connections.length > 1) {
685
+ const sourceNodes = connections.map((c) => c.from.node);
686
+ if (this.areMutuallyExclusive(sourceNodes, workflow, instanceMap)) {
687
+ return; // Suppress — mutually exclusive branches
688
+ }
689
+ const sources = connections.map((c) => `${c.from.node}.${c.from.port}`).join(', ');
690
+ this.warnings.push({
691
+ type: 'warning',
692
+ code: 'MULTIPLE_EXIT_CONNECTIONS',
693
+ message: `Exit port "${portName}" has ${connections.length} incoming connections (${sources}). Only one value will be used - consider using separate Exit ports.`,
694
+ });
695
+ }
696
+ });
697
+ }
698
+ /**
699
+ * Validate for cycles (loops) in the workflow graph.
700
+ * Cycles are connections that form a loop back to an earlier node.
701
+ * Scoped nodes handle loops internally, so cycles within same scope are checked.
702
+ */
703
+ validateCycles(workflow) {
704
+ // Group instances by parent (scope layer)
705
+ const instancesByParent = new Map();
706
+ instancesByParent.set(null, []);
707
+ for (const instance of workflow.instances) {
708
+ const parentId = instance.parent?.id || null;
709
+ if (!instancesByParent.has(parentId)) {
710
+ instancesByParent.set(parentId, []);
711
+ }
712
+ instancesByParent.get(parentId).push(instance);
713
+ }
714
+ // Group connections by parent (only connections where both ends are in same scope)
715
+ const connectionsByParent = new Map();
716
+ connectionsByParent.set(null, []);
717
+ for (const connection of workflow.connections) {
718
+ const sourceInstance = workflow.instances.find((n) => n.id === connection.from.node);
719
+ const targetInstance = workflow.instances.find((n) => n.id === connection.to.node);
720
+ // Skip connections involving Start/Exit (they're virtual)
721
+ if (!sourceInstance || !targetInstance)
722
+ continue;
723
+ const sourceParent = sourceInstance.parent?.id || null;
724
+ const targetParent = targetInstance.parent?.id || null;
725
+ // Only check connections within the same scope
726
+ if (sourceParent === targetParent) {
727
+ if (!connectionsByParent.has(sourceParent)) {
728
+ connectionsByParent.set(sourceParent, []);
729
+ }
730
+ connectionsByParent.get(sourceParent).push(connection);
731
+ }
732
+ }
733
+ // Run cycle detection per scope layer
734
+ for (const [parentId, instances] of instancesByParent.entries()) {
735
+ const connections = connectionsByParent.get(parentId) || [];
736
+ this.detectCyclesInLayer(parentId, instances, connections);
737
+ }
738
+ }
739
+ detectCyclesInLayer(parentId, instances, connections) {
740
+ const visited = new Set();
741
+ const recursionStack = new Set();
742
+ const reportedCycles = new Set(); // Track reported cycles to avoid duplicates
743
+ // Self-loops are allowed (used for iteration patterns)
744
+ const selfLoopNodes = new Set(connections.filter((c) => c.from.node === c.to.node).map((c) => c.from.node));
745
+ // Exclude self-loops from cycle detection
746
+ const nonSelfLoopConnections = connections.filter((c) => c.from.node !== c.to.node);
747
+ const dfs = (nodeName, path) => {
748
+ if (recursionStack.has(nodeName)) {
749
+ // Node with self-loop is not considered a cycle entry point
750
+ if (selfLoopNodes.has(nodeName)) {
751
+ return false;
752
+ }
753
+ const cycleStart = path.indexOf(nodeName);
754
+ const cyclePath = [...path.slice(cycleStart), nodeName];
755
+ // Normalize cycle for deduplication (start from smallest node name)
756
+ const cycleNodes = cyclePath.slice(0, -1); // Remove duplicate end node
757
+ const sortedCycle = [...cycleNodes].sort();
758
+ const cycleKey = sortedCycle.join(',');
759
+ // Only report if not already reported
760
+ if (!reportedCycles.has(cycleKey)) {
761
+ reportedCycles.add(cycleKey);
762
+ const parentContext = parentId ? ` in scope "${parentId}"` : '';
763
+ const instance = instances.find((n) => n.id === nodeName);
764
+ this.errors.push({
765
+ type: 'error',
766
+ code: 'CYCLE_DETECTED',
767
+ message: `Loop detected${parentContext}: ${cyclePath.join(' -> ')}`,
768
+ node: nodeName,
769
+ location: instance?.sourceLocation,
770
+ });
771
+ }
772
+ return true;
773
+ }
774
+ if (visited.has(nodeName)) {
775
+ return false;
776
+ }
777
+ recursionStack.add(nodeName);
778
+ const newPath = [...path, nodeName];
779
+ const instance = instances.find((n) => n.id === nodeName);
780
+ if (!instance) {
781
+ recursionStack.delete(nodeName);
782
+ return false;
783
+ }
784
+ const outgoing = nonSelfLoopConnections.filter((c) => c.from.node === nodeName);
785
+ let hasCycle = false;
786
+ for (const conn of outgoing) {
787
+ if (dfs(conn.to.node, newPath)) {
788
+ hasCycle = true;
789
+ }
790
+ }
791
+ recursionStack.delete(nodeName);
792
+ if (!hasCycle) {
793
+ visited.add(nodeName);
794
+ }
795
+ return hasCycle;
796
+ };
797
+ // Run DFS from each node (but use shared visited/reportedCycles sets)
798
+ for (const instance of instances) {
799
+ if (!visited.has(instance.id)) {
800
+ dfs(instance.id, []);
801
+ }
802
+ }
803
+ }
804
+ /**
805
+ * Validate that no input port has multiple connections.
806
+ * Only one value can be received per input port.
807
+ * (STEP ports can have multiple connections as they're control flow)
808
+ */
809
+ validateMultipleInputConnections(workflow, instanceMap) {
810
+ const inputConnections = new Map();
811
+ for (const conn of workflow.connections) {
812
+ const targetKey = `${conn.to.node}.${conn.to.port}`;
813
+ // Skip Exit node (handled separately in validateDataFlow)
814
+ if (isExitNode(conn.to.node))
815
+ continue;
816
+ // Get target port type to check if it's STEP or has mergeStrategy
817
+ const targetNodeType = instanceMap.get(conn.to.node);
818
+ if (targetNodeType) {
819
+ const targetPortDef = targetNodeType.inputs[conn.to.port];
820
+ // STEP ports can have multiple connections (control flow)
821
+ if (targetPortDef?.dataType === 'STEP')
822
+ continue;
823
+ // Ports with mergeStrategy can have multiple connections (fan-in)
824
+ if (targetPortDef?.mergeStrategy)
825
+ continue;
826
+ }
827
+ if (!inputConnections.has(targetKey)) {
828
+ inputConnections.set(targetKey, []);
829
+ }
830
+ inputConnections.get(targetKey).push(conn);
831
+ }
832
+ for (const [, connections] of inputConnections) {
833
+ if (connections.length > 1) {
834
+ const [firstConn] = connections;
835
+ const sources = connections.map((c) => `${c.from.node}.${c.from.port}`).join(', ');
836
+ this.errors.push({
837
+ type: 'error',
838
+ code: 'MULTIPLE_CONNECTIONS_TO_INPUT',
839
+ message: `Input port "${firstConn.to.port}" on node "${firstConn.to.node}" has ${connections.length} connections (${sources}). Only one value can be received.`,
840
+ node: firstConn.to.node,
841
+ connection: firstConn,
842
+ location: this.getConnectionLocation(firstConn),
843
+ });
844
+ }
845
+ }
846
+ }
847
+ /**
848
+ * Cross-check @input annotations against TypeScript function signatures.
849
+ * Warns on optionality and type mismatches between annotations and actual code.
850
+ */
851
+ validateAnnotationSignatureConsistency(workflow) {
852
+ for (const nodeType of workflow.nodeTypes) {
853
+ if (!nodeType.functionText)
854
+ continue;
855
+ let sigParams;
856
+ try {
857
+ const sig = parseFunctionSignature(nodeType.functionText);
858
+ sigParams = sig.params;
859
+ }
860
+ catch {
861
+ continue; // Can't parse signature, skip
862
+ }
863
+ // Skip the first param (execute: boolean) - it's a control flow param
864
+ const sigParamMap = new Map();
865
+ for (const p of sigParams.slice(1)) {
866
+ sigParamMap.set(p.name, p);
867
+ }
868
+ for (const [portName, portDef] of Object.entries(nodeType.inputs)) {
869
+ if (portName === 'execute')
870
+ continue; // Skip control flow port
871
+ const sigParam = sigParamMap.get(portName);
872
+ if (!sigParam)
873
+ continue; // Port not in signature (may be added by framework)
874
+ // Check optionality mismatch: annotation says required but sig says optional
875
+ if (!portDef.optional && sigParam.optional) {
876
+ this.warnings.push({
877
+ type: 'warning',
878
+ code: 'ANNOTATION_SIGNATURE_MISMATCH',
879
+ message: `Port "${portName}" in node type "${nodeType.functionName}" is optional in signature but required in annotation. Consider using @input [${portName}] to mark it optional.`,
880
+ node: nodeType.functionName,
881
+ location: nodeType.sourceLocation,
882
+ });
883
+ }
884
+ // Check type mismatch: annotation specifies a type that differs from signature
885
+ if (portDef.tsType && sigParam.tsType) {
886
+ const annotationType = this.normalizeTypeString(portDef.tsType);
887
+ const signatureType = this.normalizeTypeString(sigParam.tsType);
888
+ // Skip if no real type info (untyped JS)
889
+ if (!signatureType || signatureType === 'any')
890
+ continue;
891
+ if (annotationType !== signatureType) {
892
+ this.warnings.push({
893
+ type: 'warning',
894
+ code: 'ANNOTATION_SIGNATURE_TYPE_MISMATCH',
895
+ message: `Port "${portName}" in node type "${nodeType.functionName}" has type "${portDef.tsType}" in annotation but "${sigParam.tsType}" in function signature.`,
896
+ node: nodeType.functionName,
897
+ location: nodeType.sourceLocation,
898
+ });
899
+ }
900
+ }
901
+ }
902
+ }
903
+ }
904
+ /**
905
+ * Check if a set of source nodes are mutually exclusive — i.e., they descend
906
+ * from opposite branches (onSuccess vs onFailure) of the same branching node.
907
+ */
908
+ areMutuallyExclusive(sourceNodes, workflow, instanceMap) {
909
+ if (sourceNodes.length < 2)
910
+ return false;
911
+ // Build reverse connection map: targetNode -> [{fromNode, fromPort}]
912
+ const reverseMap = new Map();
913
+ for (const conn of workflow.connections) {
914
+ if (!reverseMap.has(conn.to.node)) {
915
+ reverseMap.set(conn.to.node, []);
916
+ }
917
+ reverseMap.get(conn.to.node).push({ fromNode: conn.from.node, fromPort: conn.from.port });
918
+ }
919
+ const findBranchAncestor = (nodeId) => {
920
+ const visited = new Set();
921
+ const queue = [nodeId];
922
+ while (queue.length > 0) {
923
+ const current = queue.shift();
924
+ if (visited.has(current))
925
+ continue;
926
+ visited.add(current);
927
+ const incomingEdges = reverseMap.get(current);
928
+ if (!incomingEdges)
929
+ continue;
930
+ for (const edge of incomingEdges) {
931
+ // Check if this incoming edge is from a branching port
932
+ if (edge.fromPort === 'onSuccess' || edge.fromPort === 'onFailure') {
933
+ const parentNodeType = instanceMap.get(edge.fromNode);
934
+ if (parentNodeType?.hasSuccessPort && parentNodeType?.hasFailurePort) {
935
+ return { branchNode: edge.fromNode, branch: edge.fromPort };
936
+ }
937
+ }
938
+ queue.push(edge.fromNode);
939
+ }
940
+ }
941
+ return null;
942
+ };
943
+ // Get branch info for all source nodes
944
+ const branchInfos = sourceNodes.map(findBranchAncestor);
945
+ // All must have a branch ancestor
946
+ if (branchInfos.some((info) => info === null))
947
+ return false;
948
+ // All must share the same branch node
949
+ const branchNode = branchInfos[0].branchNode;
950
+ if (!branchInfos.every((info) => info.branchNode === branchNode))
951
+ return false;
952
+ // They must be on different branches (not all on the same one)
953
+ const branches = new Set(branchInfos.map((info) => info.branch));
954
+ return branches.size > 1;
955
+ }
956
+ normalizeTypeString(type) {
957
+ let n = type;
958
+ // Remove all whitespace
959
+ n = n.replace(/\s+/g, '');
960
+ // Normalize Array<T> → T[]
961
+ n = n.replace(/Array<(.+?)>/g, '$1[]');
962
+ // Remove trailing semicolons before closing braces/brackets
963
+ n = n.replace(/;(?=[}\]])/g, '');
964
+ // Lowercase for case-insensitive compare
965
+ n = n.toLowerCase();
966
+ return n;
967
+ }
968
+ }
969
+ export const validator = new WorkflowValidator();
970
+ //# sourceMappingURL=validator.js.map