@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,1353 @@
1
+ /**
2
+ * In-Place Code Generation
3
+ *
4
+ * Generates executable code directly into the source file while preserving
5
+ * user code (node functions). Updates:
6
+ * - Runtime section (between markers)
7
+ * - Workflow JSDoc annotations (when AST changes)
8
+ * - Function body (between markers)
9
+ */
10
+ import { bodyGenerator } from '../body-generator.js';
11
+ import { generateInlineRuntime, generateInlineDebugClient } from './inline-runtime.js';
12
+ import { isExecutePort, isSuccessPort, isFailurePort, isControlFlowPort } from '../constants.js';
13
+ import { generateJSDocPortTag, assignPortOrders, generateNodeInstanceTag, } from '../annotation-generator.js';
14
+ import { shouldWorkflowBeAsync } from '../generator/async-detection.js';
15
+ import { detectSugarPatterns, filterStaleMacros } from '../sugar-optimizer.js';
16
+ import * as ts from 'typescript';
17
+ import * as path from 'path';
18
+ import * as fs from 'fs';
19
+ // Marker constants
20
+ export const MARKERS = {
21
+ RUNTIME_START: '// @flow-weaver-runtime-start',
22
+ RUNTIME_END: '// @flow-weaver-runtime-end',
23
+ BODY_START: '// @flow-weaver-body-start',
24
+ BODY_END: '// @flow-weaver-body-end',
25
+ };
26
+ /**
27
+ * Check if `@synergenius/flow-weaver` is available as an npm package
28
+ * by walking up from the given directory looking for node_modules.
29
+ */
30
+ function isFlowWeaverPackageInstalled(startDir) {
31
+ let dir = startDir;
32
+ const root = path.parse(dir).root;
33
+ while (dir !== root) {
34
+ const candidate = path.join(dir, 'node_modules', '@synergenius', 'flow-weaver');
35
+ try {
36
+ if (fs.existsSync(candidate)) {
37
+ return true;
38
+ }
39
+ }
40
+ catch {
41
+ // Permission error or similar — skip and keep walking
42
+ }
43
+ const parent = path.dirname(dir);
44
+ if (parent === dir)
45
+ break;
46
+ dir = parent;
47
+ }
48
+ return false;
49
+ }
50
+ /**
51
+ * Generate executable code in-place, preserving user code.
52
+ *
53
+ * @param sourceCode - The original source code
54
+ * @param ast - The parsed workflow AST
55
+ * @param options - Generation options
56
+ * @returns The updated source code with generated sections
57
+ */
58
+ export function generateInPlace(sourceCode, ast, options = {}) {
59
+ const { production = false, allWorkflows, moduleFormat = 'esm', inlineRuntime = false, sourceFile, skipParamReturns = false } = options;
60
+ let result = sourceCode;
61
+ let hasChanges = false;
62
+ // Step 1: Update JSDoc annotations for node type functions
63
+ // Skip sibling workflows (variant IMPORTED_WORKFLOW/WORKFLOW) — their JSDoc should not be rewritten
64
+ for (const nodeType of ast.nodeTypes) {
65
+ if (nodeType.variant === 'IMPORTED_WORKFLOW' || nodeType.variant === 'WORKFLOW' || nodeType.variant === 'MAP_ITERATOR') {
66
+ continue;
67
+ }
68
+ // Skip node types imported from other files — the import statement handles them.
69
+ // Inlining would create duplicate declarations (TS2440) and duplicate node type names.
70
+ if (nodeType.sourceLocation?.file &&
71
+ path.resolve(nodeType.sourceLocation.file) !== path.resolve(ast.sourceFile)) {
72
+ continue;
73
+ }
74
+ const nodeTypeResult = replaceNodeTypeJSDoc(result, nodeType);
75
+ if (nodeTypeResult.changed) {
76
+ result = nodeTypeResult.code;
77
+ hasChanges = true;
78
+ }
79
+ }
80
+ // Step 1.5: Remove orphaned nodeType functions (functions that don't match any AST nodeType)
81
+ // When multi-workflow, consider ALL workflows' node types to avoid deleting types used by siblings
82
+ const cleanupResult = removeOrphanedNodeTypeFunctions(result, ast, allWorkflows);
83
+ if (cleanupResult.changed) {
84
+ result = cleanupResult.code;
85
+ hasChanges = true;
86
+ }
87
+ // Step 2: Update JSDoc annotations for workflow function
88
+ const jsdocResult = replaceWorkflowJSDoc(result, ast, { skipParamReturns });
89
+ if (jsdocResult.changed) {
90
+ result = jsdocResult.code;
91
+ hasChanges = true;
92
+ }
93
+ // Step 3: Generate and insert/replace runtime section
94
+ // Auto-detect external runtime unless --inline-runtime is forced
95
+ let useExternalRuntime = false;
96
+ if (!inlineRuntime) {
97
+ const lookupDir = sourceFile ? path.dirname(sourceFile) : (ast.sourceFile ? path.dirname(ast.sourceFile) : process.cwd());
98
+ useExternalRuntime = isFlowWeaverPackageInstalled(lookupDir);
99
+ }
100
+ const externalRuntimePath = useExternalRuntime ? '@synergenius/flow-weaver/runtime' : undefined;
101
+ const runtimeCode = generateRuntimeSection(ast.functionName, production, moduleFormat, externalRuntimePath);
102
+ const runtimeResult = replaceOrInsertSection(result, MARKERS.RUNTIME_START, MARKERS.RUNTIME_END, runtimeCode, 'top');
103
+ if (runtimeResult.changed) {
104
+ result = runtimeResult.code;
105
+ hasChanges = true;
106
+ }
107
+ // Step 4: Ensure function signature includes __abortSignal__ parameter
108
+ const signatureResult = ensureAbortSignalParameter(result, ast.functionName);
109
+ if (signatureResult.changed) {
110
+ result = signatureResult.code;
111
+ hasChanges = true;
112
+ }
113
+ // Step 5: Detect async from node composition + source signature
114
+ // If any node is async, force async (even if source isn't marked async)
115
+ const nodesRequireAsync = shouldWorkflowBeAsync(ast, ast.nodeTypes);
116
+ const sourceIsAsync = detectFunctionIsAsync(result, ast.functionName);
117
+ const isAsync = nodesRequireAsync || sourceIsAsync;
118
+ // Add async keyword to source if nodes require it but source doesn't have it
119
+ const asyncSigResult = ensureAsyncKeyword(result, ast.functionName, nodesRequireAsync);
120
+ if (asyncSigResult.changed) {
121
+ result = asyncSigResult.code;
122
+ hasChanges = true;
123
+ }
124
+ // Step 5b: Wrap return type in Promise<T> when async was added
125
+ const returnTypeResult = ensurePromiseReturnType(result, ast.functionName, nodesRequireAsync);
126
+ if (returnTypeResult.changed) {
127
+ result = returnTypeResult.code;
128
+ hasChanges = true;
129
+ }
130
+ const functionBody = generateFunctionBody(ast, production, isAsync);
131
+ const bodyResult = replaceWorkflowFunctionBody(result, ast.functionName, functionBody);
132
+ if (bodyResult.changed) {
133
+ result = bodyResult.code;
134
+ hasChanges = true;
135
+ }
136
+ // Final check: if the output equals the input, there were no real changes
137
+ // This catches cases where individual steps report changes but produce identical output
138
+ if (hasChanges && result === sourceCode) {
139
+ hasChanges = false;
140
+ }
141
+ return { code: result, hasChanges };
142
+ }
143
+ /**
144
+ * Generate the runtime section with proper markers.
145
+ * When externalRuntimePath is provided, generates import statements instead of inline code.
146
+ */
147
+ function generateRuntimeSection(functionName, production, moduleFormat = 'esm', externalRuntimePath) {
148
+ const lines = [];
149
+ lines.push('// ============================================================================');
150
+ lines.push('// DO NOT EDIT - This section is auto-generated by Flow Weaver');
151
+ lines.push('// ============================================================================');
152
+ lines.push('');
153
+ if (externalRuntimePath) {
154
+ // External runtime: generate import statements instead of inline code
155
+ lines.push(`import { GeneratedExecutionContext, CancellationError } from '${externalRuntimePath}';`);
156
+ if (!production) {
157
+ lines.push(`import type { TDebugger } from '${externalRuntimePath}';`);
158
+ }
159
+ }
160
+ else {
161
+ // Inline runtime: embed all types and classes directly
162
+ lines.push(generateInlineRuntime(production));
163
+ // Add debug client (dev mode only)
164
+ if (!production) {
165
+ lines.push('');
166
+ lines.push(generateInlineDebugClient(moduleFormat));
167
+ }
168
+ }
169
+ return lines.join('\n');
170
+ }
171
+ /**
172
+ * Detect if a workflow function is declared as async
173
+ */
174
+ function detectFunctionIsAsync(source, functionName) {
175
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
176
+ let isAsync = false;
177
+ ts.forEachChild(sourceFile, (node) => {
178
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
179
+ isAsync = !!node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
180
+ }
181
+ });
182
+ return isAsync;
183
+ }
184
+ /**
185
+ * Ensure the workflow function has __abortSignal__ parameter.
186
+ * Adds it if not present.
187
+ */
188
+ function ensureAbortSignalParameter(source, functionName) {
189
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
190
+ let functionNode;
191
+ ts.forEachChild(sourceFile, (node) => {
192
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
193
+ functionNode = node;
194
+ }
195
+ });
196
+ if (!functionNode) {
197
+ return { code: source, changed: false };
198
+ }
199
+ // Check if __abortSignal__ parameter already exists
200
+ const hasAbortSignal = functionNode.parameters.some((param) => ts.isIdentifier(param.name) && param.name.text === '__abortSignal__');
201
+ if (hasAbortSignal) {
202
+ return { code: source, changed: false };
203
+ }
204
+ // Find the closing parenthesis of the parameter list
205
+ const lastParam = functionNode.parameters[functionNode.parameters.length - 1];
206
+ if (!lastParam) {
207
+ // No parameters - find the opening parenthesis and insert after it
208
+ const openParen = source.indexOf('(', functionNode.name?.end || 0);
209
+ if (openParen === -1) {
210
+ return { code: source, changed: false };
211
+ }
212
+ const before = source.slice(0, openParen + 1);
213
+ const after = source.slice(openParen + 1);
214
+ return {
215
+ code: before + '__abortSignal__?: AbortSignal' + after,
216
+ changed: true,
217
+ };
218
+ }
219
+ // Has parameters - insert after the last one with a comma
220
+ const lastParamEnd = lastParam.end;
221
+ const before = source.slice(0, lastParamEnd);
222
+ const after = source.slice(lastParamEnd);
223
+ return {
224
+ code: before + ', __abortSignal__?: AbortSignal' + after,
225
+ changed: true,
226
+ };
227
+ }
228
+ /**
229
+ * Ensure the workflow function has the correct async/non-async modifier.
230
+ * If shouldBeAsync is true and the function is not async, adds the `async` keyword.
231
+ */
232
+ function ensureAsyncKeyword(source, functionName, shouldBeAsync) {
233
+ if (!shouldBeAsync) {
234
+ return { code: source, changed: false };
235
+ }
236
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
237
+ let functionNode;
238
+ ts.forEachChild(sourceFile, (node) => {
239
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
240
+ functionNode = node;
241
+ }
242
+ });
243
+ if (!functionNode) {
244
+ return { code: source, changed: false };
245
+ }
246
+ const alreadyAsync = !!functionNode.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
247
+ if (alreadyAsync) {
248
+ return { code: source, changed: false };
249
+ }
250
+ // Find the 'function' keyword position and insert 'async ' before it
251
+ const funcStart = functionNode.getStart();
252
+ const textAfter = source.slice(funcStart);
253
+ // The text at funcStart starts with optional 'export' then 'function'
254
+ // Insert 'async ' right before 'function'
255
+ const functionKeywordOffset = textAfter.indexOf('function');
256
+ if (functionKeywordOffset === -1) {
257
+ return { code: source, changed: false };
258
+ }
259
+ const insertPos = funcStart + functionKeywordOffset;
260
+ const before = source.slice(0, insertPos);
261
+ const after = source.slice(insertPos);
262
+ return {
263
+ code: before + 'async ' + after,
264
+ changed: true,
265
+ };
266
+ }
267
+ /**
268
+ * Ensure the workflow function's return type is wrapped in Promise<T> when async is required.
269
+ * If shouldBeAsync is true and the return type is not already Promise<...>, wraps it.
270
+ */
271
+ function ensurePromiseReturnType(source, functionName, shouldBeAsync) {
272
+ if (!shouldBeAsync) {
273
+ return { code: source, changed: false };
274
+ }
275
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
276
+ let functionNode;
277
+ ts.forEachChild(sourceFile, (node) => {
278
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
279
+ functionNode = node;
280
+ }
281
+ });
282
+ if (!functionNode || !functionNode.type) {
283
+ return { code: source, changed: false };
284
+ }
285
+ const returnTypeText = source.slice(functionNode.type.pos, functionNode.type.end).trim();
286
+ // Already wrapped in Promise<...>
287
+ if (returnTypeText.startsWith('Promise<')) {
288
+ return { code: source, changed: false };
289
+ }
290
+ const before = source.slice(0, functionNode.type.pos);
291
+ const after = source.slice(functionNode.type.end);
292
+ return {
293
+ code: before + ' Promise<' + returnTypeText + '>' + after,
294
+ changed: true,
295
+ };
296
+ }
297
+ /**
298
+ * Generate the workflow function body
299
+ */
300
+ function generateFunctionBody(ast, production, isAsync) {
301
+ const lines = [];
302
+ lines.push(' // ============================================================================');
303
+ lines.push(' // DO NOT EDIT - This section is auto-generated by Flow Weaver');
304
+ lines.push(' // Edit the @flowWeaver annotations above to modify workflow behavior');
305
+ lines.push(' // ============================================================================');
306
+ lines.push('');
307
+ // Get the generated body from existing body generator
308
+ const body = bodyGenerator.generateWithExecutionContext(ast, ast.nodeTypes, isAsync, // Respect original function's async/sync nature
309
+ production);
310
+ // Add proper indentation
311
+ const indentedBody = body
312
+ .split('\n')
313
+ .map((line) => (line.trim() ? ' ' + line : line))
314
+ .join('\n');
315
+ lines.push(indentedBody);
316
+ return lines.join('\n');
317
+ }
318
+ /**
319
+ * Replace content between markers, or insert if markers don't exist
320
+ */
321
+ function replaceOrInsertSection(source, startMarker, endMarker, newContent, insertPosition) {
322
+ const startIdx = source.indexOf(startMarker);
323
+ const endIdx = source.indexOf(endMarker);
324
+ // If both markers exist, replace content between them
325
+ if (startIdx !== -1 && endIdx !== -1) {
326
+ const before = source.slice(0, startIdx + startMarker.length);
327
+ const after = source.slice(endIdx);
328
+ const newCode = before + '\n' + newContent + '\n' + after;
329
+ // Check if content actually changed
330
+ const originalContent = source.slice(startIdx + startMarker.length, endIdx);
331
+ const changed = originalContent.trim() !== newContent.trim();
332
+ return { code: newCode, changed };
333
+ }
334
+ // Markers don't exist - insert them
335
+ if (insertPosition === 'top') {
336
+ // Insert after imports (if any)
337
+ const insertIdx = findInsertPositionAfterImports(source);
338
+ const before = source.slice(0, insertIdx);
339
+ const after = source.slice(insertIdx);
340
+ const newSection = ['', startMarker, newContent, endMarker, ''].join('\n');
341
+ return {
342
+ code: before + newSection + after,
343
+ changed: true,
344
+ };
345
+ }
346
+ return { code: source, changed: false };
347
+ }
348
+ /**
349
+ * Find the position after all imports in the source code
350
+ */
351
+ function findInsertPositionAfterImports(source) {
352
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
353
+ let lastImportEnd = 0;
354
+ ts.forEachChild(sourceFile, (node) => {
355
+ if (ts.isImportDeclaration(node)) {
356
+ lastImportEnd = node.end;
357
+ }
358
+ });
359
+ // Find the next line after the last import
360
+ if (lastImportEnd > 0) {
361
+ const nextNewline = source.indexOf('\n', lastImportEnd);
362
+ return nextNewline !== -1 ? nextNewline + 1 : lastImportEnd;
363
+ }
364
+ // No imports - insert at the very beginning
365
+ return 0;
366
+ }
367
+ /**
368
+ * Replace the body of a workflow function with generated code
369
+ */
370
+ function replaceWorkflowFunctionBody(source, functionName, newBody) {
371
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
372
+ let functionNode;
373
+ // Find the workflow function
374
+ ts.forEachChild(sourceFile, (node) => {
375
+ if (ts.isFunctionDeclaration(node) && node.name?.text === functionName) {
376
+ functionNode = node;
377
+ }
378
+ });
379
+ if (!functionNode || !functionNode.body) {
380
+ return { code: source, changed: false };
381
+ }
382
+ // Check if body already has markers
383
+ const bodyText = source.slice(functionNode.body.pos, functionNode.body.end);
384
+ const bodyStartMarkerIdx = bodyText.indexOf(MARKERS.BODY_START);
385
+ const bodyEndMarkerIdx = bodyText.indexOf(MARKERS.BODY_END);
386
+ if (bodyStartMarkerIdx !== -1 && bodyEndMarkerIdx !== -1) {
387
+ // Replace content between body markers
388
+ const absoluteBodyStart = functionNode.body.pos + bodyStartMarkerIdx + MARKERS.BODY_START.length;
389
+ const absoluteBodyEnd = functionNode.body.pos + bodyEndMarkerIdx;
390
+ const before = source.slice(0, absoluteBodyStart);
391
+ const after = source.slice(absoluteBodyEnd);
392
+ const newCode = before + '\n' + newBody + '\n ' + after;
393
+ // Check if content changed
394
+ const originalBody = source.slice(absoluteBodyStart, absoluteBodyEnd);
395
+ const changed = originalBody.trim() !== newBody.trim();
396
+ return { code: newCode, changed };
397
+ }
398
+ // No markers in body - insert them
399
+ // Find the opening brace of the function body
400
+ const openBraceIdx = source.indexOf('{', functionNode.body.pos);
401
+ if (openBraceIdx === -1) {
402
+ return { code: source, changed: false };
403
+ }
404
+ // Find the closing brace
405
+ const closeBraceIdx = functionNode.body.end - 1;
406
+ const before = source.slice(0, openBraceIdx + 1);
407
+ const after = source.slice(closeBraceIdx);
408
+ const newBodyWithMarkers = [
409
+ '',
410
+ ' ' + MARKERS.BODY_START,
411
+ newBody,
412
+ ' ' + MARKERS.BODY_END,
413
+ '',
414
+ ].join('\n');
415
+ return {
416
+ code: before + newBodyWithMarkers + after,
417
+ changed: true,
418
+ };
419
+ }
420
+ /**
421
+ * Rename a function in the provided code to a new name.
422
+ * Handles both regular functions and arrow functions.
423
+ */
424
+ function renameFunctionInCode(code, newName) {
425
+ const codeSourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
426
+ let functionName;
427
+ let functionNameStart;
428
+ let functionNameEnd;
429
+ // Find the function declaration
430
+ ts.forEachChild(codeSourceFile, (node) => {
431
+ if (ts.isFunctionDeclaration(node) && node.name) {
432
+ functionName = node.name.text;
433
+ functionNameStart = node.name.getStart();
434
+ functionNameEnd = node.name.getEnd();
435
+ }
436
+ });
437
+ if (!functionName || functionNameStart === undefined || functionNameEnd === undefined) {
438
+ // No function found - return as-is
439
+ return code;
440
+ }
441
+ if (functionName === newName) {
442
+ // Already has the correct name
443
+ return code;
444
+ }
445
+ // Replace the function name
446
+ const before = code.slice(0, functionNameStart);
447
+ const after = code.slice(functionNameEnd);
448
+ return before + newName + after;
449
+ }
450
+ /**
451
+ * Insert a new node type function into the source code.
452
+ * Inserts before the workflow function (exported function).
453
+ * Renames the function to match nodeType.functionName if different.
454
+ */
455
+ function insertNodeTypeFunction(source, nodeType, functionCode) {
456
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
457
+ // Find position AFTER the runtime section end marker to avoid being overwritten
458
+ // when the runtime section is replaced
459
+ const runtimeEndMarker = '// @flow-weaver-runtime-end';
460
+ const runtimeEndPos = source.indexOf(runtimeEndMarker);
461
+ let insertPosition = -1;
462
+ if (runtimeEndPos !== -1) {
463
+ // Insert after the runtime-end marker line
464
+ const afterMarker = source.indexOf('\n', runtimeEndPos);
465
+ insertPosition = afterMarker !== -1 ? afterMarker + 1 : runtimeEndPos + runtimeEndMarker.length;
466
+ }
467
+ else {
468
+ // No runtime marker - find the first exported function to insert before it
469
+ let foundExportedFunction = false;
470
+ ts.forEachChild(sourceFile, (node) => {
471
+ if (!foundExportedFunction &&
472
+ ts.isFunctionDeclaration(node) &&
473
+ node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
474
+ insertPosition = node.getFullStart();
475
+ foundExportedFunction = true;
476
+ }
477
+ });
478
+ if (insertPosition === -1) {
479
+ insertPosition = source.length;
480
+ }
481
+ }
482
+ // Rename the function in the code to match nodeType.functionName
483
+ const renamedCode = renameFunctionInCode(functionCode, nodeType.functionName);
484
+ // The functionCode should already have JSDoc, but ensure it does
485
+ const hasJSDoc = renamedCode.trim().startsWith('/**');
486
+ let finalCode = renamedCode;
487
+ if (!hasJSDoc) {
488
+ // Generate JSDoc for the function
489
+ const jsdoc = generateNodeTypeJSDoc(nodeType);
490
+ finalCode = jsdoc + '\n' + renamedCode;
491
+ }
492
+ // Insert the function with proper spacing
493
+ const before = source.slice(0, insertPosition);
494
+ const after = source.slice(insertPosition);
495
+ // Ensure proper newlines
496
+ const needsLeadingNewline = before.length > 0 && !before.endsWith('\n\n');
497
+ const needsTrailingNewline = after.length > 0 && !after.startsWith('\n');
498
+ const newCode = before +
499
+ (needsLeadingNewline ? '\n\n' : '') +
500
+ finalCode +
501
+ (needsTrailingNewline ? '\n\n' : '') +
502
+ after;
503
+ return { code: newCode, changed: true };
504
+ }
505
+ /**
506
+ * Remove nodeType functions that don't match any AST nodeType.
507
+ * This cleans up orphaned functions left behind after renames.
508
+ */
509
+ function removeOrphanedNodeTypeFunctions(source, ast, allWorkflows) {
510
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
511
+ // Get all valid functionNames from AST — include ALL workflows' node types when available
512
+ const validFunctionNames = new Set(ast.nodeTypes.map((nt) => nt.functionName));
513
+ if (allWorkflows) {
514
+ for (const workflow of allWorkflows) {
515
+ for (const nt of workflow.nodeTypes) {
516
+ validFunctionNames.add(nt.functionName);
517
+ }
518
+ }
519
+ }
520
+ // Find all nodeType functions to potentially remove
521
+ const functionsToRemove = [];
522
+ ts.forEachChild(sourceFile, (node) => {
523
+ if (ts.isFunctionDeclaration(node) && node.name) {
524
+ const functionName = node.name.text;
525
+ // Check if this function has @flowWeaver nodeType JSDoc
526
+ const functionStart = node.getFullStart();
527
+ const leadingComments = ts.getLeadingCommentRanges(source, functionStart);
528
+ if (!leadingComments)
529
+ return;
530
+ let isNodeTypeFunction = false;
531
+ let jsdocStart = functionStart;
532
+ for (const comment of leadingComments) {
533
+ if (comment.kind === ts.SyntaxKind.MultiLineCommentTrivia) {
534
+ const commentText = source.slice(comment.pos, comment.end);
535
+ if (commentText.includes('@flowWeaver nodeType')) {
536
+ isNodeTypeFunction = true;
537
+ jsdocStart = comment.pos;
538
+ break;
539
+ }
540
+ }
541
+ }
542
+ if (!isNodeTypeFunction)
543
+ return;
544
+ // If this nodeType function's name doesn't match any valid functionName, mark for removal
545
+ if (!validFunctionNames.has(functionName)) {
546
+ // Find the start (including JSDoc) and end of the function
547
+ const fullStart = jsdocStart;
548
+ const fullEnd = node.end;
549
+ // Include any trailing newlines
550
+ let endPos = fullEnd;
551
+ while (endPos < source.length && (source[endPos] === '\n' || source[endPos] === '\r')) {
552
+ endPos++;
553
+ }
554
+ functionsToRemove.push({ start: fullStart, end: endPos });
555
+ }
556
+ }
557
+ });
558
+ if (functionsToRemove.length === 0) {
559
+ return { code: source, changed: false };
560
+ }
561
+ // Remove functions from end to start to preserve positions
562
+ let result = source;
563
+ for (const { start, end } of functionsToRemove.sort((a, b) => b.start - a.start)) {
564
+ result = result.slice(0, start) + result.slice(end);
565
+ }
566
+ return { code: result, changed: true };
567
+ }
568
+ /**
569
+ * Find a function by @name tag in its JSDoc comment.
570
+ * Returns the function node and the @name value if found.
571
+ */
572
+ function findFunctionByNameTag(source, sourceFile, targetName) {
573
+ let result;
574
+ ts.forEachChild(sourceFile, (node) => {
575
+ if (ts.isFunctionDeclaration(node) && node.name) {
576
+ // Check if this function has a @name tag matching targetName
577
+ const functionStart = node.getFullStart();
578
+ const leadingComments = ts.getLeadingCommentRanges(source, functionStart);
579
+ if (leadingComments) {
580
+ for (const comment of leadingComments) {
581
+ if (comment.kind === ts.SyntaxKind.MultiLineCommentTrivia) {
582
+ const commentText = source.slice(comment.pos, comment.end);
583
+ // Look for @name tag
584
+ const nameMatch = commentText.match(/@name\s+(\S+)/);
585
+ if (nameMatch && nameMatch[1] === targetName) {
586
+ result = node;
587
+ return;
588
+ }
589
+ }
590
+ }
591
+ }
592
+ }
593
+ });
594
+ return result;
595
+ }
596
+ /**
597
+ * Replace a node type function's JSDoc comment with updated annotations.
598
+ * If the function doesn't exist but nodeType has code/functionText, INSERT the function.
599
+ * Handles function renames by looking up @name tag when functionName doesn't match.
600
+ */
601
+ function replaceNodeTypeJSDoc(source, nodeType) {
602
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
603
+ let functionNode;
604
+ let needsRename = false;
605
+ // First, try to find the function by its functionName
606
+ ts.forEachChild(sourceFile, (node) => {
607
+ if (ts.isFunctionDeclaration(node) && node.name?.text === nodeType.functionName) {
608
+ functionNode = node;
609
+ }
610
+ });
611
+ // If not found by functionName, try to find by @name tag (handles renames)
612
+ if (!functionNode && nodeType.name) {
613
+ functionNode = findFunctionByNameTag(source, sourceFile, nodeType.name);
614
+ if (functionNode && functionNode.name?.text !== nodeType.functionName) {
615
+ needsRename = true;
616
+ }
617
+ }
618
+ // If still not found and name !== functionName, try finding by name as function name
619
+ // This handles the case where no @name tag exists yet (first rename after creation)
620
+ if (!functionNode && nodeType.name && nodeType.name !== nodeType.functionName) {
621
+ ts.forEachChild(sourceFile, (node) => {
622
+ if (ts.isFunctionDeclaration(node) &&
623
+ node.name?.text === nodeType.name // Find by stable identifier as function name
624
+ ) {
625
+ functionNode = node;
626
+ needsRename = true;
627
+ }
628
+ });
629
+ }
630
+ if (!functionNode) {
631
+ // Function doesn't exist - try to INSERT it if we have the code
632
+ // Check for code property (dynamically added in some contexts) or functionText
633
+ const nodeTypeWithCode = nodeType;
634
+ const functionCode = nodeTypeWithCode.code || nodeType.functionText;
635
+ if (functionCode) {
636
+ return insertNodeTypeFunction(source, nodeType, functionCode);
637
+ }
638
+ return { code: source, changed: false };
639
+ }
640
+ let result = source;
641
+ let hasChanges = false;
642
+ // If function needs to be renamed (found by @name but has old functionName)
643
+ if (needsRename && functionNode.name) {
644
+ const nameStart = functionNode.name.getStart();
645
+ const nameEnd = functionNode.name.getEnd();
646
+ result = result.slice(0, nameStart) + nodeType.functionName + result.slice(nameEnd);
647
+ hasChanges = true;
648
+ // Re-parse to get updated positions after rename
649
+ const updatedSourceFile = ts.createSourceFile('temp.ts', result, ts.ScriptTarget.Latest, true);
650
+ // Find the renamed function
651
+ ts.forEachChild(updatedSourceFile, (node) => {
652
+ if (ts.isFunctionDeclaration(node) && node.name?.text === nodeType.functionName) {
653
+ functionNode = node;
654
+ }
655
+ });
656
+ if (!functionNode) {
657
+ return { code: result, changed: hasChanges };
658
+ }
659
+ }
660
+ // Find the JSDoc comment before the function
661
+ const functionStart = functionNode.getFullStart();
662
+ const leadingComments = ts.getLeadingCommentRanges(result, functionStart);
663
+ if (!leadingComments || leadingComments.length === 0) {
664
+ return { code: result, changed: hasChanges };
665
+ }
666
+ // Find ALL /** JSDoc comments in the leading trivia and separate them:
667
+ // - flowWeaverJSDocs: contain @flowWeaver (these are node type annotations)
668
+ // - The LAST one is the primary JSDoc to replace
669
+ // - Any earlier @flowWeaver JSDoc blocks are stale duplicates to remove
670
+ const jsdocComments = [];
671
+ for (const c of leadingComments) {
672
+ if (c.kind === ts.SyntaxKind.MultiLineCommentTrivia &&
673
+ result.slice(c.pos, c.pos + 3) === '/**') {
674
+ jsdocComments.push(c);
675
+ }
676
+ }
677
+ if (jsdocComments.length === 0) {
678
+ return { code: result, changed: hasChanges };
679
+ }
680
+ // Use the LAST /** comment as the primary JSDoc (closest to the function).
681
+ // This avoids picking up file headers that also start with /**.
682
+ const jsdocComment = jsdocComments[jsdocComments.length - 1];
683
+ // Detect stale duplicate @flowWeaver JSDoc blocks from previous buggy compilations.
684
+ // Any earlier /** that contains @flowWeaver is a stale duplicate to remove.
685
+ const staleDuplicates = [];
686
+ for (let i = 0; i < jsdocComments.length - 1; i++) {
687
+ const text = result.slice(jsdocComments[i].pos, jsdocComments[i].end);
688
+ if (text.includes('@flowWeaver')) {
689
+ staleDuplicates.push(jsdocComments[i]);
690
+ }
691
+ }
692
+ // Generate new JSDoc using AnnotationGenerator
693
+ const newJSDoc = generateNodeTypeJSDoc(nodeType);
694
+ // Remove stale duplicates first (process from end to start to preserve positions)
695
+ if (staleDuplicates.length > 0) {
696
+ for (let i = staleDuplicates.length - 1; i >= 0; i--) {
697
+ const dupe = staleDuplicates[i];
698
+ // Remove the duplicate and any trailing whitespace/newline
699
+ let removeEnd = dupe.end;
700
+ while (removeEnd < result.length &&
701
+ (result[removeEnd] === '\n' || result[removeEnd] === '\r')) {
702
+ removeEnd++;
703
+ }
704
+ result = result.slice(0, dupe.pos) + result.slice(removeEnd);
705
+ hasChanges = true;
706
+ }
707
+ // Re-parse to get updated positions after removal
708
+ const updatedSourceFile = ts.createSourceFile('temp.ts', result, ts.ScriptTarget.Latest, true);
709
+ let updatedFunctionNode;
710
+ ts.forEachChild(updatedSourceFile, (node) => {
711
+ if (ts.isFunctionDeclaration(node) && node.name?.text === nodeType.functionName) {
712
+ updatedFunctionNode = node;
713
+ }
714
+ });
715
+ if (!updatedFunctionNode) {
716
+ return { code: result, changed: hasChanges };
717
+ }
718
+ // Re-find the JSDoc comment with updated positions
719
+ const updatedStart = updatedFunctionNode.getFullStart();
720
+ const updatedComments = ts.getLeadingCommentRanges(result, updatedStart);
721
+ if (!updatedComments) {
722
+ return { code: result, changed: hasChanges };
723
+ }
724
+ let updatedJsdoc;
725
+ for (const c of updatedComments) {
726
+ if (c.kind === ts.SyntaxKind.MultiLineCommentTrivia &&
727
+ result.slice(c.pos, c.pos + 3) === '/**') {
728
+ updatedJsdoc = c;
729
+ }
730
+ }
731
+ if (!updatedJsdoc) {
732
+ return { code: result, changed: hasChanges };
733
+ }
734
+ // Continue with the updated positions
735
+ return replaceJSDocContent(result, updatedJsdoc, updatedFunctionNode, nodeType, newJSDoc, hasChanges);
736
+ }
737
+ return replaceJSDocContent(result, jsdocComment, functionNode, nodeType, newJSDoc, hasChanges);
738
+ }
739
+ function replaceJSDocContent(source, jsdocComment, functionNode, nodeType, newJSDoc, hasChanges) {
740
+ const originalJSDoc = source.slice(jsdocComment.pos, jsdocComment.end);
741
+ // Check if JSDoc changed
742
+ const jsdocChanged = originalJSDoc.trim() !== newJSDoc.trim();
743
+ // Check if function body needs updating (when functionText is provided)
744
+ const nodeTypeWithCode = nodeType;
745
+ const newFunctionText = nodeTypeWithCode.code || nodeType.functionText;
746
+ let functionBodyChanged = false;
747
+ let newFunctionDeclaration = '';
748
+ if (newFunctionText) {
749
+ // Extract function declaration from functionText (strip ALL leading JSDoc blocks).
750
+ // The parser's functionText may include multiple JSDoc blocks (e.g. file header + nodeType JSDoc).
751
+ // We must strip ALL of them, not just the first one.
752
+ let strippedFunctionText = newFunctionText.trim();
753
+ while (strippedFunctionText.startsWith('/**')) {
754
+ strippedFunctionText = strippedFunctionText.replace(/^\/\*\*[\s\S]*?\*\/\s*/, '').trim();
755
+ }
756
+ newFunctionDeclaration = strippedFunctionText;
757
+ // Get current function declaration (without JSDoc)
758
+ const currentFunctionDeclaration = source
759
+ .slice(functionNode.getStart(), functionNode.getEnd())
760
+ .trim();
761
+ // Normalize for comparison (strip whitespace differences)
762
+ const normalizedNew = newFunctionDeclaration.replace(/\s+/g, ' ');
763
+ const normalizedCurrent = currentFunctionDeclaration.replace(/\s+/g, ' ');
764
+ functionBodyChanged = normalizedNew !== normalizedCurrent;
765
+ }
766
+ // If nothing changed, return early
767
+ if (!jsdocChanged && !functionBodyChanged) {
768
+ return { code: source, changed: hasChanges };
769
+ }
770
+ // Replace the JSDoc and optionally the function body
771
+ if (functionBodyChanged && newFunctionDeclaration) {
772
+ // Replace entire function (JSDoc + body)
773
+ const before = source.slice(0, jsdocComment.pos);
774
+ const after = source.slice(functionNode.getEnd());
775
+ return {
776
+ code: before + newJSDoc + '\n' + newFunctionDeclaration + after,
777
+ changed: true,
778
+ };
779
+ }
780
+ else if (jsdocChanged) {
781
+ // Only replace JSDoc
782
+ const before = source.slice(0, jsdocComment.pos);
783
+ const after = source.slice(jsdocComment.end);
784
+ return {
785
+ code: before + newJSDoc + after,
786
+ changed: true,
787
+ };
788
+ }
789
+ return { code: source, changed: hasChanges };
790
+ }
791
+ /**
792
+ * Generate JSDoc comment for node type function
793
+ */
794
+ function generateNodeTypeJSDoc(nodeType) {
795
+ const lines = [];
796
+ lines.push('/**');
797
+ // Add description
798
+ if (nodeType.description) {
799
+ lines.push(` * ${nodeType.description}`);
800
+ lines.push(` *`);
801
+ }
802
+ // @flowWeaver marker
803
+ lines.push(' * @flowWeaver nodeType');
804
+ // Add @expression tag for expression nodes
805
+ if (nodeType.expression) {
806
+ lines.push(' * @expression');
807
+ }
808
+ // Add @name tag when name differs from functionName (preserves stable identity across renames)
809
+ if (nodeType.name && nodeType.name !== nodeType.functionName) {
810
+ lines.push(` * @name ${nodeType.name}`);
811
+ }
812
+ // Add label if present
813
+ if (nodeType.label) {
814
+ lines.push(` * @label ${nodeType.label}`);
815
+ }
816
+ // Add scope if present
817
+ if (nodeType.scope) {
818
+ lines.push(` * @scope ${nodeType.scope}`);
819
+ }
820
+ // Add pullExecution if present
821
+ if (nodeType.defaultConfig?.pullExecution) {
822
+ lines.push(` * @pullExecution ${nodeType.defaultConfig.pullExecution.triggerPort}`);
823
+ }
824
+ // Add visual annotations
825
+ if (nodeType.visuals) {
826
+ if (nodeType.visuals.color) {
827
+ lines.push(` * @color ${nodeType.visuals.color}`);
828
+ }
829
+ if (nodeType.visuals.icon) {
830
+ lines.push(` * @icon ${nodeType.visuals.icon}`);
831
+ }
832
+ if (nodeType.visuals.tags && nodeType.visuals.tags.length > 0) {
833
+ for (const tag of nodeType.visuals.tags) {
834
+ if (tag.tooltip) {
835
+ lines.push(` * @tag ${tag.label} "${tag.tooltip}"`);
836
+ }
837
+ else {
838
+ lines.push(` * @tag ${tag.label}`);
839
+ }
840
+ }
841
+ }
842
+ }
843
+ // Handle both formats: inputs/outputs dictionaries OR ports array
844
+ let inputEntries = [];
845
+ let outputEntries = [];
846
+ if (nodeType.inputs && Object.keys(nodeType.inputs).length > 0) {
847
+ // Use native inputs/outputs format
848
+ // For expression nodes, skip auto-generated control flow ports (execute, onSuccess, onFailure)
849
+ const filterControlFlow = nodeType.expression
850
+ ? ([name]) => !isExecutePort(name) && !isSuccessPort(name) && !isFailurePort(name)
851
+ : () => true;
852
+ inputEntries = assignPortOrders(Object.entries(nodeType.inputs).filter(filterControlFlow), 'input');
853
+ outputEntries = assignPortOrders(Object.entries(nodeType.outputs || {}).filter(filterControlFlow), 'output');
854
+ }
855
+ else if (nodeType.ports && nodeType.ports.length > 0) {
856
+ // Convert from ports array format (UI format)
857
+ const inputs = [];
858
+ const outputs = [];
859
+ for (const port of nodeType.ports) {
860
+ // Skip mandatory control flow ports - they're implicit
861
+ if (isExecutePort(port.name) || isSuccessPort(port.name) || isFailurePort(port.name)) {
862
+ continue;
863
+ }
864
+ const portDef = {
865
+ dataType: (port.type || port.dataType || 'ANY'),
866
+ label: port.defaultLabel || port.name,
867
+ scope: port.scope,
868
+ metadata: { order: port.defaultOrder },
869
+ };
870
+ if (port.direction === 'INPUT') {
871
+ inputs.push([port.name, portDef]);
872
+ }
873
+ else {
874
+ outputs.push([port.name, portDef]);
875
+ }
876
+ }
877
+ inputEntries = assignPortOrders(inputs, 'input');
878
+ outputEntries = assignPortOrders(outputs, 'output');
879
+ }
880
+ // Add input ports
881
+ for (const [name, port] of inputEntries) {
882
+ const portTag = generateJSDocPortTag(name, port, 'input');
883
+ lines.push(` * ${portTag}`);
884
+ }
885
+ // Add output ports
886
+ for (const [name, port] of outputEntries) {
887
+ const portTag = generateJSDocPortTag(name, port, 'output');
888
+ lines.push(` * ${portTag}`);
889
+ }
890
+ lines.push(' */');
891
+ return lines.join('\n');
892
+ }
893
+ // Note: generateJSDocPortTag and assignPortOrders are now imported from annotation-generator
894
+ // to maintain DRY principle and ensure consistent behavior across all code generation
895
+ /**
896
+ * Replace the workflow function's JSDoc comment with updated annotations
897
+ */
898
+ function replaceWorkflowJSDoc(source, ast, options = {}) {
899
+ const sourceFile = ts.createSourceFile('temp.ts', source, ts.ScriptTarget.Latest, true);
900
+ let functionNode;
901
+ // Find the workflow function (exported or not)
902
+ ts.forEachChild(sourceFile, (node) => {
903
+ if (ts.isFunctionDeclaration(node) && node.name?.text === ast.functionName) {
904
+ functionNode = node;
905
+ }
906
+ });
907
+ if (!functionNode) {
908
+ return { code: source, changed: false };
909
+ }
910
+ // Find the JSDoc comment before the function
911
+ const functionStart = functionNode.getFullStart();
912
+ const leadingComments = ts.getLeadingCommentRanges(source, functionStart);
913
+ if (!leadingComments || leadingComments.length === 0) {
914
+ return { code: source, changed: false };
915
+ }
916
+ // Find the JSDoc comment (starts with /**)
917
+ const jsdocComment = leadingComments.find((c) => c.kind === ts.SyntaxKind.MultiLineCommentTrivia && source.slice(c.pos, c.pos + 3) === '/**');
918
+ if (!jsdocComment) {
919
+ return { code: source, changed: false };
920
+ }
921
+ // Generate new JSDoc
922
+ const newJSDoc = generateWorkflowJSDoc(ast, { skipParamReturns: options.skipParamReturns });
923
+ // Get the original JSDoc
924
+ const originalJSDoc = source.slice(jsdocComment.pos, jsdocComment.end);
925
+ // Check if changed
926
+ if (originalJSDoc.trim() === newJSDoc.trim()) {
927
+ return { code: source, changed: false };
928
+ }
929
+ // Replace the JSDoc
930
+ const before = source.slice(0, jsdocComment.pos);
931
+ const after = source.slice(jsdocComment.end);
932
+ return {
933
+ code: before + newJSDoc + after,
934
+ changed: true,
935
+ };
936
+ }
937
+ /**
938
+ * Check if a connection is covered by a macro (and should not be written as @connect).
939
+ * Handles @map and @path macros.
940
+ */
941
+ function isConnectionCoveredByMacro(conn, macros) {
942
+ for (const macro of macros) {
943
+ if (macro.type === 'map') {
944
+ const [sourceNode, sourcePort] = macro.sourcePort.split('.');
945
+ // Scoped connections between the map instance and its child
946
+ if ((conn.from.node === macro.instanceId || conn.from.node === macro.childId) &&
947
+ (conn.to.node === macro.instanceId || conn.to.node === macro.childId) &&
948
+ (conn.from.scope === 'iterate' || conn.to.scope === 'iterate')) {
949
+ return true;
950
+ }
951
+ // Upstream connection: source.port -> mapInstance.items
952
+ if (conn.from.node === sourceNode &&
953
+ conn.from.port === sourcePort &&
954
+ !conn.from.scope &&
955
+ conn.to.node === macro.instanceId &&
956
+ conn.to.port === 'items' &&
957
+ !conn.to.scope) {
958
+ return true;
959
+ }
960
+ }
961
+ else if (macro.type === 'path') {
962
+ if (conn.from.scope || conn.to.scope)
963
+ continue;
964
+ const steps = macro.steps;
965
+ const fromIdx = steps.findIndex(s => s.node === conn.from.node);
966
+ const toIdx = steps.findIndex(s => s.node === conn.to.node);
967
+ if (fromIdx === -1 || toIdx === -1 || fromIdx >= toIdx)
968
+ continue;
969
+ // Control flow: check consecutive pairs
970
+ if (toIdx === fromIdx + 1) {
971
+ const route = steps[fromIdx].route || 'ok';
972
+ if (conn.from.node === 'Start' && conn.from.port === 'execute' && conn.to.port === 'execute')
973
+ return true;
974
+ if (conn.to.node === 'Exit') {
975
+ if (route === 'fail' && conn.from.port === 'onFailure' && conn.to.port === 'onFailure')
976
+ return true;
977
+ if (route === 'ok' && conn.from.port === 'onSuccess' && conn.to.port === 'onSuccess')
978
+ return true;
979
+ }
980
+ if (route === 'fail' && conn.from.port === 'onFailure' && conn.to.port === 'execute')
981
+ return true;
982
+ if (route === 'ok' && conn.from.port === 'onSuccess' && conn.to.port === 'execute')
983
+ return true;
984
+ }
985
+ // Data: same-name non-control-flow, from before to, to is not Exit
986
+ if (conn.to.node !== 'Exit' &&
987
+ !isControlFlowPort(conn.from.port) &&
988
+ !isControlFlowPort(conn.to.port) &&
989
+ conn.from.port === conn.to.port) {
990
+ return true;
991
+ }
992
+ }
993
+ }
994
+ return false;
995
+ }
996
+ /**
997
+ * Generate JSDoc comment for workflow function
998
+ */
999
+ function generateWorkflowJSDoc(ast, options = {}) {
1000
+ const lines = [];
1001
+ // Build macro coverage sets for filtering (@map-specific)
1002
+ const macroInstanceIds = new Set();
1003
+ const macroChildIds = new Set();
1004
+ const macroScopeNames = new Set();
1005
+ if (ast.macros && ast.macros.length > 0) {
1006
+ for (const macro of ast.macros) {
1007
+ if (macro.type === 'map') {
1008
+ macroInstanceIds.add(macro.instanceId);
1009
+ macroChildIds.add(macro.childId);
1010
+ macroScopeNames.add(`${macro.instanceId}.iterate`);
1011
+ }
1012
+ }
1013
+ }
1014
+ lines.push('/**');
1015
+ // Add description
1016
+ if (ast.description) {
1017
+ lines.push(` * ${ast.description}`);
1018
+ lines.push(` *`);
1019
+ }
1020
+ // @flowWeaver marker
1021
+ lines.push(' * @flowWeaver workflow');
1022
+ // Add workflow options
1023
+ if (ast.options?.strictTypes) {
1024
+ lines.push(' * @strictTypes');
1025
+ }
1026
+ if (ast.options?.autoConnect) {
1027
+ lines.push(' * @autoConnect');
1028
+ }
1029
+ // @trigger round-trip
1030
+ if (ast.options?.trigger) {
1031
+ const t = ast.options.trigger;
1032
+ const parts = [];
1033
+ if (t.event)
1034
+ parts.push(`event="${t.event}"`);
1035
+ if (t.cron)
1036
+ parts.push(`cron="${t.cron}"`);
1037
+ if (parts.length > 0)
1038
+ lines.push(` * @trigger ${parts.join(' ')}`);
1039
+ }
1040
+ // @cancelOn round-trip
1041
+ if (ast.options?.cancelOn) {
1042
+ const c = ast.options.cancelOn;
1043
+ let line = ` * @cancelOn event="${c.event}"`;
1044
+ if (c.match)
1045
+ line += ` match="${c.match}"`;
1046
+ if (c.timeout)
1047
+ line += ` timeout="${c.timeout}"`;
1048
+ lines.push(line);
1049
+ }
1050
+ // @retries round-trip
1051
+ if (ast.options?.retries !== undefined) {
1052
+ lines.push(` * @retries ${ast.options.retries}`);
1053
+ }
1054
+ // @timeout round-trip
1055
+ if (ast.options?.timeout) {
1056
+ lines.push(` * @timeout "${ast.options.timeout}"`);
1057
+ }
1058
+ // @throttle round-trip
1059
+ if (ast.options?.throttle) {
1060
+ const t = ast.options.throttle;
1061
+ let line = ` * @throttle limit=${t.limit}`;
1062
+ if (t.period)
1063
+ line += ` period="${t.period}"`;
1064
+ lines.push(line);
1065
+ }
1066
+ // Add name if different from function name
1067
+ if (ast.name && ast.name !== ast.functionName) {
1068
+ lines.push(` * @name ${ast.name}`);
1069
+ }
1070
+ // Add npm package imports (external node types with importSource)
1071
+ // Format: @fwImport nodeName functionName from "packageName"
1072
+ // This persists npm node types so they survive file re-parsing
1073
+ const npmNodeTypes = ast.nodeTypes.filter((nt) => nt.importSource);
1074
+ const seenImportNames = new Set();
1075
+ for (const npmType of npmNodeTypes) {
1076
+ if (seenImportNames.has(npmType.name))
1077
+ continue;
1078
+ seenImportNames.add(npmType.name);
1079
+ // Derive the actual function name:
1080
+ // - If functionName differs from name, use functionName (explicitly set)
1081
+ // - Otherwise, extract from npm path: "npm/package/funcName" -> "funcName"
1082
+ let actualFunctionName = npmType.functionName;
1083
+ if (npmType.functionName === npmType.name && npmType.name.startsWith('npm/')) {
1084
+ // Extract function name from npm path: npm/package/funcName -> funcName
1085
+ const parts = npmType.name.split('/');
1086
+ actualFunctionName = parts[parts.length - 1];
1087
+ }
1088
+ lines.push(` * @fwImport ${npmType.name} ${actualFunctionName} from "${npmType.importSource}"`);
1089
+ }
1090
+ // Add node instances — skip synthetic MAP_ITERATOR instances, strip parent from macro children
1091
+ for (const instance of ast.instances) {
1092
+ if (macroInstanceIds.has(instance.id))
1093
+ continue;
1094
+ if (macroChildIds.has(instance.id) && instance.parent) {
1095
+ // Write child @node without parent scope — @map handles it
1096
+ const stripped = { ...instance, parent: undefined };
1097
+ lines.push(generateNodeInstanceTag(stripped));
1098
+ }
1099
+ else {
1100
+ lines.push(generateNodeInstanceTag(instance));
1101
+ }
1102
+ }
1103
+ // Filter stale macros (e.g. paths whose connections were deleted)
1104
+ const existingMacros = filterStaleMacros(ast.macros || [], ast.connections, ast.instances);
1105
+ // Auto-detect @path sugar patterns from connections
1106
+ const detected = detectSugarPatterns(ast.connections, ast.instances, existingMacros, ast.nodeTypes, ast.startPorts, ast.exitPorts);
1107
+ // Merge detected macros with existing ones
1108
+ const allMacros = [
1109
+ ...existingMacros,
1110
+ ...detected.paths,
1111
+ ];
1112
+ // Add @map and @path macros
1113
+ if (allMacros.length > 0) {
1114
+ for (const macro of allMacros) {
1115
+ if (macro.type === 'map') {
1116
+ let mapLine = ` * @map ${macro.instanceId} ${macro.childId}`;
1117
+ if (macro.inputPort || macro.outputPort) {
1118
+ mapLine += `(${macro.inputPort} -> ${macro.outputPort})`;
1119
+ }
1120
+ mapLine += ` over ${macro.sourcePort}`;
1121
+ lines.push(mapLine);
1122
+ }
1123
+ else if (macro.type === 'path') {
1124
+ const stepsStr = macro.steps.map(s => s.route ? `${s.node}:${s.route}` : s.node).join(' -> ');
1125
+ lines.push(` * @path ${stepsStr}`);
1126
+ }
1127
+ }
1128
+ }
1129
+ // Auto-position: compute default positions for nodes without explicit positions.
1130
+ // Uses a left-to-right layout with topological ordering when connections are available.
1131
+ const autoPositions = computeAutoPositions(ast);
1132
+ // Add positions - Start node
1133
+ const startX = ast.ui?.startNode?.x ?? autoPositions.get('Start')?.x;
1134
+ const startY = ast.ui?.startNode?.y ?? autoPositions.get('Start')?.y;
1135
+ if (startX !== undefined && startY !== undefined) {
1136
+ lines.push(` * @position Start ${Math.round(startX)} ${Math.round(startY)}`);
1137
+ }
1138
+ // Add positions - instances (use explicit position if available, otherwise auto-computed)
1139
+ for (const instance of ast.instances) {
1140
+ const explicitX = instance.config?.x;
1141
+ const explicitY = instance.config?.y;
1142
+ const autoPos = autoPositions.get(instance.id);
1143
+ const x = explicitX ?? autoPos?.x;
1144
+ const y = explicitY ?? autoPos?.y;
1145
+ if (x !== undefined && y !== undefined) {
1146
+ lines.push(` * @position ${instance.id} ${Math.round(x)} ${Math.round(y)}`);
1147
+ }
1148
+ }
1149
+ // Add positions - Exit node
1150
+ const exitX = ast.ui?.exitNode?.x ?? autoPositions.get('Exit')?.x;
1151
+ const exitY = ast.ui?.exitNode?.y ?? autoPositions.get('Exit')?.y;
1152
+ if (exitX !== undefined && exitY !== undefined) {
1153
+ lines.push(` * @position Exit ${Math.round(exitX)} ${Math.round(exitY)}`);
1154
+ }
1155
+ // Add connections (with scope suffix when present)
1156
+ // Skip connections covered by @map macros and autoConnect-generated connections
1157
+ if (!ast.options?.autoConnect) {
1158
+ for (const conn of ast.connections) {
1159
+ if (allMacros.length > 0 && isConnectionCoveredByMacro(conn, allMacros))
1160
+ continue;
1161
+ const fromScope = conn.from.scope ? `:${conn.from.scope}` : '';
1162
+ const toScope = conn.to.scope ? `:${conn.to.scope}` : '';
1163
+ lines.push(` * @connect ${conn.from.node}.${conn.from.port}${fromScope} -> ${conn.to.node}.${conn.to.port}${toScope}`);
1164
+ }
1165
+ }
1166
+ // Add @param annotations for start ports (workflow inputs)
1167
+ if (!options.skipParamReturns && ast.startPorts && Object.keys(ast.startPorts).length > 0) {
1168
+ const startPortEntries = assignPortOrders(Object.entries(ast.startPorts), 'input');
1169
+ startPortEntries.forEach(([name, port], index) => {
1170
+ const paramTag = generateJSDocPortTag(name, port, 'input', index);
1171
+ // Replace @input with @param for workflow-level JSDoc
1172
+ lines.push(` * ${paramTag.replace('@input', '@param')}`);
1173
+ });
1174
+ }
1175
+ // Add @returns annotations for exit ports (workflow outputs)
1176
+ if (!options.skipParamReturns && ast.exitPorts && Object.keys(ast.exitPorts).length > 0) {
1177
+ const exitPortEntries = assignPortOrders(Object.entries(ast.exitPorts), 'output');
1178
+ exitPortEntries.forEach(([name, port], index) => {
1179
+ const returnTag = generateJSDocPortTag(name, port, 'output', index);
1180
+ // Replace @output with @returns for workflow-level JSDoc
1181
+ lines.push(` * ${returnTag.replace('@output', '@returns')}`);
1182
+ });
1183
+ }
1184
+ // Add scopes — skip scopes covered by @map macros
1185
+ if (ast.scopes) {
1186
+ for (const [scopeName, children] of Object.entries(ast.scopes)) {
1187
+ if (macroScopeNames.has(scopeName))
1188
+ continue;
1189
+ lines.push(` * @scope ${scopeName} [${children.join(', ')}]`);
1190
+ }
1191
+ }
1192
+ lines.push(' */');
1193
+ return lines.join('\n');
1194
+ }
1195
+ /**
1196
+ * Compute auto-layout positions for nodes that don't have explicit positions.
1197
+ * Uses topological order (from connections) for left-to-right layout.
1198
+ * Only computes positions for nodes that are missing them.
1199
+ *
1200
+ * Layout strategy:
1201
+ * - Start node at x=0, y=0
1202
+ * - Each subsequent node gets x += 270 (standard node width + gap)
1203
+ * - Exit node placed after the last instance
1204
+ * - Uses topological order when connections are available
1205
+ * - Falls back to declaration order otherwise
1206
+ *
1207
+ * @returns Map of nodeId -> {x, y} for nodes that need auto-positioned
1208
+ */
1209
+ function computeAutoPositions(ast) {
1210
+ const positions = new Map();
1211
+ const SPACING_X = 270; // Standard horizontal spacing between nodes
1212
+ // Determine which nodes need positions
1213
+ const needsPosition = (nodeId) => {
1214
+ if (nodeId === 'Start') {
1215
+ return ast.ui?.startNode?.x === undefined || ast.ui?.startNode?.y === undefined;
1216
+ }
1217
+ if (nodeId === 'Exit') {
1218
+ return ast.ui?.exitNode?.x === undefined || ast.ui?.exitNode?.y === undefined;
1219
+ }
1220
+ const instance = ast.instances.find((inst) => inst.id === nodeId);
1221
+ return !instance || instance.config?.x === undefined || instance.config?.y === undefined;
1222
+ };
1223
+ // Check if any nodes need auto-positioning
1224
+ const startNeedsPosition = needsPosition('Start');
1225
+ const exitNeedsPosition = needsPosition('Exit');
1226
+ const instancesNeedingPosition = ast.instances.filter((inst) => needsPosition(inst.id));
1227
+ // If nothing needs positioning, return empty map
1228
+ if (!startNeedsPosition && !exitNeedsPosition && instancesNeedingPosition.length === 0) {
1229
+ return positions;
1230
+ }
1231
+ // Compute topological order from connections, or fall back to declaration order
1232
+ const orderedIds = computeTopologicalOrder(ast);
1233
+ // Assign positions left-to-right
1234
+ let currentX = 0;
1235
+ const y = 0;
1236
+ // Start node
1237
+ if (startNeedsPosition) {
1238
+ positions.set('Start', { x: currentX, y });
1239
+ }
1240
+ currentX += SPACING_X;
1241
+ // Instance nodes in topological/declaration order
1242
+ for (const instanceId of orderedIds) {
1243
+ if (needsPosition(instanceId)) {
1244
+ positions.set(instanceId, { x: currentX, y });
1245
+ }
1246
+ currentX += SPACING_X;
1247
+ }
1248
+ // Exit node
1249
+ if (exitNeedsPosition) {
1250
+ positions.set('Exit', { x: currentX, y });
1251
+ }
1252
+ return positions;
1253
+ }
1254
+ /**
1255
+ * Compute topological order for workflow instances using connections.
1256
+ * Falls back to declaration order when connections don't provide a clear ordering.
1257
+ */
1258
+ function computeTopologicalOrder(ast) {
1259
+ const instanceIds = ast.instances.map((inst) => inst.id);
1260
+ // If no connections, use declaration order
1261
+ if (!ast.connections || ast.connections.length === 0) {
1262
+ return instanceIds;
1263
+ }
1264
+ // Build adjacency list from execution flow connections only
1265
+ // (control flow connections determine order, data connections don't)
1266
+ const graph = new Map();
1267
+ const inDegree = new Map();
1268
+ for (const id of instanceIds) {
1269
+ graph.set(id, new Set());
1270
+ inDegree.set(id, 0);
1271
+ }
1272
+ for (const conn of ast.connections) {
1273
+ const fromNode = conn.from.node;
1274
+ const toNode = conn.to.node;
1275
+ // Only consider connections between instances (skip Start/Exit)
1276
+ if (!graph.has(fromNode) || !graph.has(toNode))
1277
+ continue;
1278
+ // Only use execution flow (onSuccess/onFailure -> execute) for ordering
1279
+ const isExecutionFlow = (conn.from.port === 'onSuccess' || conn.from.port === 'onFailure') &&
1280
+ conn.to.port === 'execute';
1281
+ if (isExecutionFlow && !graph.get(fromNode).has(toNode)) {
1282
+ graph.get(fromNode).add(toNode);
1283
+ inDegree.set(toNode, (inDegree.get(toNode) || 0) + 1);
1284
+ }
1285
+ }
1286
+ // Kahn's algorithm for topological sort
1287
+ const queue = [];
1288
+ for (const id of instanceIds) {
1289
+ if ((inDegree.get(id) || 0) === 0) {
1290
+ queue.push(id);
1291
+ }
1292
+ }
1293
+ const sorted = [];
1294
+ while (queue.length > 0) {
1295
+ const node = queue.shift();
1296
+ sorted.push(node);
1297
+ for (const neighbor of graph.get(node) || []) {
1298
+ const newDegree = (inDegree.get(neighbor) || 1) - 1;
1299
+ inDegree.set(neighbor, newDegree);
1300
+ if (newDegree === 0) {
1301
+ queue.push(neighbor);
1302
+ }
1303
+ }
1304
+ }
1305
+ // If topological sort didn't include all nodes (cycles or disconnected),
1306
+ // append remaining nodes in declaration order
1307
+ if (sorted.length < instanceIds.length) {
1308
+ const sortedSet = new Set(sorted);
1309
+ for (const id of instanceIds) {
1310
+ if (!sortedSet.has(id)) {
1311
+ sorted.push(id);
1312
+ }
1313
+ }
1314
+ }
1315
+ return sorted;
1316
+ }
1317
+ /**
1318
+ * Check if source code has in-place generation markers
1319
+ */
1320
+ export function hasInPlaceMarkers(source) {
1321
+ return (source.includes(MARKERS.RUNTIME_START) &&
1322
+ source.includes(MARKERS.RUNTIME_END) &&
1323
+ source.includes(MARKERS.BODY_START) &&
1324
+ source.includes(MARKERS.BODY_END));
1325
+ }
1326
+ /**
1327
+ * Remove all generated sections from source code
1328
+ */
1329
+ export function stripGeneratedSections(source) {
1330
+ let result = source;
1331
+ // Remove runtime section
1332
+ const runtimeStartIdx = result.indexOf(MARKERS.RUNTIME_START);
1333
+ const runtimeEndIdx = result.indexOf(MARKERS.RUNTIME_END);
1334
+ if (runtimeStartIdx !== -1 && runtimeEndIdx !== -1) {
1335
+ const lineStart = result.lastIndexOf('\n', runtimeStartIdx);
1336
+ const lineEnd = result.indexOf('\n', runtimeEndIdx + MARKERS.RUNTIME_END.length);
1337
+ result =
1338
+ result.slice(0, lineStart === -1 ? 0 : lineStart) +
1339
+ result.slice(lineEnd === -1 ? result.length : lineEnd);
1340
+ }
1341
+ // Remove ALL body sections (multi-workflow files have multiple)
1342
+ while (true) {
1343
+ const bodyStartIdx = result.indexOf(MARKERS.BODY_START);
1344
+ const bodyEndIdx = result.indexOf(MARKERS.BODY_END);
1345
+ if (bodyStartIdx === -1 || bodyEndIdx === -1 || bodyEndIdx < bodyStartIdx)
1346
+ break;
1347
+ const before = result.slice(0, bodyStartIdx);
1348
+ const after = result.slice(bodyEndIdx + MARKERS.BODY_END.length);
1349
+ result = before + `throw new Error('Not implemented');` + after;
1350
+ }
1351
+ return result;
1352
+ }
1353
+ //# sourceMappingURL=generate-in-place.js.map