@mmapp/react-compiler 0.1.0-alpha.1 → 0.1.0-alpha.4

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 (346) hide show
  1. package/ATOM-PIPELINE.md +144 -0
  2. package/README.md +88 -40
  3. package/dist/auth-3UK75242.mjs +17 -0
  4. package/dist/babel/index.d.mts +2 -2
  5. package/dist/babel/index.d.ts +2 -2
  6. package/dist/babel/index.js +2816 -279
  7. package/dist/babel/index.mjs +2 -2
  8. package/dist/chunk-3USIFFE4.mjs +2190 -0
  9. package/dist/chunk-45YMGEVT.mjs +186 -0
  10. package/dist/chunk-4FN2AISW.mjs +148 -0
  11. package/dist/chunk-4OPI5L7G.mjs +2593 -0
  12. package/dist/chunk-4RYTKOOJ.mjs +186 -0
  13. package/dist/chunk-52XHYD2V.mjs +214 -0
  14. package/dist/chunk-5FTDWKHH.mjs +244 -0
  15. package/dist/chunk-5GUFFFGL.mjs +148 -0
  16. package/dist/chunk-5RKTOVR5.mjs +244 -0
  17. package/dist/chunk-5YDMOO4X.mjs +214 -0
  18. package/dist/chunk-64ZWEMLJ.mjs +148 -0
  19. package/dist/chunk-6XP4KSWQ.mjs +2190 -0
  20. package/dist/chunk-72QWL54I.mjs +175 -0
  21. package/dist/chunk-7B4TRI7C.mjs +4835 -0
  22. package/dist/chunk-7JRAEFRB.mjs +7510 -0
  23. package/dist/chunk-7T6Q5KAA.mjs +7506 -0
  24. package/dist/chunk-7ZKGHTNB.mjs +4952 -0
  25. package/dist/chunk-ABYPKRSB.mjs +215 -0
  26. package/dist/chunk-BZEXUPDH.mjs +175 -0
  27. package/dist/chunk-CIESM3BP.mjs +33 -0
  28. package/dist/chunk-DE3ZGQAC.mjs +148 -0
  29. package/dist/chunk-DMCY3BBG.mjs +1933 -0
  30. package/dist/chunk-DPIK3PJS.mjs +244 -0
  31. package/dist/chunk-E5IVH4RE.mjs +186 -0
  32. package/dist/chunk-E6FZNUR5.mjs +4953 -0
  33. package/dist/chunk-EJRBDQDP.mjs +2607 -0
  34. package/dist/chunk-ELO4TXJL.mjs +186 -0
  35. package/dist/chunk-EO6SYNCG.mjs +175 -0
  36. package/dist/chunk-FKRO52XH.mjs +3446 -0
  37. package/dist/chunk-FL4YAKU6.mjs +4941 -0
  38. package/dist/chunk-FYT47UBU.mjs +5076 -0
  39. package/dist/chunk-GCLGPOJZ.mjs +148 -0
  40. package/dist/chunk-GXB4JOP7.mjs +5072 -0
  41. package/dist/chunk-HFXOUMTD.mjs +175 -0
  42. package/dist/chunk-HRYR54PT.mjs +175 -0
  43. package/dist/chunk-HWIZ47US.mjs +214 -0
  44. package/dist/chunk-IB7MNPQL.mjs +4953 -0
  45. package/dist/chunk-ICSIHQCG.mjs +148 -0
  46. package/dist/chunk-J3M4GUS7.mjs +161 -0
  47. package/dist/chunk-J7JUAHS4.mjs +186 -0
  48. package/dist/chunk-JLA5VNQ3.mjs +186 -0
  49. package/dist/chunk-JQLWFCTM.mjs +214 -0
  50. package/dist/chunk-JRGFBWTN.mjs +2918 -0
  51. package/dist/chunk-KFJJCQAL.mjs +148 -0
  52. package/dist/chunk-KJUIIEQE.mjs +186 -0
  53. package/dist/chunk-KNWTHRVQ.mjs +175 -0
  54. package/dist/chunk-KSG4XSZF.mjs +175 -0
  55. package/dist/chunk-LF5N6DOU.mjs +175 -0
  56. package/dist/chunk-LJQCM2IM.mjs +214 -0
  57. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  58. package/dist/chunk-NW6555WJ.mjs +186 -0
  59. package/dist/chunk-O4AUS7EU.mjs +148 -0
  60. package/dist/chunk-OMZE6VLQ.mjs +214 -0
  61. package/dist/chunk-OPJKP747.mjs +7506 -0
  62. package/dist/chunk-P4BR7WVO.mjs +2190 -0
  63. package/dist/chunk-QQHVYH2X.mjs +244 -0
  64. package/dist/chunk-R2DD5GTY.mjs +186 -0
  65. package/dist/chunk-S5QLWLLT.mjs +186 -0
  66. package/dist/chunk-SCWGT2FY.mjs +2190 -0
  67. package/dist/chunk-SMKJUSB3.mjs +2190 -0
  68. package/dist/chunk-THFYE5ZX.mjs +244 -0
  69. package/dist/chunk-UDDTWG5J.mjs +734 -0
  70. package/dist/chunk-VCAY2KGM.mjs +175 -0
  71. package/dist/chunk-VLTKQDJ3.mjs +244 -0
  72. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  73. package/dist/chunk-WECAV6QB.mjs +148 -0
  74. package/dist/chunk-WMKBXUCE.mjs +3228 -0
  75. package/dist/chunk-WVYY32LD.mjs +939 -0
  76. package/dist/chunk-XAJ5BKKL.mjs +4947 -0
  77. package/dist/chunk-XDVM4YHX.mjs +3450 -0
  78. package/dist/chunk-XG2X7AEA.mjs +175 -0
  79. package/dist/chunk-XG7Z23NQ.mjs +148 -0
  80. package/dist/chunk-XWZAOCQ7.mjs +2607 -0
  81. package/dist/chunk-Y6MA7ULW.mjs +148 -0
  82. package/dist/chunk-YMS7Q7LG.mjs +214 -0
  83. package/dist/chunk-Z2G5RZ4H.mjs +186 -0
  84. package/dist/chunk-ZA37XTGA.mjs +175 -0
  85. package/dist/chunk-ZE3KCHBM.mjs +2918 -0
  86. package/dist/cli/index.js +14720 -7199
  87. package/dist/cli/index.mjs +224 -183
  88. package/dist/codemod/cli.js +1 -1
  89. package/dist/codemod/cli.mjs +2 -2
  90. package/dist/codemod/index.d.mts +3 -3
  91. package/dist/codemod/index.d.ts +3 -3
  92. package/dist/codemod/index.js +1 -1
  93. package/dist/codemod/index.mjs +2 -2
  94. package/dist/config-PL24KEWL.mjs +219 -0
  95. package/dist/deploy-YAJGW6II.mjs +9 -0
  96. package/dist/dev-server-CrQ041KP.d.mts +79 -0
  97. package/dist/dev-server-CrQ041KP.d.ts +79 -0
  98. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  99. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  100. package/dist/dev-server.d.mts +2 -2
  101. package/dist/dev-server.d.ts +2 -2
  102. package/dist/dev-server.js +6424 -1597
  103. package/dist/dev-server.mjs +5 -5
  104. package/dist/envelope-ChEkuHij.d.mts +265 -0
  105. package/dist/envelope-ChEkuHij.d.ts +265 -0
  106. package/dist/envelope.d.mts +2 -2
  107. package/dist/envelope.d.ts +2 -2
  108. package/dist/envelope.js +2814 -277
  109. package/dist/envelope.mjs +3 -3
  110. package/dist/index-CEKyyazf.d.mts +104 -0
  111. package/dist/index-CEKyyazf.d.ts +104 -0
  112. package/dist/index.d.mts +168 -9
  113. package/dist/index.d.ts +168 -9
  114. package/dist/index.js +5606 -681
  115. package/dist/index.mjs +217 -9
  116. package/dist/init-7FJENUDK.mjs +407 -0
  117. package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
  118. package/dist/init-DQDX3QK6.mjs +369 -0
  119. package/dist/init-EHO4VQ22.mjs +369 -0
  120. package/dist/init-UC3FWPIW.mjs +367 -0
  121. package/dist/init-UNSMVKIK.mjs +366 -0
  122. package/dist/init-UNV5XIDE.mjs +367 -0
  123. package/dist/project-compiler-2P4N4DR7.mjs +10 -0
  124. package/dist/project-compiler-D2LCC27O.mjs +10 -0
  125. package/dist/project-compiler-EJ3GANJE.mjs +10 -0
  126. package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
  127. package/dist/project-compiler-NNK32MPG.mjs +10 -0
  128. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  129. package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
  130. package/dist/project-compiler-VWNNCHGO.mjs +10 -0
  131. package/dist/project-compiler-XVAAU4C5.mjs +10 -0
  132. package/dist/project-compiler-YES5FGMD.mjs +10 -0
  133. package/dist/project-compiler-ZB4RUYVL.mjs +10 -0
  134. package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
  135. package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
  136. package/dist/project-decompiler-U55HQUHW.mjs +7 -0
  137. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  138. package/dist/project-decompiler-VLPR22QF.mjs +7 -0
  139. package/dist/pull-FUS5QYZS.mjs +109 -0
  140. package/dist/pull-KOL2QAYQ.mjs +109 -0
  141. package/dist/pull-LD5ENLGY.mjs +109 -0
  142. package/dist/pull-P44LDRWB.mjs +109 -0
  143. package/dist/seed-KOGEPGOJ.mjs +154 -0
  144. package/dist/server-VW6UPCHO.mjs +277 -0
  145. package/dist/testing/index.d.mts +8 -8
  146. package/dist/testing/index.d.ts +8 -8
  147. package/dist/testing/index.js +2824 -287
  148. package/dist/testing/index.mjs +2 -2
  149. package/dist/verify-BYHUKARQ.mjs +1833 -0
  150. package/dist/verify-OQDEQYMS.mjs +1833 -0
  151. package/dist/verify-SEIXUGN4.mjs +1833 -0
  152. package/dist/vite/index.d.mts +1 -1
  153. package/dist/vite/index.d.ts +1 -1
  154. package/dist/vite/index.js +2817 -280
  155. package/dist/vite/index.mjs +3 -3
  156. package/examples/authentication/main.workflow.tsx +1 -1
  157. package/examples/authentication/mm.config.ts +1 -1
  158. package/examples/authentication/pages/LoginPage.tsx +2 -2
  159. package/examples/authentication/pages/SignupPage.tsx +2 -2
  160. package/examples/counter.workflow.tsx +1 -1
  161. package/examples/dashboard.workflow.tsx +1 -1
  162. package/examples/invoice-approval/actions/invoice.server.ts +1 -1
  163. package/examples/invoice-approval/main.workflow.tsx +1 -1
  164. package/examples/invoice-approval/mm.config.ts +1 -1
  165. package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +1 -1
  166. package/examples/invoice-approval/pages/InvoiceFormPage.tsx +1 -1
  167. package/examples/invoice-approval/pages/InvoiceListPage.tsx +1 -1
  168. package/examples/todo-app.workflow.tsx +1 -1
  169. package/examples/uber-app/actions/matching.server.ts +1 -1
  170. package/examples/uber-app/actions/notifications.server.ts +1 -1
  171. package/examples/uber-app/actions/payments.server.ts +1 -1
  172. package/examples/uber-app/actions/pricing.server.ts +1 -1
  173. package/examples/uber-app/app/admin/analytics.tsx +2 -2
  174. package/examples/uber-app/app/admin/fleet.tsx +21 -21
  175. package/examples/uber-app/app/admin/surge-pricing.tsx +2 -2
  176. package/examples/uber-app/app/driver/dashboard.tsx +2 -2
  177. package/examples/uber-app/app/driver/earnings.tsx +2 -2
  178. package/examples/uber-app/app/driver/navigation.tsx +2 -2
  179. package/examples/uber-app/app/driver/ride-acceptance.tsx +2 -2
  180. package/examples/uber-app/app/rider/home.tsx +2 -2
  181. package/examples/uber-app/app/rider/payment-methods.tsx +2 -2
  182. package/examples/uber-app/app/rider/ride-history.tsx +2 -2
  183. package/examples/uber-app/app/rider/ride-tracking.tsx +2 -2
  184. package/examples/uber-app/components/DriverCard.tsx +1 -1
  185. package/examples/uber-app/components/MapView.tsx +3 -3
  186. package/examples/uber-app/components/RatingStars.tsx +2 -2
  187. package/examples/uber-app/components/RideCard.tsx +1 -1
  188. package/examples/uber-app/mm.config.ts +1 -1
  189. package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +2 -2
  190. package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +2 -2
  191. package/examples/uber-app/workflows/payment-processing.workflow.tsx +2 -2
  192. package/examples/uber-app/workflows/ride-request.workflow.tsx +2 -2
  193. package/package.json +10 -4
  194. package/compile-blueprint-chat.mjs +0 -99
  195. package/compile-blueprint-glass-console.mjs +0 -98
  196. package/compile-chat-defs.mjs +0 -92
  197. package/examples/uber-app/tests/payment.test.tsx +0 -129
  198. package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
  199. package/package.json.backup +0 -86
  200. package/scripts/decompile.ts +0 -226
  201. package/scripts/seed-auth.ts +0 -267
  202. package/scripts/seed-uber.ts +0 -248
  203. package/scripts/validate-uber.ts +0 -119
  204. package/seed-blueprint-chat.mjs +0 -444
  205. package/seed-blueprint-glass-console.mjs +0 -445
  206. package/seed-compiled.mjs +0 -318
  207. package/src/RoundTripValidator.ts +0 -400
  208. package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
  209. package/src/__tests__/auth-module-compilation.test.ts +0 -247
  210. package/src/__tests__/auth-template-compilation.test.ts +0 -589
  211. package/src/__tests__/change-extractor.test.ts +0 -142
  212. package/src/__tests__/cli-pull.test.ts +0 -73
  213. package/src/__tests__/cli-test.test.ts +0 -72
  214. package/src/__tests__/component-extractor.test.ts +0 -331
  215. package/src/__tests__/context-extractor.test.ts +0 -145
  216. package/src/__tests__/decompiler.test.ts +0 -718
  217. package/src/__tests__/define-blueprint.test.ts +0 -133
  218. package/src/__tests__/definition-validator.test.ts +0 -519
  219. package/src/__tests__/during-extractor.test.ts +0 -152
  220. package/src/__tests__/effect-extractor.test.ts +0 -107
  221. package/src/__tests__/event-emission.test.ts +0 -127
  222. package/src/__tests__/examples.test.ts +0 -236
  223. package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
  224. package/src/__tests__/golden-suite.test.ts +0 -403
  225. package/src/__tests__/grammar-island-extractor.test.ts +0 -289
  226. package/src/__tests__/instance-key.test.ts +0 -82
  227. package/src/__tests__/ir-migration.test.ts +0 -255
  228. package/src/__tests__/lock-file.test.ts +0 -117
  229. package/src/__tests__/model-extractor.test.ts +0 -195
  230. package/src/__tests__/model-field-acl.test.ts +0 -237
  231. package/src/__tests__/model-hooks.test.ts +0 -130
  232. package/src/__tests__/model-ref-resolution.test.ts +0 -268
  233. package/src/__tests__/model-roundtrip.test.ts +0 -502
  234. package/src/__tests__/model-runtime.test.ts +0 -112
  235. package/src/__tests__/model-transitions.test.ts +0 -183
  236. package/src/__tests__/nrt-action-trace.test.ts +0 -391
  237. package/src/__tests__/pipeline-hardening.test.ts +0 -413
  238. package/src/__tests__/project-compiler.test.ts +0 -546
  239. package/src/__tests__/project-decompiler.test.ts +0 -343
  240. package/src/__tests__/query-compilation.test.ts +0 -145
  241. package/src/__tests__/round-trip/PLAN.md +0 -158
  242. package/src/__tests__/round-trip/README.md +0 -52
  243. package/src/__tests__/round-trip/RESULTS.md +0 -86
  244. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
  245. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
  246. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
  247. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
  248. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
  249. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
  250. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
  251. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
  252. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
  253. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
  254. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
  255. package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
  256. package/src/__tests__/round-trip-ir.test.ts +0 -300
  257. package/src/__tests__/round-trip.test.ts +0 -1212
  258. package/src/__tests__/route-merging.test.ts +0 -372
  259. package/src/__tests__/router-composition.test.ts +0 -489
  260. package/src/__tests__/router-extractor.test.ts +0 -176
  261. package/src/__tests__/server-action-extractor.test.ts +0 -128
  262. package/src/__tests__/smart-type-inference.test.ts +0 -365
  263. package/src/__tests__/source-envelope.test.ts +0 -284
  264. package/src/__tests__/source-fidelity.test.ts +0 -516
  265. package/src/__tests__/state-extractor.test.ts +0 -115
  266. package/src/__tests__/strict-mode.test.ts +0 -227
  267. package/src/__tests__/transition-effect-extractor.test.ts +0 -119
  268. package/src/__tests__/transition-extractor.test.ts +0 -68
  269. package/src/__tests__/ts-to-expression.test.ts +0 -462
  270. package/src/__tests__/type-generator.test.ts +0 -201
  271. package/src/__tests__/uber-validation.test.ts +0 -502
  272. package/src/action-compiler.ts +0 -361
  273. package/src/babel/emitters/experience-transform.ts +0 -199
  274. package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
  275. package/src/babel/emitters/pure-form-emitter.ts +0 -1023
  276. package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
  277. package/src/babel/extractors/change-extractor.ts +0 -199
  278. package/src/babel/extractors/component-extractor.ts +0 -907
  279. package/src/babel/extractors/computed-extractor.ts +0 -262
  280. package/src/babel/extractors/context-extractor.ts +0 -277
  281. package/src/babel/extractors/during-extractor.ts +0 -295
  282. package/src/babel/extractors/effect-extractor.ts +0 -340
  283. package/src/babel/extractors/event-extractor.ts +0 -235
  284. package/src/babel/extractors/grammar-island-extractor.ts +0 -302
  285. package/src/babel/extractors/model-extractor.ts +0 -1018
  286. package/src/babel/extractors/router-extractor.ts +0 -303
  287. package/src/babel/extractors/server-action-extractor.ts +0 -173
  288. package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
  289. package/src/babel/extractors/server-state-extractor.ts +0 -88
  290. package/src/babel/extractors/state-extractor.ts +0 -214
  291. package/src/babel/extractors/transition-effect-extractor.ts +0 -176
  292. package/src/babel/extractors/transition-extractor.ts +0 -143
  293. package/src/babel/index.ts +0 -24
  294. package/src/babel/transpilers/ts-to-expression.ts +0 -674
  295. package/src/babel/visitor.ts +0 -807
  296. package/src/cli/auth.ts +0 -255
  297. package/src/cli/build.ts +0 -288
  298. package/src/cli/deploy.ts +0 -206
  299. package/src/cli/index.ts +0 -328
  300. package/src/cli/installer.ts +0 -261
  301. package/src/cli/lock-file.ts +0 -94
  302. package/src/cli/mmrc.ts +0 -22
  303. package/src/cli/pull.ts +0 -172
  304. package/src/cli/registry-client.ts +0 -175
  305. package/src/cli/test.ts +0 -397
  306. package/src/cli/type-generator.ts +0 -243
  307. package/src/codemod/__tests__/forward.test.ts +0 -239
  308. package/src/codemod/__tests__/reverse.test.ts +0 -145
  309. package/src/codemod/__tests__/round-trip.test.ts +0 -137
  310. package/src/codemod/annotation.ts +0 -97
  311. package/src/codemod/classify.ts +0 -197
  312. package/src/codemod/cli.ts +0 -207
  313. package/src/codemod/control-flow.ts +0 -409
  314. package/src/codemod/forward.ts +0 -244
  315. package/src/codemod/import-manager.ts +0 -171
  316. package/src/codemod/index.ts +0 -120
  317. package/src/codemod/reverse.ts +0 -197
  318. package/src/codemod/rules.ts +0 -174
  319. package/src/codemod/state-transform.ts +0 -126
  320. package/src/decompiler/ast-builder.ts +0 -538
  321. package/src/decompiler/config-generator.ts +0 -151
  322. package/src/decompiler/index.ts +0 -315
  323. package/src/decompiler/project-decompiler.ts +0 -1776
  324. package/src/decompiler/project.ts +0 -862
  325. package/src/decompiler/split-strategy.ts +0 -140
  326. package/src/decompiler/state-emitter.ts +0 -1053
  327. package/src/decompiler/sx-emitter.ts +0 -318
  328. package/src/decompiler/workspace-hydrator.ts +0 -189
  329. package/src/dev-server.ts +0 -238
  330. package/src/envelope/fs-tree.ts +0 -217
  331. package/src/envelope/source-envelope.ts +0 -264
  332. package/src/envelope.ts +0 -315
  333. package/src/incremental-compiler.ts +0 -401
  334. package/src/index.ts +0 -99
  335. package/src/model-compiler.ts +0 -277
  336. package/src/project-compiler.ts +0 -1629
  337. package/src/route-extractor.ts +0 -333
  338. package/src/testing/index.ts +0 -32
  339. package/src/testing/snapshot.ts +0 -252
  340. package/src/testing/test-utils.ts +0 -226
  341. package/src/types.ts +0 -68
  342. package/src/vite/index.ts +0 -288
  343. package/test-compile.mjs +0 -142
  344. package/tsconfig.json +0 -25
  345. package/tsup.config.ts +0 -23
  346. package/vitest.config.ts +0 -9
@@ -0,0 +1,3450 @@
1
+ // src/decompiler/index.ts
2
+ import * as t4 from "@babel/types";
3
+ import generate from "@babel/generator";
4
+
5
+ // src/decompiler/ast-builder.ts
6
+ import * as t2 from "@babel/types";
7
+ import { parseExpression } from "@babel/parser";
8
+
9
+ // src/decompiler/sx-emitter.ts
10
+ import * as t from "@babel/types";
11
+ function isTRBL(v) {
12
+ if (typeof v !== "object" || v === null) return false;
13
+ const obj = v;
14
+ return typeof obj.top === "number" && typeof obj.right === "number" && typeof obj.bottom === "number" && typeof obj.left === "number";
15
+ }
16
+ function isFourCorners(v) {
17
+ if (typeof v !== "object" || v === null) return false;
18
+ const obj = v;
19
+ return typeof obj.topLeft === "number" && typeof obj.topRight === "number" && typeof obj.bottomRight === "number" && typeof obj.bottomLeft === "number";
20
+ }
21
+ function isTokenRef(v) {
22
+ if (typeof v !== "object" || v === null) return false;
23
+ return v.kind === "token-ref";
24
+ }
25
+ function emitSpacingShorthand(prefix, trbl) {
26
+ const { top, right, bottom, left } = trbl;
27
+ if (top === right && right === bottom && bottom === left) {
28
+ return [prop(prefix, top)];
29
+ }
30
+ if (top === bottom && left === right) {
31
+ return [
32
+ prop(`${prefix}y`, top),
33
+ prop(`${prefix}x`, left)
34
+ ];
35
+ }
36
+ return [
37
+ prop(`${prefix}t`, top),
38
+ prop(`${prefix}r`, right),
39
+ prop(`${prefix}b`, bottom),
40
+ prop(`${prefix}l`, left)
41
+ ];
42
+ }
43
+ function emitRadiusShorthand(corners) {
44
+ const { topLeft, topRight, bottomRight, bottomLeft } = corners;
45
+ if (topLeft === topRight && topRight === bottomRight && bottomRight === bottomLeft) {
46
+ return [prop("rounded", topLeft)];
47
+ }
48
+ return [
49
+ prop("roundedTL", topLeft),
50
+ prop("roundedTR", topRight),
51
+ prop("roundedBR", bottomRight),
52
+ prop("roundedBL", bottomLeft)
53
+ ];
54
+ }
55
+ function emitColorValue(color) {
56
+ if (isTokenRef(color)) {
57
+ return t.stringLiteral(`token:${color.name}`);
58
+ }
59
+ if (typeof color === "string") {
60
+ return t.stringLiteral(color);
61
+ }
62
+ if (typeof color === "object" && color !== null) {
63
+ const c = color;
64
+ if (typeof c.r === "number" && typeof c.g === "number" && typeof c.b === "number") {
65
+ const a = typeof c.a === "number" ? c.a : 1;
66
+ if (a === 1) {
67
+ const hex = `#${hexByte(c.r)}${hexByte(c.g)}${hexByte(c.b)}`;
68
+ return t.stringLiteral(hex);
69
+ }
70
+ return t.stringLiteral(`rgba(${c.r}, ${c.g}, ${c.b}, ${a})`);
71
+ }
72
+ }
73
+ return t.stringLiteral("inherit");
74
+ }
75
+ function hexByte(n) {
76
+ return Math.round(n).toString(16).padStart(2, "0");
77
+ }
78
+ function prop(key, value) {
79
+ return t.objectProperty(t.identifier(key), valueToExpression(value));
80
+ }
81
+ function valueToExpression(value) {
82
+ if (typeof value === "string") return t.stringLiteral(value);
83
+ if (typeof value === "number") return t.numericLiteral(value);
84
+ if (typeof value === "boolean") return t.booleanLiteral(value);
85
+ if (value === null) return t.nullLiteral();
86
+ if (value === void 0) return t.identifier("undefined");
87
+ if (Array.isArray(value)) {
88
+ return t.arrayExpression(value.map((v) => valueToExpression(v)));
89
+ }
90
+ if (typeof value === "object") {
91
+ const entries = Object.entries(value);
92
+ return t.objectExpression(
93
+ entries.map(
94
+ ([k, v]) => t.objectProperty(t.identifier(k), valueToExpression(v))
95
+ )
96
+ );
97
+ }
98
+ return t.identifier("undefined");
99
+ }
100
+ var STATE_TO_SX_KEY = {
101
+ hovered: "hover",
102
+ pressed: "pressed",
103
+ focused: "focus",
104
+ selected: "selected",
105
+ disabled: "disabled"
106
+ };
107
+ function emitSxProp(style) {
108
+ if (!style || typeof style !== "object") return null;
109
+ const props = [];
110
+ if (style.padding && isTRBL(style.padding)) {
111
+ props.push(...emitSpacingShorthand("p", style.padding));
112
+ }
113
+ if (style.margin && isTRBL(style.margin)) {
114
+ props.push(...emitSpacingShorthand("m", style.margin));
115
+ }
116
+ if (style.backgroundColor) {
117
+ props.push(t.objectProperty(
118
+ t.identifier("bg"),
119
+ emitColorValue(style.backgroundColor)
120
+ ));
121
+ }
122
+ if (style.borderRadius && isFourCorners(style.borderRadius)) {
123
+ props.push(...emitRadiusShorthand(style.borderRadius));
124
+ }
125
+ if (typeof style.gap === "number") {
126
+ props.push(prop("gap", style.gap));
127
+ }
128
+ if (typeof style.flexDirection === "string") {
129
+ props.push(prop("direction", style.flexDirection));
130
+ }
131
+ if (typeof style.justifyContent === "string") {
132
+ props.push(prop("justify", style.justifyContent));
133
+ }
134
+ if (typeof style.alignItems === "string") {
135
+ props.push(prop("align", style.alignItems));
136
+ }
137
+ if (style.width !== void 0) props.push(prop("w", style.width));
138
+ if (style.height !== void 0) props.push(prop("h", style.height));
139
+ if (style.minWidth !== void 0) props.push(prop("minW", style.minWidth));
140
+ if (style.minHeight !== void 0) props.push(prop("minH", style.minHeight));
141
+ if (style.maxWidth !== void 0) props.push(prop("maxW", style.maxWidth));
142
+ if (style.maxHeight !== void 0) props.push(prop("maxH", style.maxHeight));
143
+ if (typeof style.opacity === "number") {
144
+ props.push(prop("opacity", style.opacity));
145
+ }
146
+ if (style.color) {
147
+ props.push(t.objectProperty(
148
+ t.identifier("color"),
149
+ emitColorValue(style.color)
150
+ ));
151
+ }
152
+ if (typeof style.overflow === "string") {
153
+ props.push(prop("overflow", style.overflow));
154
+ }
155
+ if (typeof style.position === "string") {
156
+ props.push(prop("position", style.position));
157
+ }
158
+ if (typeof style.states === "object" && style.states !== null) {
159
+ for (const [state, override] of Object.entries(style.states)) {
160
+ const sxKey = STATE_TO_SX_KEY[state] || state;
161
+ if (typeof override === "object" && override !== null) {
162
+ const subProp = emitSxProp(override);
163
+ if (subProp && t.isJSXExpressionContainer(subProp.value) && t.isObjectExpression(subProp.value.expression)) {
164
+ props.push(t.objectProperty(
165
+ t.identifier(sxKey),
166
+ subProp.value.expression
167
+ ));
168
+ }
169
+ }
170
+ }
171
+ }
172
+ if (props.length === 0) return null;
173
+ return t.jsxAttribute(
174
+ t.jsxIdentifier("sx"),
175
+ t.jsxExpressionContainer(t.objectExpression(props))
176
+ );
177
+ }
178
+
179
+ // src/decompiler/ast-builder.ts
180
+ function buildSymbolTable(fields, metadata, transitions, experience) {
181
+ const locals = /* @__PURE__ */ new Map();
182
+ const setters = /* @__PURE__ */ new Map();
183
+ const queries = /* @__PURE__ */ new Map();
184
+ const transMap = /* @__PURE__ */ new Map();
185
+ for (const field of fields) {
186
+ if (field.computed) continue;
187
+ const camel = toCamelCase(field.name);
188
+ locals.set(field.name, camel);
189
+ setters.set(field.name, setterName(field.name));
190
+ }
191
+ const dataSources = metadata?.dataSources;
192
+ if (dataSources) {
193
+ for (const ds of dataSources) {
194
+ if (ds.type !== "workflow") continue;
195
+ const varName = toCamelCase(ds.name.replace(/-/g, "_"));
196
+ queries.set(ds.name, varName);
197
+ queries.set(ds.name.replace(/-/g, "_"), varName);
198
+ }
199
+ }
200
+ for (const trans of transitions) {
201
+ transMap.set(trans.name, toCamelCase(trans.name));
202
+ }
203
+ if (experience) {
204
+ collectLocalDefaults(experience, locals, setters);
205
+ }
206
+ return { locals, setters, queries, transitions: transMap };
207
+ }
208
+ function collectLocalDefaults(node, locals, setters) {
209
+ if (node.config?.localDefaults && typeof node.config.localDefaults === "object") {
210
+ for (const key of Object.keys(node.config.localDefaults)) {
211
+ if (key.includes(".")) continue;
212
+ if (!locals.has(key)) {
213
+ const camel = toCamelCase(key);
214
+ locals.set(key, camel);
215
+ setters.set(key, setterName(key));
216
+ }
217
+ }
218
+ }
219
+ if (node.children) {
220
+ for (const child of node.children) {
221
+ collectLocalDefaults(child, locals, setters);
222
+ }
223
+ }
224
+ }
225
+ var REACT_ATOMS = /* @__PURE__ */ new Set([
226
+ "Stack",
227
+ "Row",
228
+ "Column",
229
+ "Grid",
230
+ "Divider",
231
+ "Spacer",
232
+ "Text",
233
+ "Heading",
234
+ "Field",
235
+ "Image",
236
+ "Badge",
237
+ "Icon",
238
+ "Button",
239
+ "Link",
240
+ "Show",
241
+ "Each",
242
+ "Card",
243
+ "Tabs",
244
+ "Accordion",
245
+ "Section",
246
+ "Modal",
247
+ "TextInput",
248
+ "Select",
249
+ "Markdown",
250
+ "ScrollArea",
251
+ "Canvas3D",
252
+ "DataGrid",
253
+ "AnimatedBox",
254
+ "ServerGrid",
255
+ "Chart",
256
+ "MetricCard",
257
+ "Slot"
258
+ ]);
259
+ function resolveIRTokens(expr, symbols) {
260
+ let result = expr;
261
+ const exprMatch = result.match(/^\$expr\((.+)\)$/s);
262
+ if (exprMatch) {
263
+ return resolveIRTokens(exprMatch[1], symbols);
264
+ }
265
+ const seqMatch = result.match(/^\$action\.seq\((.+)\)$/);
266
+ if (seqMatch) {
267
+ const actions = splitTopLevelArgs(seqMatch[1]);
268
+ const resolved = actions.map((a) => resolveIRTokens(a.trim(), symbols));
269
+ return `() => { ${resolved.join("; ")}; }`;
270
+ }
271
+ while (result.includes("$action.setLocal(")) {
272
+ const updated = resolveOneSetLocal(result, symbols);
273
+ if (updated === result) break;
274
+ result = updated;
275
+ }
276
+ result = result.replace(/\$action\.transition\("([^"]+)"\)/g, (_, name) => {
277
+ const varName = symbols.transitions.get(name) || toCamelCase(name);
278
+ return `${varName}()`;
279
+ });
280
+ result = result.replace(/\$instance\.\[/g, "[");
281
+ result = result.replace(/\$instance\??\.(?:state_data\.)?(\w+)/g, (_, field) => {
282
+ return symbols.queries.get(field) || toCamelCase(field);
283
+ });
284
+ result = result.replace(/\$local\.(\w+)/g, (_, field) => {
285
+ return symbols.locals.get(field) || toCamelCase(field);
286
+ });
287
+ const concatMatch = result.match(/^\$fn\.concat\((.+)\)$/);
288
+ if (concatMatch) {
289
+ const parts = splitTopLevelArgs(concatMatch[1]);
290
+ return "`" + parts.map((p) => `\${${p.trim()}}`).join("") + "`";
291
+ }
292
+ const notMatch = result.match(/^not\((.+)\)$/);
293
+ if (notMatch) {
294
+ return `!(${resolveIRTokens(notMatch[1], symbols)})`;
295
+ }
296
+ return result;
297
+ }
298
+ function resolveOneSetLocal(expr, symbols) {
299
+ const prefix = "$action.setLocal(";
300
+ const idx = expr.indexOf(prefix);
301
+ if (idx === -1) return expr;
302
+ let depth = 0;
303
+ let end = -1;
304
+ for (let i = idx + prefix.length - 1; i < expr.length; i++) {
305
+ if (expr[i] === "(") depth++;
306
+ else if (expr[i] === ")") {
307
+ depth--;
308
+ if (depth === 0) {
309
+ end = i;
310
+ break;
311
+ }
312
+ }
313
+ }
314
+ if (end === -1) return expr;
315
+ const inner = expr.substring(idx + prefix.length, end);
316
+ const fieldMatch = inner.match(/^"([^"]+)",\s*/);
317
+ if (!fieldMatch) return expr;
318
+ const field = fieldMatch[1];
319
+ const value = inner.substring(fieldMatch[0].length);
320
+ const setter = symbols.setters.get(field) || setterName(field);
321
+ const resolvedValue = resolveIRTokens(value.trim(), symbols);
322
+ return expr.substring(0, idx) + `${setter}(${resolvedValue})` + expr.substring(end + 1);
323
+ }
324
+ function splitTopLevelArgs(input) {
325
+ const result = [];
326
+ let depth = 0;
327
+ let inStr = null;
328
+ let current = "";
329
+ for (let i = 0; i < input.length; i++) {
330
+ const ch = input[i];
331
+ if ((ch === '"' || ch === "'" || ch === "`") && (i === 0 || input[i - 1] !== "\\")) {
332
+ if (inStr === ch) inStr = null;
333
+ else if (!inStr) inStr = ch;
334
+ }
335
+ if (!inStr) {
336
+ if (ch === "(" || ch === "[" || ch === "{") depth++;
337
+ else if (ch === ")" || ch === "]" || ch === "}") depth--;
338
+ else if (ch === "," && depth === 0) {
339
+ result.push(current);
340
+ current = "";
341
+ continue;
342
+ }
343
+ }
344
+ current += ch;
345
+ }
346
+ if (current.trim()) result.push(current);
347
+ return result;
348
+ }
349
+ function emitBindingExpression(expr, symbols) {
350
+ if (expr === "[Expression]" || expr === '"[Expression]"') {
351
+ return t2.nullLiteral();
352
+ }
353
+ const resolved = resolveIRTokens(expr, symbols);
354
+ const fieldsMatch = resolved.match(/^fields\.(\w+)$/);
355
+ if (fieldsMatch) {
356
+ return t2.identifier(toCamelCase(fieldsMatch[1]));
357
+ }
358
+ const setFieldMatch = resolved.match(/^set_field\((\w+),\s*(.+)\)$/);
359
+ if (setFieldMatch) {
360
+ const setter = setterName(setFieldMatch[1]);
361
+ return t2.callExpression(
362
+ t2.identifier(setter),
363
+ [emitBindingExpression(setFieldMatch[2].trim(), symbols)]
364
+ );
365
+ }
366
+ if (/^\w+$/.test(resolved)) {
367
+ return t2.identifier(toCamelCase(resolved));
368
+ }
369
+ if (/^[\w$]+(?:\??\.[\w$]+)+$/.test(resolved)) {
370
+ const parts = resolved.split(/(\??\.)/).filter(Boolean);
371
+ let node = t2.identifier(parts[0]);
372
+ for (let i = 1; i < parts.length; i += 2) {
373
+ const isOptional = parts[i] === "?.";
374
+ const prop2 = parts[i + 1];
375
+ if (!prop2) break;
376
+ node = t2.optionalMemberExpression(
377
+ node,
378
+ t2.identifier(prop2),
379
+ false,
380
+ isOptional
381
+ );
382
+ }
383
+ return node;
384
+ }
385
+ if (/[=<>!+\-*/%&|^~?:,()[\]{}]/.test(resolved)) {
386
+ try {
387
+ return parseExpression(resolved, {
388
+ plugins: ["typescript", "jsx"]
389
+ });
390
+ } catch {
391
+ return t2.stringLiteral(resolved);
392
+ }
393
+ }
394
+ return t2.stringLiteral(resolved);
395
+ }
396
+ function emitConfigAttribute(key, value) {
397
+ if (typeof value === "string") {
398
+ if (value.includes('"') || value.includes("\\") || value.includes("\n")) {
399
+ return t2.jsxAttribute(
400
+ t2.jsxIdentifier(key),
401
+ t2.jsxExpressionContainer(t2.stringLiteral(value))
402
+ );
403
+ }
404
+ return t2.jsxAttribute(
405
+ t2.jsxIdentifier(key),
406
+ t2.stringLiteral(value)
407
+ );
408
+ }
409
+ if (typeof value === "boolean" && value === true) {
410
+ return t2.jsxAttribute(t2.jsxIdentifier(key), null);
411
+ }
412
+ return t2.jsxAttribute(
413
+ t2.jsxIdentifier(key),
414
+ t2.jsxExpressionContainer(valueToExpression(value))
415
+ );
416
+ }
417
+ function toCamelCase(str) {
418
+ return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
419
+ }
420
+ function setterName(snakeName) {
421
+ const camel = toCamelCase(snakeName);
422
+ return `set${camel.charAt(0).toUpperCase()}${camel.slice(1)}`;
423
+ }
424
+ function buildJSXTree(node, imports, symbols) {
425
+ if (!node.component) {
426
+ const fragmentChildren = (node.children || []).map(
427
+ (child) => buildJSXTree(child, imports, symbols)
428
+ );
429
+ return t2.jsxFragment(
430
+ t2.jsxOpeningFragment(),
431
+ t2.jsxClosingFragment(),
432
+ fragmentChildren
433
+ );
434
+ }
435
+ const componentName = node.component;
436
+ if (REACT_ATOMS.has(componentName)) {
437
+ addImport(imports, "@mmapp/react", componentName);
438
+ }
439
+ const attributes = [];
440
+ if (node.config) {
441
+ for (const [key, value] of Object.entries(node.config)) {
442
+ if (key === "localDefaults") continue;
443
+ attributes.push(emitConfigAttribute(key, value));
444
+ }
445
+ }
446
+ if (node.bindings) {
447
+ for (const [key, expr] of Object.entries(node.bindings)) {
448
+ let emitted = emitBindingExpression(expr, symbols);
449
+ if (isEventHandlerKey(key) && !t2.isArrowFunctionExpression(emitted) && !t2.isFunctionExpression(emitted)) {
450
+ emitted = t2.arrowFunctionExpression([], emitted);
451
+ }
452
+ attributes.push(
453
+ t2.jsxAttribute(
454
+ t2.jsxIdentifier(key),
455
+ t2.jsxExpressionContainer(emitted)
456
+ )
457
+ );
458
+ }
459
+ }
460
+ if (node.style && typeof node.style === "object" && Object.keys(node.style).length > 0) {
461
+ const sxAttr = emitSxProp(node.style);
462
+ if (sxAttr) attributes.push(sxAttr);
463
+ }
464
+ const jsxChildren = [];
465
+ if (node.children) {
466
+ for (const child of node.children) {
467
+ jsxChildren.push(buildJSXTree(child, imports, symbols));
468
+ }
469
+ }
470
+ const isSelfClosing = jsxChildren.length === 0;
471
+ const element = t2.jsxElement(
472
+ t2.jsxOpeningElement(
473
+ t2.jsxIdentifier(componentName),
474
+ attributes,
475
+ isSelfClosing
476
+ ),
477
+ isSelfClosing ? null : t2.jsxClosingElement(t2.jsxIdentifier(componentName)),
478
+ jsxChildren,
479
+ isSelfClosing
480
+ );
481
+ if (node.visible_when) {
482
+ addImport(imports, "@mmapp/react", "Show");
483
+ return t2.jsxElement(
484
+ t2.jsxOpeningElement(
485
+ t2.jsxIdentifier("Show"),
486
+ [
487
+ t2.jsxAttribute(
488
+ t2.jsxIdentifier("when"),
489
+ t2.jsxExpressionContainer(emitBindingExpression(node.visible_when, symbols))
490
+ )
491
+ ],
492
+ false
493
+ ),
494
+ t2.jsxClosingElement(t2.jsxIdentifier("Show")),
495
+ [element],
496
+ false
497
+ );
498
+ }
499
+ return element;
500
+ }
501
+ function isEventHandlerKey(key) {
502
+ return /^on[A-Z]/.test(key);
503
+ }
504
+ function addImport(tracker, source, name) {
505
+ if (!tracker.has(source)) tracker.set(source, /* @__PURE__ */ new Set());
506
+ tracker.get(source).add(name);
507
+ }
508
+
509
+ // src/decompiler/state-emitter.ts
510
+ import * as t3 from "@babel/types";
511
+ function toCamelCase2(str) {
512
+ return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
513
+ }
514
+ function toPascalCase(str) {
515
+ const camel = toCamelCase2(str);
516
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
517
+ }
518
+ function fieldTypeAnnotation(field) {
519
+ const f = field;
520
+ const validation = f.validation;
521
+ const options = validation?.options || f.metadata?.options || f.options;
522
+ if (options && Array.isArray(options) && options.length > 0 && (field.type === "select" || field.type === "text")) {
523
+ return t3.tsUnionType(
524
+ options.map((o) => t3.tsLiteralType(t3.stringLiteral(String(o))))
525
+ );
526
+ }
527
+ switch (field.type) {
528
+ case "text":
529
+ case "rich_text":
530
+ case "email":
531
+ case "url":
532
+ case "phone":
533
+ case "color":
534
+ return t3.tsStringKeyword();
535
+ case "number":
536
+ case "currency":
537
+ case "percentage":
538
+ case "rating":
539
+ case "duration":
540
+ case "auto_number":
541
+ return t3.tsNumberKeyword();
542
+ case "boolean":
543
+ return t3.tsBooleanKeyword();
544
+ case "date":
545
+ case "datetime":
546
+ case "created_at":
547
+ case "updated_at":
548
+ return t3.tsTypeReference(t3.identifier("Date"));
549
+ case "select":
550
+ return t3.tsStringKeyword();
551
+ case "multi_select":
552
+ return t3.tsArrayType(t3.tsStringKeyword());
553
+ // string[]
554
+ case "json":
555
+ return null;
556
+ // Inferred from default
557
+ default:
558
+ return null;
559
+ }
560
+ }
561
+ function emitFieldDeclarations(fields, imports) {
562
+ if (fields.length === 0) return [];
563
+ addImport2(imports, "@mmapp/react", "useState");
564
+ const statements = [];
565
+ for (const field of fields) {
566
+ if (field.computed) continue;
567
+ const camelName = toCamelCase2(field.name);
568
+ const setter = `set${toPascalCase(field.name)}`;
569
+ const defaultExpr = field.default_value !== void 0 ? valueToExpression(field.default_value) : inferDefaultFromType(field.type);
570
+ const args = [defaultExpr];
571
+ if (field.state_home?.scope === "ephemeral") {
572
+ args.push(t3.objectExpression([
573
+ t3.objectProperty(t3.identifier("ephemeral"), t3.booleanLiteral(true))
574
+ ]));
575
+ }
576
+ const declaration = t3.variableDeclaration("const", [
577
+ t3.variableDeclarator(
578
+ t3.arrayPattern([
579
+ t3.identifier(camelName),
580
+ t3.identifier(setter)
581
+ ]),
582
+ t3.callExpression(t3.identifier("useState"), args)
583
+ )
584
+ ]);
585
+ if (field.default_value !== null) {
586
+ const tsType = fieldTypeAnnotation(field);
587
+ if (tsType) {
588
+ const callExpr = declaration.declarations[0].init;
589
+ callExpr.typeParameters = t3.tsTypeParameterInstantiation([tsType]);
590
+ }
591
+ }
592
+ statements.push(declaration);
593
+ }
594
+ return statements;
595
+ }
596
+ function inferDefaultFromType(fieldType) {
597
+ switch (fieldType) {
598
+ case "text":
599
+ case "rich_text":
600
+ case "email":
601
+ case "url":
602
+ case "phone":
603
+ case "color":
604
+ case "select":
605
+ return t3.stringLiteral("");
606
+ case "number":
607
+ case "currency":
608
+ case "percentage":
609
+ case "rating":
610
+ case "duration":
611
+ case "auto_number":
612
+ return t3.numericLiteral(0);
613
+ case "boolean":
614
+ return t3.booleanLiteral(false);
615
+ case "multi_select":
616
+ return t3.arrayExpression([]);
617
+ case "json":
618
+ return t3.objectExpression([]);
619
+ default:
620
+ return t3.nullLiteral();
621
+ }
622
+ }
623
+ function emitStateEffects(states, imports) {
624
+ const statements = [];
625
+ for (const state of states) {
626
+ if (state.on_enter.length > 0) {
627
+ addImport2(imports, "@mmapp/react", "useOnEnter");
628
+ statements.push(
629
+ t3.expressionStatement(
630
+ t3.callExpression(t3.identifier("useOnEnter"), [
631
+ t3.stringLiteral(state.name),
632
+ emitActionCallback(state.on_enter)
633
+ ])
634
+ )
635
+ );
636
+ }
637
+ if (state.on_exit.length > 0) {
638
+ addImport2(imports, "@mmapp/react", "useOnExit");
639
+ statements.push(
640
+ t3.expressionStatement(
641
+ t3.callExpression(t3.identifier("useOnExit"), [
642
+ t3.stringLiteral(state.name),
643
+ emitActionCallback(state.on_exit)
644
+ ])
645
+ )
646
+ );
647
+ }
648
+ if (state.during.length > 0) {
649
+ addImport2(imports, "@mmapp/react", "useWhileIn");
650
+ for (const during of state.during) {
651
+ const args = [
652
+ t3.stringLiteral(state.name)
653
+ ];
654
+ if (during.interval_ms) {
655
+ args.push(t3.numericLiteral(during.interval_ms));
656
+ }
657
+ args.push(emitActionCallback(during.actions));
658
+ statements.push(
659
+ t3.expressionStatement(
660
+ t3.callExpression(t3.identifier("useWhileIn"), args)
661
+ )
662
+ );
663
+ }
664
+ }
665
+ }
666
+ return statements;
667
+ }
668
+ function emitTransitionDeclarations(transitions, imports) {
669
+ if (transitions.length === 0) return [];
670
+ addImport2(imports, "@mmapp/react", "useTransition");
671
+ const correctedTransitions = correctFromFields(transitions);
672
+ const statements = [];
673
+ for (const transition of correctedTransitions) {
674
+ const varName = toCamelCase2(transition.name);
675
+ const configProps = [];
676
+ const fromArr = normalizeFrom(transition.from);
677
+ if (fromArr.length === 1) {
678
+ configProps.push(
679
+ t3.objectProperty(t3.identifier("from"), t3.stringLiteral(fromArr[0]))
680
+ );
681
+ } else if (fromArr.length > 1) {
682
+ configProps.push(
683
+ t3.objectProperty(
684
+ t3.identifier("from"),
685
+ t3.arrayExpression(fromArr.map((s) => t3.stringLiteral(s)))
686
+ )
687
+ );
688
+ }
689
+ configProps.push(
690
+ t3.objectProperty(t3.identifier("to"), t3.stringLiteral(transition.to))
691
+ );
692
+ if (transition.conditions && transition.conditions.length > 0) {
693
+ configProps.push(
694
+ t3.objectProperty(
695
+ t3.identifier("when"),
696
+ emitConditionExpression(transition.conditions)
697
+ )
698
+ );
699
+ }
700
+ if (transition.roles && transition.roles.length > 0) {
701
+ configProps.push(
702
+ t3.objectProperty(
703
+ t3.identifier("roles"),
704
+ t3.arrayExpression(transition.roles.map((r) => t3.stringLiteral(r)))
705
+ )
706
+ );
707
+ }
708
+ if (transition.auto) {
709
+ configProps.push(
710
+ t3.objectProperty(t3.identifier("auto"), t3.booleanLiteral(true))
711
+ );
712
+ }
713
+ if (transition.required_fields && transition.required_fields.length > 0) {
714
+ configProps.push(
715
+ t3.objectProperty(
716
+ t3.identifier("requiredFields"),
717
+ t3.arrayExpression(transition.required_fields.map((f) => t3.stringLiteral(f)))
718
+ )
719
+ );
720
+ }
721
+ statements.push(
722
+ t3.variableDeclaration("const", [
723
+ t3.variableDeclarator(
724
+ t3.identifier(varName),
725
+ t3.callExpression(t3.identifier("useTransition"), [
726
+ t3.stringLiteral(transition.name),
727
+ t3.objectExpression(configProps)
728
+ ])
729
+ )
730
+ ])
731
+ );
732
+ }
733
+ return statements;
734
+ }
735
+ function emitActionCallback(actions) {
736
+ const body = [];
737
+ for (const action of actions) {
738
+ const stmt = emitSingleAction(action);
739
+ if (stmt) body.push(stmt);
740
+ }
741
+ if (body.length === 0) {
742
+ body.push(t3.emptyStatement());
743
+ }
744
+ return t3.arrowFunctionExpression([], t3.blockStatement(body));
745
+ }
746
+ function emitSingleAction(action) {
747
+ switch (action.type) {
748
+ case "set_field": {
749
+ if (!action.config.field) return null;
750
+ const setter = `set${toPascalCase(String(action.config.field))}`;
751
+ const expr = action.config.expression ? t3.identifier(String(action.config.expression)) : valueToExpression(action.config.value ?? null);
752
+ return t3.expressionStatement(
753
+ t3.callExpression(t3.identifier(setter), [expr])
754
+ );
755
+ }
756
+ case "log_event":
757
+ case "log": {
758
+ const msg = action.config.message ?? action.config.event ?? action.type;
759
+ return t3.expressionStatement(
760
+ t3.callExpression(
761
+ t3.memberExpression(t3.identifier("console"), t3.identifier("log")),
762
+ [t3.stringLiteral(String(msg))]
763
+ )
764
+ );
765
+ }
766
+ case "emit_event": {
767
+ const eventName = String(action.config.event ?? action.config.name ?? "unknown");
768
+ const args = [t3.stringLiteral(eventName)];
769
+ if (action.config.payload) {
770
+ args.push(valueToExpression(action.config.payload));
771
+ }
772
+ return t3.expressionStatement(
773
+ t3.callExpression(t3.identifier("emitEvent"), args)
774
+ );
775
+ }
776
+ case "call_workflow": {
777
+ const slug = String(action.config.slug ?? action.config.workflow ?? "unknown");
778
+ const args = [t3.stringLiteral(slug)];
779
+ if (action.config.params) {
780
+ args.push(valueToExpression(action.config.params));
781
+ }
782
+ return t3.expressionStatement(
783
+ t3.callExpression(t3.identifier("callWorkflow"), args)
784
+ );
785
+ }
786
+ case "spawn_instance":
787
+ case "spawn_subworkflow": {
788
+ const slug = String(action.config.slug ?? action.config.workflow ?? "unknown");
789
+ const args = [t3.stringLiteral(slug)];
790
+ if (action.config.params || action.config.input) {
791
+ args.push(valueToExpression(action.config.params ?? action.config.input));
792
+ }
793
+ return t3.expressionStatement(
794
+ t3.callExpression(t3.identifier("spawnInstance"), args)
795
+ );
796
+ }
797
+ case "http_request":
798
+ case "webhook":
799
+ case "call_webhook": {
800
+ const url = String(action.config.url ?? "");
801
+ const method = String(action.config.method ?? "POST");
802
+ return t3.expressionStatement(
803
+ t3.callExpression(t3.identifier("fetch"), [
804
+ t3.stringLiteral(url),
805
+ t3.objectExpression([
806
+ t3.objectProperty(t3.identifier("method"), t3.stringLiteral(method))
807
+ ])
808
+ ])
809
+ );
810
+ }
811
+ case "delay": {
812
+ const ms = Number(action.config.duration_ms ?? action.config.delay_ms ?? 1e3);
813
+ return t3.expressionStatement(
814
+ t3.awaitExpression(
815
+ t3.callExpression(
816
+ t3.identifier("delay"),
817
+ [t3.numericLiteral(ms)]
818
+ )
819
+ )
820
+ );
821
+ }
822
+ case "notify":
823
+ case "send_notification": {
824
+ const msg = String(action.config.message ?? action.config.body ?? "");
825
+ return t3.expressionStatement(
826
+ t3.callExpression(t3.identifier("notify"), [t3.stringLiteral(msg)])
827
+ );
828
+ }
829
+ case "noop":
830
+ return null;
831
+ default: {
832
+ const comment = ` ${action.type}(${JSON.stringify(action.config)}) `;
833
+ const voidExpr = t3.unaryExpression("void", t3.numericLiteral(0));
834
+ t3.addComment(voidExpr, "leading", comment, false);
835
+ return t3.expressionStatement(voidExpr);
836
+ }
837
+ }
838
+ }
839
+ function emitConditionExpression(conditions) {
840
+ if (conditions.length === 1) {
841
+ return emitSingleCondition(conditions[0]);
842
+ }
843
+ let result = emitSingleCondition(conditions[0]);
844
+ for (let i = 1; i < conditions.length; i++) {
845
+ result = t3.logicalExpression("&&", result, emitSingleCondition(conditions[i]));
846
+ }
847
+ return result;
848
+ }
849
+ function emitSingleCondition(cond) {
850
+ if (cond.expression) {
851
+ return t3.identifier(cond.expression);
852
+ }
853
+ if (cond.field && cond.operator) {
854
+ const fieldExpr = t3.identifier(toCamelCase2(cond.field));
855
+ const valueExpr = valueToExpression(cond.value);
856
+ const opMap = {
857
+ eq: "===",
858
+ ne: "!==",
859
+ gt: ">",
860
+ gte: ">=",
861
+ lt: "<",
862
+ lte: "<="
863
+ };
864
+ const jsOp = opMap[cond.operator];
865
+ if (jsOp) {
866
+ return t3.binaryExpression(jsOp, fieldExpr, valueExpr);
867
+ }
868
+ if (cond.operator === "is_set") {
869
+ return t3.binaryExpression("!==", fieldExpr, t3.nullLiteral());
870
+ }
871
+ if (cond.operator === "is_empty") {
872
+ return t3.binaryExpression("===", fieldExpr, t3.nullLiteral());
873
+ }
874
+ if (cond.operator === "contains" && typeof cond.value === "string") {
875
+ return t3.callExpression(
876
+ t3.memberExpression(fieldExpr, t3.identifier("includes")),
877
+ [t3.stringLiteral(cond.value)]
878
+ );
879
+ }
880
+ if (cond.operator === "in" && Array.isArray(cond.value)) {
881
+ return t3.callExpression(
882
+ t3.memberExpression(
883
+ t3.arrayExpression(cond.value.map((v) => valueToExpression(v))),
884
+ t3.identifier("includes")
885
+ ),
886
+ [fieldExpr]
887
+ );
888
+ }
889
+ }
890
+ if (cond.OR && cond.OR.length > 0) {
891
+ let result = emitSingleCondition(cond.OR[0]);
892
+ for (let i = 1; i < cond.OR.length; i++) {
893
+ result = t3.logicalExpression("||", result, emitSingleCondition(cond.OR[i]));
894
+ }
895
+ return result;
896
+ }
897
+ if (cond.AND && cond.AND.length > 0) {
898
+ return emitConditionExpression(cond.AND);
899
+ }
900
+ return t3.booleanLiteral(true);
901
+ }
902
+ function emitRoleDeclarations(metadata, imports) {
903
+ const roleDeps = metadata?.roleDependencies;
904
+ if (!roleDeps || roleDeps.length === 0) return [];
905
+ addImport2(imports, "@mmapp/react", "useRole");
906
+ const statements = [];
907
+ for (const role of roleDeps) {
908
+ const varName = `is${toPascalCase(role)}`;
909
+ statements.push(
910
+ t3.variableDeclaration("const", [
911
+ t3.variableDeclarator(
912
+ t3.identifier(varName),
913
+ t3.callExpression(t3.identifier("useRole"), [t3.stringLiteral(role)])
914
+ )
915
+ ])
916
+ );
917
+ }
918
+ return statements;
919
+ }
920
+ function emitQueryDeclarations(metadata, imports) {
921
+ const statements = [];
922
+ const dataSources = metadata?.dataSources;
923
+ const modelImports = metadata?.modelImports;
924
+ const addedModelImports = /* @__PURE__ */ new Set();
925
+ if (dataSources && dataSources.length > 0) {
926
+ addImport2(imports, "@mmapp/react", "useQuery");
927
+ const usedVarNames = /* @__PURE__ */ new Set();
928
+ for (const ds of dataSources) {
929
+ if (ds.type !== "workflow") continue;
930
+ const slug = ds.slug || ds.name;
931
+ const baseName = ds.name.includes(".") ? ds.name.split(".").pop() : ds.name;
932
+ let varName = toCamelCase2(baseName.replace(/-/g, "_"));
933
+ if (usedVarNames.has(varName)) {
934
+ let suffix = 2;
935
+ while (usedVarNames.has(`${varName}${suffix}`)) suffix++;
936
+ varName = `${varName}${suffix}`;
937
+ }
938
+ usedVarNames.add(varName);
939
+ const modelPath = modelImports?.[slug];
940
+ const modelVarName = modelPath ? `${toCamelCase2(slug.replace(/-/g, "_"))}Model` : void 0;
941
+ if (modelPath && modelVarName && !addedModelImports.has(slug)) {
942
+ addImport2(imports, modelPath, `default as ${modelVarName}`);
943
+ addedModelImports.add(slug);
944
+ }
945
+ const optionProps = [];
946
+ if (ds.pageSize) {
947
+ optionProps.push(
948
+ t3.objectProperty(t3.identifier("limit"), t3.numericLiteral(ds.pageSize))
949
+ );
950
+ }
951
+ if (ds.sort) {
952
+ optionProps.push(
953
+ t3.objectProperty(t3.identifier("orderBy"), t3.stringLiteral(ds.sort))
954
+ );
955
+ }
956
+ if (ds.filter && Object.keys(ds.filter).length > 0) {
957
+ optionProps.push(
958
+ t3.objectProperty(t3.identifier("filter"), valueToExpression(ds.filter))
959
+ );
960
+ }
961
+ if (ds.search) {
962
+ optionProps.push(
963
+ t3.objectProperty(t3.identifier("search"), t3.stringLiteral(ds.search))
964
+ );
965
+ }
966
+ if (ds.groupBy) {
967
+ optionProps.push(
968
+ t3.objectProperty(t3.identifier("groupBy"), t3.stringLiteral(ds.groupBy))
969
+ );
970
+ }
971
+ const queryTarget = modelVarName ? t3.identifier(modelVarName) : t3.stringLiteral(slug);
972
+ const args = [queryTarget];
973
+ if (optionProps.length > 0) {
974
+ args.push(t3.objectExpression(optionProps));
975
+ }
976
+ statements.push(
977
+ t3.variableDeclaration("const", [
978
+ t3.variableDeclarator(
979
+ t3.objectPattern([
980
+ t3.objectProperty(
981
+ t3.identifier("data"),
982
+ t3.identifier(varName)
983
+ )
984
+ ]),
985
+ t3.callExpression(t3.identifier("useQuery"), args)
986
+ )
987
+ ])
988
+ );
989
+ }
990
+ }
991
+ const mutationTargets = metadata?.mutationTargets;
992
+ if (mutationTargets && mutationTargets.length > 0) {
993
+ addImport2(imports, "@mmapp/react", "useMutation");
994
+ for (const slug of mutationTargets) {
995
+ const modelPath = modelImports?.[slug];
996
+ const modelVarName2 = modelPath ? `${toCamelCase2(slug.replace(/-/g, "_"))}Model` : void 0;
997
+ if (modelPath && modelVarName2 && !addedModelImports?.has(slug)) {
998
+ addImport2(imports, modelPath, `default as ${modelVarName2}`);
999
+ addedModelImports?.add(slug);
1000
+ }
1001
+ const mutTarget = modelVarName2 ? t3.identifier(modelVarName2) : t3.stringLiteral(slug);
1002
+ const varName = `${toCamelCase2(slug.replace(/-/g, "_"))}Mutation`;
1003
+ statements.push(
1004
+ t3.variableDeclaration("const", [
1005
+ t3.variableDeclarator(
1006
+ t3.identifier(varName),
1007
+ t3.callExpression(t3.identifier("useMutation"), [mutTarget])
1008
+ )
1009
+ ])
1010
+ );
1011
+ }
1012
+ }
1013
+ return statements;
1014
+ }
1015
+ function emitChangeWatchers(metadata, imports) {
1016
+ const watchers = metadata?.fieldWatchers;
1017
+ if (!watchers || watchers.length === 0) return [];
1018
+ addImport2(imports, "@mmapp/react", "useOnChange");
1019
+ const statements = [];
1020
+ for (const watcher of watchers) {
1021
+ statements.push(
1022
+ t3.expressionStatement(
1023
+ t3.callExpression(t3.identifier("useOnChange"), [
1024
+ t3.stringLiteral(watcher.field),
1025
+ emitActionCallback(watcher.actions)
1026
+ ])
1027
+ )
1028
+ );
1029
+ }
1030
+ return statements;
1031
+ }
1032
+ function emitEventSubscriptions(onEvent, imports) {
1033
+ if (!onEvent || onEvent.length === 0) return [];
1034
+ addImport2(imports, "@mmapp/react", "useOnEvent");
1035
+ const statements = [];
1036
+ for (const sub of onEvent) {
1037
+ const actions = sub.actions.map((a, i) => ({
1038
+ id: `event_${i}`,
1039
+ type: a.type,
1040
+ mode: "auto",
1041
+ config: {
1042
+ ...a.field && { field: a.field },
1043
+ ...a.expression && { expression: a.expression },
1044
+ ...a.message && { message: a.message }
1045
+ }
1046
+ }));
1047
+ statements.push(
1048
+ t3.expressionStatement(
1049
+ t3.callExpression(t3.identifier("useOnEvent"), [
1050
+ t3.stringLiteral(sub.match),
1051
+ emitActionCallback(actions)
1052
+ ])
1053
+ )
1054
+ );
1055
+ }
1056
+ return statements;
1057
+ }
1058
+ function emitTransitionEffects(metadata, imports) {
1059
+ const effects = metadata?.transitionEffects;
1060
+ if (!effects || effects.length === 0) return [];
1061
+ addImport2(imports, "@mmapp/react", "useOnTransition");
1062
+ const statements = [];
1063
+ for (const effect of effects) {
1064
+ statements.push(
1065
+ t3.expressionStatement(
1066
+ t3.callExpression(t3.identifier("useOnTransition"), [
1067
+ t3.stringLiteral(effect.transition),
1068
+ emitActionCallback(effect.actions)
1069
+ ])
1070
+ )
1071
+ );
1072
+ }
1073
+ return statements;
1074
+ }
1075
+ function emitLocalDefaultDeclarations(experience, existingFields, imports) {
1076
+ const allDefaults = /* @__PURE__ */ new Map();
1077
+ collectAllLocalDefaults(experience, allDefaults);
1078
+ const existingNames = /* @__PURE__ */ new Set();
1079
+ for (const f of existingFields) {
1080
+ if (f.computed) continue;
1081
+ existingNames.add(f.name);
1082
+ existingNames.add(toCamelCase2(f.name));
1083
+ }
1084
+ const emittedCamelNames = /* @__PURE__ */ new Set();
1085
+ const statements = [];
1086
+ for (const [key, value] of allDefaults) {
1087
+ if (key.includes(".")) continue;
1088
+ if (existingNames.has(key) || existingNames.has(toCamelCase2(key))) continue;
1089
+ const camelName = toCamelCase2(key);
1090
+ if (emittedCamelNames.has(camelName)) continue;
1091
+ emittedCamelNames.add(camelName);
1092
+ addImport2(imports, "@mmapp/react", "useState");
1093
+ const setter = `set${toPascalCase(key)}`;
1094
+ let defaultExpr;
1095
+ if (value === void 0 || value === null) {
1096
+ defaultExpr = t3.identifier("undefined");
1097
+ } else if (typeof value === "string" && /^[a-zA-Z]\w*$/.test(value) && value !== key) {
1098
+ defaultExpr = t3.identifier("undefined");
1099
+ } else {
1100
+ defaultExpr = valueToExpression(value);
1101
+ }
1102
+ statements.push(
1103
+ t3.variableDeclaration("const", [
1104
+ t3.variableDeclarator(
1105
+ t3.arrayPattern([
1106
+ t3.identifier(camelName),
1107
+ t3.identifier(setter)
1108
+ ]),
1109
+ t3.callExpression(t3.identifier("useState"), [defaultExpr])
1110
+ )
1111
+ ])
1112
+ );
1113
+ }
1114
+ return statements;
1115
+ }
1116
+ function collectAllLocalDefaults(node, out) {
1117
+ if (node.config?.localDefaults && typeof node.config.localDefaults === "object") {
1118
+ const ld = node.config.localDefaults;
1119
+ for (const [key, value] of Object.entries(ld)) {
1120
+ if (!out.has(key)) out.set(key, value);
1121
+ }
1122
+ }
1123
+ if (node.children) {
1124
+ for (const child of node.children) {
1125
+ collectAllLocalDefaults(child, out);
1126
+ }
1127
+ }
1128
+ }
1129
+ function normalizeFrom(from) {
1130
+ if (Array.isArray(from)) return from;
1131
+ if (typeof from === "string") return [from];
1132
+ return [];
1133
+ }
1134
+ function correctFromFields(transitions) {
1135
+ if (transitions.length <= 1) return transitions;
1136
+ const fromValues = /* @__PURE__ */ new Set();
1137
+ for (const t5 of transitions) {
1138
+ const arr = normalizeFrom(t5.from);
1139
+ for (const f of arr) fromValues.add(f);
1140
+ }
1141
+ if (fromValues.size !== 1) return transitions;
1142
+ const stateNames = /* @__PURE__ */ new Set([...fromValues]);
1143
+ for (const t5 of transitions) {
1144
+ stateNames.add(t5.to);
1145
+ }
1146
+ const allNamesAreStates = transitions.every((t5) => stateNames.has(t5.name));
1147
+ if (!allNamesAreStates) return transitions;
1148
+ return transitions.map((t5) => ({
1149
+ ...t5,
1150
+ from: [t5.name]
1151
+ }));
1152
+ }
1153
+ function addImport2(tracker, source, name) {
1154
+ if (!tracker.has(source)) tracker.set(source, /* @__PURE__ */ new Set());
1155
+ tracker.get(source).add(name);
1156
+ }
1157
+
1158
+ // src/decompiler/project.ts
1159
+ function pascalCase(slug) {
1160
+ return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
1161
+ }
1162
+ function camelCase(str) {
1163
+ return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
1164
+ }
1165
+ function fieldTypeToTS(fieldType, field) {
1166
+ const options = getFieldOptions(field);
1167
+ if (options && options.length > 0 && (fieldType === "select" || fieldType === "text")) {
1168
+ return options.map((o) => `'${o.replace(/'/g, "\\'")}'`).join(" | ");
1169
+ }
1170
+ switch (fieldType) {
1171
+ case "text":
1172
+ case "rich_text":
1173
+ case "email":
1174
+ case "url":
1175
+ case "phone":
1176
+ case "color":
1177
+ case "select":
1178
+ return "string";
1179
+ case "number":
1180
+ case "currency":
1181
+ case "percentage":
1182
+ case "rating":
1183
+ case "duration":
1184
+ case "auto_number":
1185
+ return "number";
1186
+ case "boolean":
1187
+ return "boolean";
1188
+ case "date":
1189
+ case "datetime":
1190
+ case "created_at":
1191
+ case "updated_at":
1192
+ return "Date";
1193
+ case "multi_select":
1194
+ return "string[]";
1195
+ case "json":
1196
+ return "Record<string, unknown>";
1197
+ case "file":
1198
+ case "image":
1199
+ return "string";
1200
+ // URL
1201
+ case "relation":
1202
+ case "lookup":
1203
+ return "string";
1204
+ // ID reference
1205
+ default:
1206
+ return "unknown";
1207
+ }
1208
+ }
1209
+ function getFieldOptions(field) {
1210
+ if (!field) return null;
1211
+ const f = field;
1212
+ const validation = f.validation;
1213
+ if (validation?.options && Array.isArray(validation.options)) return validation.options;
1214
+ const meta = f.metadata;
1215
+ if (meta?.options && Array.isArray(meta.options)) return meta.options;
1216
+ if (f.options && Array.isArray(f.options)) return f.options;
1217
+ return null;
1218
+ }
1219
+ function generateModelFile(slug, fields) {
1220
+ const typeName = pascalCase(slug);
1221
+ const lines = [
1222
+ `/**`,
1223
+ ` * ${typeName} \u2014 data model interface.`,
1224
+ ` *`,
1225
+ ` * Auto-generated from workflow definition "${slug}".`,
1226
+ ` * Fields: ${fields.length}`,
1227
+ ` */`,
1228
+ ``,
1229
+ `export interface ${typeName} {`
1230
+ ];
1231
+ for (const field of fields) {
1232
+ const tsType = fieldTypeToTS(field.type, field);
1233
+ const optional = field.required ? "" : "?";
1234
+ const comment = field.label ? ` /** ${field.label} */` : "";
1235
+ if (comment) lines.push(` ${comment}`);
1236
+ lines.push(` ${camelCase(field.name)}${optional}: ${tsType};`);
1237
+ }
1238
+ lines.push(`}`);
1239
+ lines.push(``);
1240
+ return lines.join("\n");
1241
+ }
1242
+ function generateStateEnum(slug, states) {
1243
+ if (states.length === 0) return "";
1244
+ const enumName = `${pascalCase(slug)}State`;
1245
+ const lines = [
1246
+ ``,
1247
+ `export enum ${enumName} {`
1248
+ ];
1249
+ for (const state of states) {
1250
+ const enumKey = pascalCase(state.name);
1251
+ lines.push(` ${enumKey} = '${state.name}',`);
1252
+ }
1253
+ lines.push(`}`);
1254
+ lines.push(``);
1255
+ return lines.join("\n");
1256
+ }
1257
+ function extractServerActions(states, transitions) {
1258
+ const seen = /* @__PURE__ */ new Set();
1259
+ const actions = [];
1260
+ const SERVER_ACTION_TYPES2 = /* @__PURE__ */ new Set([
1261
+ "http_request",
1262
+ "webhook",
1263
+ "call_webhook",
1264
+ "notify",
1265
+ "send_notification",
1266
+ "call_workflow",
1267
+ "spawn_instance",
1268
+ "spawn_subworkflow",
1269
+ "emit_event"
1270
+ ]);
1271
+ function collectFromActions(defs) {
1272
+ for (const action of defs) {
1273
+ if (SERVER_ACTION_TYPES2.has(action.type) && !seen.has(action.id)) {
1274
+ seen.add(action.id);
1275
+ const name = actionToFunctionName(action);
1276
+ actions.push({ name, type: action.type, config: action.config });
1277
+ }
1278
+ }
1279
+ }
1280
+ for (const state of states) {
1281
+ collectFromActions(state.on_enter);
1282
+ collectFromActions(state.on_exit);
1283
+ for (const during of state.during) {
1284
+ collectFromActions(during.actions);
1285
+ }
1286
+ }
1287
+ for (const trans of transitions) {
1288
+ collectFromActions(trans.actions);
1289
+ }
1290
+ return actions;
1291
+ }
1292
+ function actionToFunctionName(action) {
1293
+ const slug = action.config.name ?? action.config.event ?? action.config.slug ?? action.type;
1294
+ return camelCase(String(slug).replace(/[^a-zA-Z0-9_-]/g, "-"));
1295
+ }
1296
+ function generateServerActionFile(actions) {
1297
+ const lines = [
1298
+ `/**`,
1299
+ ` * Server actions \u2014 backend handlers for workflow transitions.`,
1300
+ ` *`,
1301
+ ` * These functions run server-side during state transitions.`,
1302
+ ` * Each receives a TransitionContext with instance data and utilities.`,
1303
+ ` */`,
1304
+ ``,
1305
+ `import type { TransitionContext } from '@mmapp/react';`,
1306
+ ``
1307
+ ];
1308
+ for (const action of actions) {
1309
+ const comment = actionComment(action.type, action.config);
1310
+ lines.push(`/** ${comment} */`);
1311
+ lines.push(`export async function ${action.name}(ctx: TransitionContext): Promise<void> {`);
1312
+ lines.push(` const { instance, env } = ctx;`);
1313
+ switch (action.type) {
1314
+ case "http_request":
1315
+ case "webhook":
1316
+ case "call_webhook": {
1317
+ const url = String(action.config.url ?? "https://api.example.com/webhook");
1318
+ const method = String(action.config.method ?? "POST");
1319
+ lines.push(` await fetch('${url}', {`);
1320
+ lines.push(` method: '${method}',`);
1321
+ lines.push(` headers: { 'Content-Type': 'application/json' },`);
1322
+ lines.push(` body: JSON.stringify({ instanceId: instance.id }),`);
1323
+ lines.push(` });`);
1324
+ break;
1325
+ }
1326
+ case "notify":
1327
+ case "send_notification": {
1328
+ const msg = String(action.config.message ?? action.config.body ?? "Notification");
1329
+ lines.push(` // Send notification: "${msg}"`);
1330
+ lines.push(` await env.notify({ message: '${msg}', instanceId: instance.id });`);
1331
+ break;
1332
+ }
1333
+ case "call_workflow":
1334
+ case "spawn_instance":
1335
+ case "spawn_subworkflow": {
1336
+ const slug = String(action.config.slug ?? action.config.workflow ?? "child-workflow");
1337
+ lines.push(` await env.spawn('${slug}', { parentId: instance.id });`);
1338
+ break;
1339
+ }
1340
+ case "emit_event": {
1341
+ const event = String(action.config.event ?? action.config.name ?? "custom-event");
1342
+ lines.push(` await env.emit('${event}', { instanceId: instance.id });`);
1343
+ break;
1344
+ }
1345
+ default:
1346
+ lines.push(` // TODO: Implement ${action.type} logic`);
1347
+ }
1348
+ lines.push(`}`);
1349
+ lines.push(``);
1350
+ }
1351
+ return lines.join("\n");
1352
+ }
1353
+ function actionComment(type, config) {
1354
+ switch (type) {
1355
+ case "http_request":
1356
+ case "webhook":
1357
+ case "call_webhook":
1358
+ return `HTTP ${config.method ?? "POST"} to ${config.url ?? "webhook endpoint"}`;
1359
+ case "notify":
1360
+ case "send_notification":
1361
+ return `Send notification: ${config.message ?? config.body ?? ""}`;
1362
+ case "call_workflow":
1363
+ case "spawn_instance":
1364
+ case "spawn_subworkflow":
1365
+ return `Spawn child workflow: ${config.slug ?? config.workflow ?? ""}`;
1366
+ case "emit_event":
1367
+ return `Emit event: ${config.event ?? config.name ?? ""}`;
1368
+ default:
1369
+ return `Server action: ${type}`;
1370
+ }
1371
+ }
1372
+ function generateConfigFile(def) {
1373
+ const lines = [
1374
+ `/**`,
1375
+ ` * Blueprint configuration for "${def.name || pascalCase(def.slug)}".`,
1376
+ ` */`,
1377
+ ``,
1378
+ `import { defineBlueprint } from '@mmapp/react';`,
1379
+ ``,
1380
+ `export default defineBlueprint({`,
1381
+ ` slug: '${def.slug}',`,
1382
+ ` name: '${(def.name || pascalCase(def.slug)).replace(/'/g, "\\'")}',`,
1383
+ ` version: '${def.version}',`,
1384
+ ` category: '${def.category}',`
1385
+ ];
1386
+ if (def.description) {
1387
+ lines.push(` description: '${def.description.replace(/'/g, "\\'")}',`);
1388
+ }
1389
+ if (def.roles && def.roles.length > 0) {
1390
+ lines.push(` roles: [`);
1391
+ for (const role of def.roles) {
1392
+ const perms = role.permissions.map((p) => `'${p}'`).join(", ");
1393
+ lines.push(` { name: '${role.name}', permissions: [${perms}] },`);
1394
+ }
1395
+ lines.push(` ],`);
1396
+ }
1397
+ lines.push(`});`);
1398
+ lines.push(``);
1399
+ return lines.join("\n");
1400
+ }
1401
+ function extractPages(experience, slug) {
1402
+ if (!experience || !experience.children) return [];
1403
+ const pages = [];
1404
+ const rootChildren = experience.children;
1405
+ for (const child of rootChildren) {
1406
+ const isRoutePage = child.visible_when && /activeRoute|route|isOn/i.test(child.visible_when);
1407
+ const isPageId = child.id && /page|route|view/i.test(child.id);
1408
+ const isShowRoute = child.component === "Show" && child.config?.when && /activeRoute|route/i.test(String(child.config.when));
1409
+ if (isRoutePage || isPageId || isShowRoute) {
1410
+ const routeName = extractRouteName(child) || child.id || `page-${pages.length}`;
1411
+ const componentName = pascalCase(routeName.replace(/^\//, "").replace(/\//g, "-") || `${slug}-page`);
1412
+ pages.push({
1413
+ route: routeName,
1414
+ componentName,
1415
+ tree: child,
1416
+ filePath: `pages/${componentName}.tsx`
1417
+ });
1418
+ }
1419
+ }
1420
+ return pages;
1421
+ }
1422
+ function extractRouteName(node) {
1423
+ if (node.visible_when) {
1424
+ const match = node.visible_when.match(/['"]\/([^'"]+)['"]/);
1425
+ if (match) return match[1];
1426
+ const boolMatch = node.visible_when.match(/isOn([A-Z]\w+)/);
1427
+ if (boolMatch) return boolMatch[1].toLowerCase();
1428
+ }
1429
+ if (node.config?.when) {
1430
+ const match = String(node.config.when).match(/['"]\/([^'"]+)['"]/);
1431
+ if (match) return match[1];
1432
+ }
1433
+ return "";
1434
+ }
1435
+ function generatePageFile(page, _slug) {
1436
+ const pageInput = {
1437
+ slug: page.route || "page",
1438
+ name: page.componentName,
1439
+ version: "1.0.0",
1440
+ category: "page",
1441
+ states: [],
1442
+ transitions: [],
1443
+ fields: [],
1444
+ roles: [],
1445
+ experience: page.tree
1446
+ };
1447
+ const result = decompile(pageInput, {
1448
+ componentName: page.componentName,
1449
+ includeAnnotation: false
1450
+ });
1451
+ return result.code;
1452
+ }
1453
+ function generateMainFile(def, pages, hasModel, serverActionNames, options) {
1454
+ if (pages.length === 0) {
1455
+ const result = decompile(def, options);
1456
+ return result.code;
1457
+ }
1458
+ const slug = def.slug;
1459
+ const componentName = options?.componentName ?? pascalCase(slug);
1460
+ const importLines = [];
1461
+ const reactImports = /* @__PURE__ */ new Set();
1462
+ const nonComputed = def.fields.filter((f) => !f.computed);
1463
+ if (nonComputed.length > 0) reactImports.add("useState");
1464
+ for (const state of def.states) {
1465
+ if (state.on_enter.length > 0) reactImports.add("useOnEnter");
1466
+ if (state.on_exit.length > 0) reactImports.add("useOnExit");
1467
+ if (state.during.length > 0) reactImports.add("useWhileIn");
1468
+ }
1469
+ if (def.transitions.length > 0) reactImports.add("useTransition");
1470
+ reactImports.add("Stack");
1471
+ if (reactImports.size > 0) {
1472
+ const sorted = [...reactImports].sort();
1473
+ importLines.push(`import { ${sorted.join(", ")} } from '@mmapp/react';`);
1474
+ }
1475
+ if (hasModel) {
1476
+ const typeName = pascalCase(slug);
1477
+ importLines.push(`import type { ${typeName} } from './models/${slug}';`);
1478
+ }
1479
+ if (serverActionNames.length > 0) {
1480
+ const names = serverActionNames.join(", ");
1481
+ importLines.push(`import { ${names} } from './actions/${slug}.server';`);
1482
+ }
1483
+ for (const page of pages) {
1484
+ importLines.push(`import { ${page.componentName} } from './${page.filePath.replace(/\.tsx$/, "")}';`);
1485
+ }
1486
+ const bodyLines = [];
1487
+ for (const field of nonComputed) {
1488
+ const name = camelCase(field.name);
1489
+ const setter = `set${pascalCase(field.name)}`;
1490
+ const defaultVal = fieldDefaultLiteral(field);
1491
+ bodyLines.push(` const [${name}, ${setter}] = useState(${defaultVal});`);
1492
+ }
1493
+ if (nonComputed.length > 0) bodyLines.push(``);
1494
+ for (const state of def.states) {
1495
+ if (state.on_enter.length > 0) {
1496
+ const actions = state.on_enter.map((a) => actionToInlineCode(a)).filter(Boolean);
1497
+ bodyLines.push(` useOnEnter('${state.name}', () => {`);
1498
+ for (const a of actions) bodyLines.push(` ${a}`);
1499
+ bodyLines.push(` });`);
1500
+ }
1501
+ if (state.on_exit.length > 0) {
1502
+ const actions = state.on_exit.map((a) => actionToInlineCode(a)).filter(Boolean);
1503
+ bodyLines.push(` useOnExit('${state.name}', () => {`);
1504
+ for (const a of actions) bodyLines.push(` ${a}`);
1505
+ bodyLines.push(` });`);
1506
+ }
1507
+ }
1508
+ for (const trans of def.transitions) {
1509
+ const varName = camelCase(trans.name);
1510
+ const from = trans.from.length === 1 ? `'${trans.from[0]}'` : `[${trans.from.map((f) => `'${f}'`).join(", ")}]`;
1511
+ bodyLines.push(` const ${varName} = useTransition('${trans.name}', { from: ${from}, to: '${trans.to}' });`);
1512
+ }
1513
+ if (def.transitions.length > 0) bodyLines.push(``);
1514
+ bodyLines.push(` return (`);
1515
+ bodyLines.push(` <Stack sx={{ minHeight: '100vh' }}>`);
1516
+ for (const page of pages) {
1517
+ bodyLines.push(` <${page.componentName} />`);
1518
+ }
1519
+ bodyLines.push(` </Stack>`);
1520
+ bodyLines.push(` );`);
1521
+ const lines = [
1522
+ ...importLines,
1523
+ ``,
1524
+ `/**`,
1525
+ ` * @workflow slug="${slug}" version="${def.version}" category="${def.category}"`,
1526
+ ...def.description ? [` * @description ${def.description}`] : [],
1527
+ ` */`,
1528
+ `export default function ${componentName}() {`,
1529
+ ...bodyLines,
1530
+ `}`,
1531
+ ``
1532
+ ];
1533
+ return lines.join("\n");
1534
+ }
1535
+ function fieldDefaultLiteral(field) {
1536
+ if (field.default_value !== void 0) {
1537
+ if (typeof field.default_value === "string") return `'${field.default_value}'`;
1538
+ return JSON.stringify(field.default_value);
1539
+ }
1540
+ switch (field.type) {
1541
+ case "text":
1542
+ case "rich_text":
1543
+ case "email":
1544
+ case "url":
1545
+ case "phone":
1546
+ case "color":
1547
+ case "select":
1548
+ return "''";
1549
+ case "number":
1550
+ case "currency":
1551
+ case "percentage":
1552
+ case "rating":
1553
+ case "duration":
1554
+ case "auto_number":
1555
+ return "0";
1556
+ case "boolean":
1557
+ return "false";
1558
+ case "date":
1559
+ case "datetime":
1560
+ return "null";
1561
+ case "multi_select":
1562
+ case "json":
1563
+ return "[]";
1564
+ default:
1565
+ return "null";
1566
+ }
1567
+ }
1568
+ function actionToInlineCode(action) {
1569
+ switch (action.type) {
1570
+ case "set_field": {
1571
+ const field = String(action.config.field ?? "");
1572
+ const setter = `set${pascalCase(field)}`;
1573
+ const val = action.config.expression ? String(action.config.expression) : JSON.stringify(action.config.value ?? null);
1574
+ return `${setter}(${val});`;
1575
+ }
1576
+ case "log_event":
1577
+ case "log":
1578
+ return `console.log('${action.config.message ?? action.config.event ?? action.type}');`;
1579
+ case "noop":
1580
+ return "";
1581
+ default:
1582
+ return `// ${action.type}: ${JSON.stringify(action.config)}`;
1583
+ }
1584
+ }
1585
+ function decompileProject(definition, options) {
1586
+ const files = [];
1587
+ const slug = definition.slug;
1588
+ const pages = options?.skipPages ? [] : extractPages(definition.experience, slug);
1589
+ const serverActions = options?.skipActions ? [] : extractServerActions(definition.states, definition.transitions);
1590
+ const hasFields = definition.fields.length > 0;
1591
+ if (hasFields && !options?.skipModels) {
1592
+ let modelContent = generateModelFile(slug, definition.fields);
1593
+ if (definition.states.length > 0) {
1594
+ modelContent += generateStateEnum(slug, definition.states);
1595
+ }
1596
+ files.push({
1597
+ path: `models/${slug}.ts`,
1598
+ role: "model",
1599
+ content: modelContent
1600
+ });
1601
+ }
1602
+ if (serverActions.length > 0) {
1603
+ files.push({
1604
+ path: `actions/${slug}.server.ts`,
1605
+ role: "server-action",
1606
+ content: generateServerActionFile(serverActions)
1607
+ });
1608
+ }
1609
+ for (const page of pages) {
1610
+ files.push({
1611
+ path: page.filePath,
1612
+ role: "page",
1613
+ content: generatePageFile(page, slug)
1614
+ });
1615
+ }
1616
+ if (!options?.skipConfig) {
1617
+ files.push({
1618
+ path: "mm.config.ts",
1619
+ role: "config",
1620
+ content: generateConfigFile(definition)
1621
+ });
1622
+ }
1623
+ if (definition.childDefinitions && definition.childDefinitions.length > 0) {
1624
+ for (const child of definition.childDefinitions) {
1625
+ const childSlug = child.slug;
1626
+ const childFileName = `${childSlug}.workflow.tsx`;
1627
+ const childInput = {
1628
+ ...child,
1629
+ experience: child.views?.default
1630
+ };
1631
+ if (child.category === "data" && child.fields.length > 0 && !options?.skipModels) {
1632
+ let childModelContent = generateModelFile(childSlug, child.fields);
1633
+ if (child.states.length > 0) {
1634
+ childModelContent += generateStateEnum(childSlug, child.states);
1635
+ }
1636
+ files.push({
1637
+ path: `models/${childSlug}.ts`,
1638
+ role: "model",
1639
+ content: childModelContent
1640
+ });
1641
+ }
1642
+ if (!options?.skipActions) {
1643
+ const childServerActions = extractServerActions(child.states, child.transitions);
1644
+ if (childServerActions.length > 0) {
1645
+ files.push({
1646
+ path: `actions/${childSlug}.server.ts`,
1647
+ role: "server-action",
1648
+ content: generateServerActionFile(childServerActions)
1649
+ });
1650
+ }
1651
+ }
1652
+ const childResult = decompile(childInput, {
1653
+ componentName: pascalCase(childSlug),
1654
+ includeAnnotation: true
1655
+ });
1656
+ files.push({
1657
+ path: childFileName,
1658
+ role: "view-entry",
1659
+ content: childResult.code
1660
+ });
1661
+ }
1662
+ }
1663
+ const mainContent = generateMainFile(
1664
+ definition,
1665
+ pages,
1666
+ hasFields && !options?.skipModels,
1667
+ serverActions.map((a) => a.name),
1668
+ options
1669
+ );
1670
+ files.push({
1671
+ path: "main.workflow.tsx",
1672
+ role: "view-entry",
1673
+ content: mainContent
1674
+ });
1675
+ return {
1676
+ files,
1677
+ entryFile: "main.workflow.tsx",
1678
+ slug
1679
+ };
1680
+ }
1681
+ function shouldDecompileAsProject(def) {
1682
+ const meta = def.metadata;
1683
+ const childSlugs = meta?.childSlugs;
1684
+ const hasChildren = Array.isArray(childSlugs) && childSlugs.length > 0;
1685
+ return hasChildren || def.states.length > 1 && def.transitions.length > 0 && (def.fields.length > 0 || def.category === "blueprint");
1686
+ }
1687
+
1688
+ // src/decompiler/split-strategy.ts
1689
+ var SMALL_FIELD_LIMIT = 5;
1690
+ var SMALL_STATE_LIMIT = 1;
1691
+ var MEDIUM_FIELD_LIMIT = 15;
1692
+ var MEDIUM_STATE_LIMIT = 5;
1693
+ function determineSplitStrategy(def) {
1694
+ const fieldCount = def.fields.length;
1695
+ const stateCount = def.states.length;
1696
+ const hasExperience = !!def.experience || !!def.views?.default;
1697
+ const hasChildren = Array.isArray(def.metadata?.childSlugs) && def.metadata.childSlugs.length > 0;
1698
+ if (def.category === "blueprint" || hasChildren) {
1699
+ return {
1700
+ tier: "large",
1701
+ emitModels: fieldCount > 0,
1702
+ emitPages: hasExperience,
1703
+ emitActions: true,
1704
+ emitConfig: true,
1705
+ reason: "blueprint or has children \u2014 always full project"
1706
+ };
1707
+ }
1708
+ if (fieldCount > MEDIUM_FIELD_LIMIT || stateCount > MEDIUM_STATE_LIMIT) {
1709
+ return {
1710
+ tier: "large",
1711
+ emitModels: true,
1712
+ emitPages: hasExperience,
1713
+ emitActions: true,
1714
+ emitConfig: true,
1715
+ reason: "large definition (>15 fields or >5 states)"
1716
+ };
1717
+ }
1718
+ if (fieldCount > SMALL_FIELD_LIMIT || stateCount > SMALL_STATE_LIMIT) {
1719
+ const hasRoutes = hasExperience && hasRouteStructure(def);
1720
+ return {
1721
+ tier: "medium",
1722
+ emitModels: fieldCount > 0,
1723
+ emitPages: hasRoutes,
1724
+ emitActions: false,
1725
+ emitConfig: true,
1726
+ reason: "medium definition (>5 fields or >1 state)"
1727
+ };
1728
+ }
1729
+ return {
1730
+ tier: "single",
1731
+ emitModels: false,
1732
+ emitPages: false,
1733
+ emitActions: false,
1734
+ emitConfig: false,
1735
+ reason: "small definition \u2014 single file sufficient"
1736
+ };
1737
+ }
1738
+ function hasRouteStructure(def) {
1739
+ const exp = def.experience;
1740
+ if (!exp) return false;
1741
+ function walk(node) {
1742
+ const comp = node.component || node.layout || "";
1743
+ if (comp === "Router" || comp === "Route") return true;
1744
+ if (node.children) {
1745
+ for (const c of node.children) {
1746
+ if (walk(c)) return true;
1747
+ }
1748
+ }
1749
+ return false;
1750
+ }
1751
+ return walk(exp);
1752
+ }
1753
+
1754
+ // src/decompiler/config-generator.ts
1755
+ function extractConfigData(def, modelPaths, entryPaths, actionPaths) {
1756
+ const meta = def.metadata;
1757
+ return {
1758
+ slug: def.slug,
1759
+ name: def.name || pascalCase2(def.slug),
1760
+ version: def.version,
1761
+ category: def.category,
1762
+ description: def.description || void 0,
1763
+ mode: meta?.mode || void 0,
1764
+ defaultRuntime: meta?.defaultRuntime || void 0,
1765
+ models: modelPaths,
1766
+ entries: entryPaths,
1767
+ actions: actionPaths,
1768
+ roles: (def.roles || []).map((r) => r.name)
1769
+ };
1770
+ }
1771
+ function generateMmConfig(config) {
1772
+ const isBlueprint = config.category === "blueprint";
1773
+ const fnName = isBlueprint ? "defineBlueprint" : "defineWorkspace";
1774
+ const label = isBlueprint ? "Blueprint" : "Workspace";
1775
+ const lines = [
1776
+ `/**`,
1777
+ ` * MindMatrix ${label.toLowerCase()} configuration.`,
1778
+ ` *`,
1779
+ ` * Auto-generated from workflow definition "${config.slug}".`,
1780
+ ` */`,
1781
+ ``,
1782
+ `import { ${fnName} } from '@mmapp/react';`,
1783
+ ``,
1784
+ `export default ${fnName}({`,
1785
+ ` slug: '${esc(config.slug)}',`,
1786
+ ` name: '${esc(config.name)}',`,
1787
+ ` version: '${esc(config.version)}',`,
1788
+ ` category: ${Array.isArray(config.category) ? `[${config.category.map((c) => `'${esc(c)}'`).join(", ")}]` : `'${esc(config.category)}'`},`
1789
+ ];
1790
+ if (config.description) {
1791
+ lines.push(` description: '${esc(config.description)}',`);
1792
+ }
1793
+ if (isBlueprint) {
1794
+ if (config.mode) {
1795
+ lines.push(` mode: '${esc(config.mode)}',`);
1796
+ }
1797
+ if (config.defaultRuntime) {
1798
+ lines.push(` defaultRuntime: '${esc(config.defaultRuntime)}',`);
1799
+ }
1800
+ }
1801
+ if (config.models.length > 0) {
1802
+ lines.push(` models: [`);
1803
+ for (const m of config.models) {
1804
+ lines.push(` '${esc(m)}',`);
1805
+ }
1806
+ lines.push(` ],`);
1807
+ }
1808
+ if (config.entries.length > 0) {
1809
+ lines.push(` entries: [`);
1810
+ for (const e of config.entries) {
1811
+ lines.push(` '${esc(e)}',`);
1812
+ }
1813
+ lines.push(` ],`);
1814
+ }
1815
+ if (config.actions.length > 0) {
1816
+ lines.push(` actions: [`);
1817
+ for (const a of config.actions) {
1818
+ lines.push(` '${esc(a)}',`);
1819
+ }
1820
+ lines.push(` ],`);
1821
+ }
1822
+ if (config.roles.length > 0) {
1823
+ lines.push(` roles: [${config.roles.map((r) => `'${esc(r)}'`).join(", ")}],`);
1824
+ }
1825
+ lines.push(`});`);
1826
+ lines.push(``);
1827
+ return lines.join("\n");
1828
+ }
1829
+ function esc(s) {
1830
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1831
+ }
1832
+ function pascalCase2(slug) {
1833
+ return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
1834
+ }
1835
+
1836
+ // src/decompiler/index.ts
1837
+ function pascalCase3(slug) {
1838
+ return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
1839
+ }
1840
+ function emitImportStatements(tracker) {
1841
+ const result = [];
1842
+ const sources = [...tracker.keys()].sort();
1843
+ for (const source of sources) {
1844
+ const names = [...tracker.get(source)].sort();
1845
+ const specifiers = [];
1846
+ for (const name of names) {
1847
+ if (name.startsWith("default as ")) {
1848
+ const localName = name.slice("default as ".length);
1849
+ specifiers.push(t4.importDefaultSpecifier(t4.identifier(localName)));
1850
+ } else {
1851
+ specifiers.push(t4.importSpecifier(t4.identifier(name), t4.identifier(name)));
1852
+ }
1853
+ }
1854
+ result.push(t4.importDeclaration(specifiers, t4.stringLiteral(source)));
1855
+ }
1856
+ return result;
1857
+ }
1858
+ function buildWorkflowAnnotation(def) {
1859
+ const parts = [
1860
+ `@workflow slug="${def.slug}" version="${def.version}" category="${def.category}"`
1861
+ ];
1862
+ if (def.description) {
1863
+ parts.push(`@description ${def.description}`);
1864
+ }
1865
+ return `*
1866
+ * ${parts.join("\n * ")}
1867
+ `;
1868
+ }
1869
+ function decompile(definition, options) {
1870
+ const imports = /* @__PURE__ */ new Map();
1871
+ const includeAnnotation = options?.includeAnnotation ?? true;
1872
+ const componentName = options?.componentName ?? definition.metadata?.displayName ?? pascalCase3(definition.slug);
1873
+ const symbols = buildSymbolTable(
1874
+ definition.fields,
1875
+ definition.metadata,
1876
+ definition.transitions,
1877
+ definition.experience
1878
+ );
1879
+ const componentBody = [];
1880
+ const sortedFields = [...definition.fields].sort((a, b) => a.name.localeCompare(b.name));
1881
+ const fieldStmts = emitFieldDeclarations(sortedFields, imports);
1882
+ componentBody.push(...fieldStmts);
1883
+ if (definition.experience) {
1884
+ const localDefaultStmts = emitLocalDefaultDeclarations(
1885
+ definition.experience,
1886
+ definition.fields,
1887
+ imports
1888
+ );
1889
+ componentBody.push(...localDefaultStmts);
1890
+ }
1891
+ const queryStmts = emitQueryDeclarations(definition.metadata, imports);
1892
+ componentBody.push(...queryStmts);
1893
+ const roleStmts = emitRoleDeclarations(definition.metadata, imports);
1894
+ componentBody.push(...roleStmts);
1895
+ const sortedStates = [...definition.states].sort((a, b) => a.name.localeCompare(b.name));
1896
+ const effectStmts = emitStateEffects(sortedStates, imports);
1897
+ componentBody.push(...effectStmts);
1898
+ const watcherStmts = emitChangeWatchers(definition.metadata, imports);
1899
+ componentBody.push(...watcherStmts);
1900
+ const eventStmts = emitEventSubscriptions(definition.on_event, imports);
1901
+ componentBody.push(...eventStmts);
1902
+ const sortedTransitions = [...definition.transitions].sort((a, b) => a.name.localeCompare(b.name));
1903
+ const transStmts = emitTransitionDeclarations(sortedTransitions, imports);
1904
+ componentBody.push(...transStmts);
1905
+ const effectHookStmts = emitTransitionEffects(definition.metadata, imports);
1906
+ componentBody.push(...effectHookStmts);
1907
+ if (definition.experience) {
1908
+ const jsxTree = buildJSXTree(definition.experience, imports, symbols);
1909
+ componentBody.push(t4.returnStatement(jsxTree));
1910
+ } else {
1911
+ componentBody.push(t4.returnStatement(t4.nullLiteral()));
1912
+ }
1913
+ const func = t4.functionDeclaration(
1914
+ t4.identifier(componentName),
1915
+ [],
1916
+ t4.blockStatement(componentBody)
1917
+ );
1918
+ const programBody = [];
1919
+ programBody.push(...emitImportStatements(imports));
1920
+ const exportDecl = t4.exportDefaultDeclaration(func);
1921
+ if (includeAnnotation) {
1922
+ t4.addComment(exportDecl, "leading", buildWorkflowAnnotation(definition), false);
1923
+ }
1924
+ programBody.push(exportDecl);
1925
+ const program2 = t4.program(programBody);
1926
+ const file2 = t4.file(program2);
1927
+ const { code } = generate(file2, {
1928
+ retainLines: false,
1929
+ compact: false,
1930
+ jsescOption: { minimal: true }
1931
+ });
1932
+ return {
1933
+ code,
1934
+ imports: [...imports.keys()],
1935
+ componentName
1936
+ };
1937
+ }
1938
+
1939
+ // src/decompiler/project-decompiler.ts
1940
+ function pascalCase4(slug) {
1941
+ return slug.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
1942
+ }
1943
+ function camelCase2(str) {
1944
+ return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
1945
+ }
1946
+ function correctTransitionFromFields(transitions) {
1947
+ if (transitions.length <= 1) return transitions;
1948
+ const fromValues = /* @__PURE__ */ new Set();
1949
+ for (const t5 of transitions) {
1950
+ const arr = Array.isArray(t5.from) ? t5.from : [t5.from];
1951
+ for (const f of arr) fromValues.add(f);
1952
+ }
1953
+ if (fromValues.size !== 1) return transitions;
1954
+ const stateNames = /* @__PURE__ */ new Set([...fromValues]);
1955
+ for (const t5 of transitions) {
1956
+ stateNames.add(t5.to);
1957
+ }
1958
+ const allNamesAreStates = transitions.every((t5) => stateNames.has(t5.name));
1959
+ if (!allNamesAreStates) return transitions;
1960
+ return transitions.map((t5) => ({ ...t5, from: [t5.name] }));
1961
+ }
1962
+ function fieldTypeToTS2(fieldType, field) {
1963
+ const options = getFieldOptions2(field);
1964
+ if (options && options.length > 0 && (fieldType === "select" || fieldType === "text")) {
1965
+ return options.map((o) => `'${esc2(String(o))}'`).join(" | ");
1966
+ }
1967
+ switch (fieldType) {
1968
+ case "text":
1969
+ case "rich_text":
1970
+ case "email":
1971
+ case "url":
1972
+ case "phone":
1973
+ case "color":
1974
+ case "select":
1975
+ return "string";
1976
+ case "number":
1977
+ case "currency":
1978
+ case "percentage":
1979
+ case "rating":
1980
+ case "duration":
1981
+ case "auto_number":
1982
+ return "number";
1983
+ case "boolean":
1984
+ return "boolean";
1985
+ case "date":
1986
+ case "datetime":
1987
+ case "created_at":
1988
+ case "updated_at":
1989
+ return "Date";
1990
+ case "multi_select":
1991
+ return "string[]";
1992
+ case "json":
1993
+ case "object":
1994
+ return "Record<string, unknown>";
1995
+ case "array":
1996
+ return "string[]";
1997
+ case "file":
1998
+ case "image":
1999
+ return "string";
2000
+ case "relation":
2001
+ case "lookup":
2002
+ return "string";
2003
+ default:
2004
+ return "unknown";
2005
+ }
2006
+ }
2007
+ function getFieldOptions2(field) {
2008
+ if (!field) return null;
2009
+ const f = field;
2010
+ const validation = f.validation;
2011
+ if (validation?.options && Array.isArray(validation.options)) {
2012
+ return validation.options;
2013
+ }
2014
+ const meta = f.metadata;
2015
+ if (meta?.options && Array.isArray(meta.options)) {
2016
+ return meta.options;
2017
+ }
2018
+ if (f.options && Array.isArray(f.options)) {
2019
+ return f.options;
2020
+ }
2021
+ return null;
2022
+ }
2023
+ function safeKey(name) {
2024
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `'${esc2(name)}'`;
2025
+ }
2026
+ function fieldTypeToModelType(fieldType) {
2027
+ switch (fieldType) {
2028
+ case "text":
2029
+ case "rich_text":
2030
+ case "select":
2031
+ return "string";
2032
+ case "email":
2033
+ return "email";
2034
+ case "url":
2035
+ return "url";
2036
+ case "phone":
2037
+ return "phone";
2038
+ case "color":
2039
+ return "color";
2040
+ case "number":
2041
+ case "currency":
2042
+ case "percentage":
2043
+ case "rating":
2044
+ case "duration":
2045
+ case "auto_number":
2046
+ return "number";
2047
+ case "boolean":
2048
+ return "boolean";
2049
+ case "date":
2050
+ case "datetime":
2051
+ case "created_at":
2052
+ case "updated_at":
2053
+ return "date";
2054
+ case "multi_select":
2055
+ return "array";
2056
+ case "json":
2057
+ case "object":
2058
+ return "json";
2059
+ case "array":
2060
+ return "array";
2061
+ case "file":
2062
+ case "image":
2063
+ return "file";
2064
+ case "relation":
2065
+ case "lookup":
2066
+ return "relation";
2067
+ default:
2068
+ return fieldType;
2069
+ }
2070
+ }
2071
+ function serializeDefault(value) {
2072
+ if (value === void 0 || value === null) return "null";
2073
+ if (typeof value === "string") return `'${esc2(value)}'`;
2074
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2075
+ if (Array.isArray(value)) {
2076
+ if (value.length === 0) return "[]";
2077
+ return `[${value.map((v) => serializeDefault(v)).join(", ")}]`;
2078
+ }
2079
+ return JSON.stringify(value);
2080
+ }
2081
+ function serializeValue(value, indent) {
2082
+ const pad = " ".repeat(indent);
2083
+ if (value === void 0 || value === null) return "null";
2084
+ if (typeof value === "string") return `'${esc2(value)}'`;
2085
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2086
+ if (Array.isArray(value)) {
2087
+ if (value.length === 0) return "[]";
2088
+ if (value.every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean")) {
2089
+ return `[${value.map((v) => serializeValue(v, 0)).join(", ")}]`;
2090
+ }
2091
+ const items = value.map((v) => `${pad} ${serializeValue(v, indent + 2)},`);
2092
+ return `[
2093
+ ${items.join("\n")}
2094
+ ${pad}]`;
2095
+ }
2096
+ if (typeof value === "object") {
2097
+ return serializeObject(value, indent);
2098
+ }
2099
+ return JSON.stringify(value);
2100
+ }
2101
+ function serializeObject(obj, indent) {
2102
+ const pad = " ".repeat(indent);
2103
+ const entries = Object.entries(obj).filter(([, v]) => v !== void 0);
2104
+ if (entries.length === 0) return "{}";
2105
+ const simple = entries.every(([, v]) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null);
2106
+ if (simple && entries.length <= 3) {
2107
+ const parts = entries.map(([k, v]) => `${safeKey(k)}: ${serializeValue(v, 0)}`);
2108
+ return `{ ${parts.join(", ")} }`;
2109
+ }
2110
+ const lines = entries.map(([k, v]) => `${pad} ${safeKey(k)}: ${serializeValue(v, indent + 2)},`);
2111
+ return `{
2112
+ ${lines.join("\n")}
2113
+ ${pad}}`;
2114
+ }
2115
+ function serializeActions(actions, indent) {
2116
+ const pad = " ".repeat(indent);
2117
+ if (!actions || actions.length === 0) return [];
2118
+ const lines = [];
2119
+ lines.push(`${pad}[`);
2120
+ for (const action of actions) {
2121
+ const inner = indent + 2;
2122
+ const ip = " ".repeat(inner);
2123
+ lines.push(`${ip}{`);
2124
+ if (action.id) lines.push(`${ip} id: '${esc2(String(action.id))}',`);
2125
+ if (action.type) lines.push(`${ip} type: '${esc2(String(action.type))}',`);
2126
+ if (action.mode) lines.push(`${ip} mode: '${esc2(String(action.mode))}',`);
2127
+ if (action.condition) lines.push(`${ip} condition: '${esc2(String(action.condition))}',`);
2128
+ if (action.config && typeof action.config === "object" && Object.keys(action.config).length > 0) {
2129
+ lines.push(`${ip} config: ${serializeValue(action.config, inner + 2)},`);
2130
+ }
2131
+ lines.push(`${ip}},`);
2132
+ }
2133
+ lines.push(`${pad}]`);
2134
+ return lines;
2135
+ }
2136
+ function serializeConditions(conditions, indent) {
2137
+ const pad = " ".repeat(indent);
2138
+ if (!conditions || conditions.length === 0) return [];
2139
+ const lines = [];
2140
+ lines.push(`${pad}[`);
2141
+ for (const cond of conditions) {
2142
+ lines.push(`${" ".repeat(indent + 2)}${serializeObject(cond, indent + 2)},`);
2143
+ }
2144
+ lines.push(`${pad}]`);
2145
+ return lines;
2146
+ }
2147
+ function serializeDuring(during, indent) {
2148
+ const pad = " ".repeat(indent);
2149
+ if (!during || during.length === 0) return [];
2150
+ const lines = [];
2151
+ lines.push(`${pad}[`);
2152
+ for (const d of during) {
2153
+ const inner = indent + 2;
2154
+ const ip = " ".repeat(inner);
2155
+ lines.push(`${ip}{`);
2156
+ if (d.id) lines.push(`${ip} id: '${esc2(String(d.id))}',`);
2157
+ if (d.type) lines.push(`${ip} type: '${esc2(String(d.type))}',`);
2158
+ if (d.schedule && typeof d.schedule === "object") {
2159
+ lines.push(`${ip} schedule: ${serializeValue(d.schedule, inner + 2)},`);
2160
+ }
2161
+ if (d.interval_ms != null) lines.push(`${ip} interval_ms: ${d.interval_ms},`);
2162
+ if (d.cron) lines.push(`${ip} cron: '${esc2(String(d.cron))}',`);
2163
+ if (d.delay_ms != null) lines.push(`${ip} delay_ms: ${d.delay_ms},`);
2164
+ if (d.condition) lines.push(`${ip} condition: '${esc2(String(d.condition))}',`);
2165
+ if (d.actions && Array.isArray(d.actions) && d.actions.length > 0) {
2166
+ const actLines = serializeActions(d.actions, inner + 2);
2167
+ lines.push(`${ip} actions: ${actLines[0]?.trim()}`);
2168
+ for (let i = 1; i < actLines.length; i++) lines.push(actLines[i]);
2169
+ const lastIdx = lines.length - 1;
2170
+ if (!lines[lastIdx].trimEnd().endsWith(",")) lines[lastIdx] += ",";
2171
+ }
2172
+ lines.push(`${ip}},`);
2173
+ }
2174
+ lines.push(`${pad}]`);
2175
+ return lines;
2176
+ }
2177
+ function serializeOnEvent(events, indent) {
2178
+ const pad = " ".repeat(indent);
2179
+ if (!events || events.length === 0) return [];
2180
+ const lines = [];
2181
+ lines.push(`${pad}[`);
2182
+ for (const evt of events) {
2183
+ const inner = indent + 2;
2184
+ const ip = " ".repeat(inner);
2185
+ lines.push(`${ip}{`);
2186
+ if (evt.match) lines.push(`${ip} match: '${esc2(String(evt.match))}',`);
2187
+ if (evt.description) lines.push(`${ip} description: '${esc2(String(evt.description))}',`);
2188
+ if (evt.conditions && Array.isArray(evt.conditions) && evt.conditions.length > 0) {
2189
+ const conds = evt.conditions.map((c) => `'${esc2(c)}'`);
2190
+ if (conds.length === 1) {
2191
+ lines.push(`${ip} conditions: [${conds[0]}],`);
2192
+ } else {
2193
+ lines.push(`${ip} conditions: [`);
2194
+ for (const c of conds) lines.push(`${ip} ${c},`);
2195
+ lines.push(`${ip} ],`);
2196
+ }
2197
+ }
2198
+ if (evt.actions && Array.isArray(evt.actions) && evt.actions.length > 0) {
2199
+ const actLines = serializeActions(evt.actions, inner + 2);
2200
+ lines.push(`${ip} actions: ${actLines[0]?.trim()}`);
2201
+ for (let i = 1; i < actLines.length; i++) lines.push(actLines[i]);
2202
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2203
+ }
2204
+ lines.push(`${ip}},`);
2205
+ }
2206
+ lines.push(`${pad}]`);
2207
+ return lines;
2208
+ }
2209
+ function generateModelFile2(slug, fields, states, transitions, meta) {
2210
+ const typeName = pascalCase4(slug);
2211
+ const interfaceName = `${typeName}Fields`;
2212
+ const version = meta?.version || "0.1.0";
2213
+ const category = meta?.category || "data";
2214
+ const lines = [];
2215
+ lines.push(`import { defineModel } from '@mmapp/react';`);
2216
+ lines.push(``);
2217
+ const sortedFields = [...fields].sort((a, b) => a.name.localeCompare(b.name));
2218
+ if (sortedFields.length > 0) {
2219
+ lines.push(`export interface ${interfaceName} {`);
2220
+ for (const field of sortedFields) {
2221
+ const tsType = fieldTypeToTS2(field.type, field);
2222
+ const optional = field.required ? "" : "?";
2223
+ lines.push(` ${camelCase2(field.name)}${optional}: ${tsType};`);
2224
+ }
2225
+ lines.push(`}`);
2226
+ lines.push(``);
2227
+ }
2228
+ lines.push(`export default defineModel({`);
2229
+ lines.push(` slug: '${esc2(slug)}',`);
2230
+ lines.push(` version: '${esc2(version)}',`);
2231
+ lines.push(` category: '${esc2(category)}',`);
2232
+ if (meta?.description) {
2233
+ lines.push(` description: '${esc2(meta.description)}',`);
2234
+ }
2235
+ if (sortedFields.length > 0) {
2236
+ lines.push(` fields: {`);
2237
+ for (const field of sortedFields) {
2238
+ const props = [];
2239
+ props.push(`type: '${fieldTypeToModelType(field.type)}'`);
2240
+ if (field.required) {
2241
+ props.push(`required: true`);
2242
+ }
2243
+ if (field.default_value === null) {
2244
+ props.push(`default: null`);
2245
+ } else if (field.default_value !== void 0 && field.default_value !== "" && field.default_value !== 0 && field.default_value !== false) {
2246
+ props.push(`default: ${serializeDefault(field.default_value)}`);
2247
+ }
2248
+ const options = getFieldOptions2(field);
2249
+ if (options && options.length > 0) {
2250
+ const enumStr = options.map((o) => `'${esc2(String(o))}'`).join(", ");
2251
+ props.push(`enum: [${enumStr}]`);
2252
+ }
2253
+ lines.push(` ${camelCase2(field.name)}: { ${props.join(", ")} },`);
2254
+ }
2255
+ lines.push(` },`);
2256
+ }
2257
+ const validStates = states.filter((s) => s.name !== "");
2258
+ if (validStates.length > 0) {
2259
+ const sortedStates = [...validStates].sort((a, b) => a.name.localeCompare(b.name));
2260
+ lines.push(` states: {`);
2261
+ for (const state of sortedStates) {
2262
+ const hasOnEnter = state.on_enter && state.on_enter.length > 0;
2263
+ const hasOnExit = state.on_exit && state.on_exit.length > 0;
2264
+ const hasDuring = state.during && state.during.length > 0;
2265
+ const hasOnEvent = state.on_event && state.on_event.length > 0;
2266
+ const hasLifecycle = hasOnEnter || hasOnExit || hasDuring || hasOnEvent;
2267
+ if (!hasLifecycle) {
2268
+ const props = [];
2269
+ if (state.type === "START") props.push(`type: 'initial'`);
2270
+ else if (state.type === "END") props.push(`type: 'final'`);
2271
+ if (state.description) props.push(`description: '${esc2(state.description)}'`);
2272
+ const body = props.length > 0 ? ` ${props.join(", ")} ` : "";
2273
+ lines.push(` ${safeKey(state.name)}: {${body}},`);
2274
+ continue;
2275
+ }
2276
+ lines.push(` ${safeKey(state.name)}: {`);
2277
+ if (state.type === "START") lines.push(` type: 'initial',`);
2278
+ else if (state.type === "END") lines.push(` type: 'final',`);
2279
+ if (state.description) lines.push(` description: '${esc2(state.description)}',`);
2280
+ if (hasOnEnter) {
2281
+ const actLines = serializeActions(state.on_enter, 8);
2282
+ lines.push(` on_enter: ${actLines[0]?.trim()}`);
2283
+ for (let i = 1; i < actLines.length; i++) lines.push(actLines[i]);
2284
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2285
+ }
2286
+ if (hasOnExit) {
2287
+ const actLines = serializeActions(state.on_exit, 8);
2288
+ lines.push(` on_exit: ${actLines[0]?.trim()}`);
2289
+ for (let i = 1; i < actLines.length; i++) lines.push(actLines[i]);
2290
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2291
+ }
2292
+ if (hasDuring) {
2293
+ const durLines = serializeDuring(state.during, 8);
2294
+ lines.push(` during: ${durLines[0]?.trim()}`);
2295
+ for (let i = 1; i < durLines.length; i++) lines.push(durLines[i]);
2296
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2297
+ }
2298
+ if (hasOnEvent) {
2299
+ const evtLines = serializeOnEvent(state.on_event, 8);
2300
+ lines.push(` on_event: ${evtLines[0]?.trim()}`);
2301
+ for (let i = 1; i < evtLines.length; i++) lines.push(evtLines[i]);
2302
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2303
+ }
2304
+ lines.push(` },`);
2305
+ }
2306
+ lines.push(` },`);
2307
+ }
2308
+ if (transitions.length > 0) {
2309
+ const correctedTrans = correctTransitionFromFields(transitions);
2310
+ const sortedTrans = [...correctedTrans].sort((a, b) => a.name.localeCompare(b.name));
2311
+ lines.push(` transitions: {`);
2312
+ for (const trans of sortedTrans) {
2313
+ const fromArr = Array.isArray(trans.from) ? trans.from : [trans.from];
2314
+ const hasConditions = trans.conditions && trans.conditions.length > 0;
2315
+ const hasActions = trans.actions && trans.actions.length > 0;
2316
+ const hasRichContent = hasConditions || hasActions;
2317
+ if (!hasRichContent) {
2318
+ const parts = [];
2319
+ if (fromArr.length === 1) parts.push(`from: '${esc2(fromArr[0])}'`);
2320
+ else parts.push(`from: [${fromArr.map((f) => `'${esc2(f)}'`).join(", ")}]`);
2321
+ parts.push(`to: '${esc2(trans.to)}'`);
2322
+ if (trans.roles && trans.roles.length > 0) {
2323
+ parts.push(`roles: [${trans.roles.map((r) => `'${esc2(r)}'`).join(", ")}]`);
2324
+ }
2325
+ if (trans.auto) parts.push(`auto: true`);
2326
+ if (trans.required_fields && trans.required_fields.length > 0) {
2327
+ parts.push(`required_fields: [${trans.required_fields.map((f) => `'${esc2(f)}'`).join(", ")}]`);
2328
+ }
2329
+ lines.push(` ${safeKey(trans.name)}: { ${parts.join(", ")} },`);
2330
+ continue;
2331
+ }
2332
+ lines.push(` ${safeKey(trans.name)}: {`);
2333
+ if (fromArr.length === 1) {
2334
+ lines.push(` from: '${esc2(fromArr[0])}',`);
2335
+ } else {
2336
+ lines.push(` from: [${fromArr.map((f) => `'${esc2(f)}'`).join(", ")}],`);
2337
+ }
2338
+ lines.push(` to: '${esc2(trans.to)}',`);
2339
+ if (trans.roles && trans.roles.length > 0) {
2340
+ lines.push(` roles: [${trans.roles.map((r) => `'${esc2(r)}'`).join(", ")}],`);
2341
+ }
2342
+ if (trans.auto) lines.push(` auto: true,`);
2343
+ if (trans.required_fields && trans.required_fields.length > 0) {
2344
+ lines.push(` required_fields: [${trans.required_fields.map((f) => `'${esc2(f)}'`).join(", ")}],`);
2345
+ }
2346
+ if (hasConditions) {
2347
+ const condLines = serializeConditions(trans.conditions, 8);
2348
+ lines.push(` conditions: ${condLines[0]?.trim()}`);
2349
+ for (let i = 1; i < condLines.length; i++) lines.push(condLines[i]);
2350
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2351
+ }
2352
+ if (hasActions) {
2353
+ const actLines = serializeActions(trans.actions, 8);
2354
+ lines.push(` actions: ${actLines[0]?.trim()}`);
2355
+ for (let i = 1; i < actLines.length; i++) lines.push(actLines[i]);
2356
+ if (!lines[lines.length - 1].trimEnd().endsWith(",")) lines[lines.length - 1] += ",";
2357
+ }
2358
+ lines.push(` },`);
2359
+ }
2360
+ lines.push(` },`);
2361
+ }
2362
+ if (meta && meta.orchestration && typeof meta.orchestration === "object") {
2363
+ const orchLines = serializeValue(meta.orchestration, 4);
2364
+ lines.push(` orchestration: ${orchLines.trim()},`);
2365
+ }
2366
+ lines.push(`});`);
2367
+ lines.push(``);
2368
+ return lines.join("\n");
2369
+ }
2370
+ function generateLayoutFile(slug, roles) {
2371
+ const componentName = `${pascalCase4(slug)}Layout`;
2372
+ const lines = [
2373
+ `/**`,
2374
+ ` * ${componentName} \u2014 root layout with auth guards.`,
2375
+ ` */`,
2376
+ ``,
2377
+ `import { useRole, Stack } from '@mmapp/react';`,
2378
+ ``,
2379
+ `interface LayoutProps {`,
2380
+ ` children: React.ReactNode;`,
2381
+ `}`,
2382
+ ``,
2383
+ `export default function ${componentName}({ children }: LayoutProps) {`
2384
+ ];
2385
+ for (const role of roles) {
2386
+ const varName = `is${pascalCase4(role.name)}`;
2387
+ lines.push(` const ${varName} = useRole('${esc2(role.name)}');`);
2388
+ }
2389
+ if (roles.length > 0) {
2390
+ lines.push(``);
2391
+ const roleChecks = roles.map((r) => `is${pascalCase4(r.name)}`).join(" || ");
2392
+ lines.push(` const isAuthorized = ${roleChecks};`);
2393
+ lines.push(``);
2394
+ lines.push(` if (!isAuthorized) {`);
2395
+ lines.push(` return <Stack sx={{ p: 32, align: 'center' }}>Access denied</Stack>;`);
2396
+ lines.push(` }`);
2397
+ }
2398
+ lines.push(``);
2399
+ lines.push(` return (`);
2400
+ lines.push(` <Stack sx={{ minH: '100vh' }}>`);
2401
+ lines.push(` {children}`);
2402
+ lines.push(` </Stack>`);
2403
+ lines.push(` );`);
2404
+ lines.push(`}`);
2405
+ lines.push(``);
2406
+ return lines.join("\n");
2407
+ }
2408
+ function extractRouteTable(childDefinitions) {
2409
+ if (!childDefinitions) return [];
2410
+ const router = childDefinitions.find(
2411
+ (c) => c.slug.endsWith("-router") || c.category === "router"
2412
+ );
2413
+ if (!router?.states) return [];
2414
+ return router.states.filter((s) => s.description?.startsWith("Route:")).map((s) => {
2415
+ const route = s.description.replace(/^Route:\s*/, "").replace(/^\//, "");
2416
+ const parts = route.split("/");
2417
+ return {
2418
+ stateName: s.name,
2419
+ role: parts[0] || "shared",
2420
+ page: parts.slice(1).join("/") || parts[0],
2421
+ path: route
2422
+ };
2423
+ });
2424
+ }
2425
+ function findPageTitle(node, maxDepth = 5) {
2426
+ const queue = [{ n: node, d: 0 }];
2427
+ while (queue.length > 0) {
2428
+ const { n, d } = queue.shift();
2429
+ if (d > maxDepth) continue;
2430
+ if ((n.component === "Text" || n.component === "Heading") && (n.config?.level === 2 || n.config?.variant === "h2")) {
2431
+ if (n.config?.text) return n.config.text;
2432
+ if (n.children?.length) {
2433
+ const parts = n.children.filter((c) => c.config?.value).map((c) => c.config.value);
2434
+ if (parts.length > 0) return parts.join("");
2435
+ }
2436
+ }
2437
+ if (n.children) {
2438
+ for (const child of n.children) {
2439
+ queue.push({ n: child, d: d + 1 });
2440
+ }
2441
+ }
2442
+ }
2443
+ return null;
2444
+ }
2445
+ function detectPageRole(node) {
2446
+ const when = node.bindings?.when || "";
2447
+ if (/isAdmin|is_admin/i.test(when)) return "admin";
2448
+ if (/isDriver|is_driver/i.test(when)) return "driver";
2449
+ if (/isRider|is_rider/i.test(when)) return "rider";
2450
+ const ld = node.config?.localDefaults || {};
2451
+ const keys = Object.keys(ld);
2452
+ if (keys.some((k) => k.startsWith("driver.") || k === "is_online")) return "driver";
2453
+ if (keys.some((k) => k.startsWith("earnings.") || k.startsWith("matching."))) return "driver";
2454
+ if (keys.some((k) => k.startsWith("rider."))) return "rider";
2455
+ if (keys.includes("ride.current_fare") || keys.includes("time_left")) return "driver";
2456
+ if (keys.includes("selected_vehicle") || keys.includes("driver_location")) return "rider";
2457
+ if (keys.includes("date_from") || keys.includes("date_to")) return "rider";
2458
+ if (keys.includes("cvv") || keys.includes("card_number")) return "rider";
2459
+ const title = findPageTitle(node);
2460
+ if (title) {
2461
+ const lower = title.toLowerCase();
2462
+ if (lower.includes("driver") || lower.includes("earning") || lower.includes("navigation")) return "driver";
2463
+ if (lower.includes("ride request") || lower.includes("ride acceptance")) return "driver";
2464
+ if (lower.includes("payment method") || lower.includes("ride history")) return "rider";
2465
+ if (lower.includes("your ride") || lower.includes("where to") || lower.includes("ride tracking")) return "rider";
2466
+ if (lower.includes("analytics") || lower.includes("surge") || lower.includes("fleet")) return "admin";
2467
+ }
2468
+ return "shared";
2469
+ }
2470
+ function isChildWorkflowView(node, childSlugs) {
2471
+ const title = findPageTitle(node);
2472
+ if (!title) return false;
2473
+ if (title.includes("#")) return true;
2474
+ const titleSlug = title.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "-");
2475
+ return childSlugs.has(titleSlug);
2476
+ }
2477
+ function titleToSlug(title) {
2478
+ const MAP = {
2479
+ "where to?": "home",
2480
+ "where to": "home",
2481
+ "your ride": "ride-tracking",
2482
+ "incoming ride request": "ride-acceptance"
2483
+ };
2484
+ const lower = title.toLowerCase();
2485
+ if (MAP[lower]) return MAP[lower];
2486
+ return lower.replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "-");
2487
+ }
2488
+ function scoreRouteMatch(title, route) {
2489
+ const titleSlug = title.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "-");
2490
+ const titleWords = new Set(titleSlug.split("-").filter((w) => w.length > 2));
2491
+ let score = 0;
2492
+ for (const word of route.page.split("-")) {
2493
+ if (titleWords.has(word)) score += 2;
2494
+ }
2495
+ if (titleSlug.includes(route.page) || route.page.includes(titleSlug)) {
2496
+ score += 3;
2497
+ }
2498
+ return score;
2499
+ }
2500
+ function extractRoleGuard(node) {
2501
+ const when = node.bindings?.when || "";
2502
+ const match = when.match(/\$instance\.(is\w+)/);
2503
+ return match ? match[1] : void 0;
2504
+ }
2505
+ function collectDataSources(node) {
2506
+ const sources = [];
2507
+ const nodeDS = node.dataSources;
2508
+ if (Array.isArray(nodeDS)) sources.push(...nodeDS);
2509
+ if (node.children) {
2510
+ for (const child of node.children) {
2511
+ const childDS = child.dataSources;
2512
+ if (Array.isArray(childDS)) sources.push(...childDS);
2513
+ }
2514
+ }
2515
+ return sources;
2516
+ }
2517
+ function stripLocalDefaults(node) {
2518
+ if (!node.config?.localDefaults) return node;
2519
+ const { localDefaults, ...rest } = node.config;
2520
+ return { ...node, config: Object.keys(rest).length > 0 ? rest : void 0 };
2521
+ }
2522
+ function getPageContentTree(node) {
2523
+ if (node.component === "Show" && node.children?.length === 1) {
2524
+ return stripLocalDefaults(node.children[0]);
2525
+ }
2526
+ return stripLocalDefaults(node);
2527
+ }
2528
+ function inferFieldType(value) {
2529
+ if (typeof value === "boolean") return "boolean";
2530
+ if (typeof value === "number") return "number";
2531
+ return "text";
2532
+ }
2533
+ function extractPageSections(experience, childDefinitions) {
2534
+ if (!experience?.children) return { pages: [], childViews: [] };
2535
+ const routerExtracted = extractFromRouterExperience(experience);
2536
+ if (routerExtracted) return routerExtracted;
2537
+ const routeTable = extractRouteTable(childDefinitions);
2538
+ const childSlugs = new Set(
2539
+ (childDefinitions || []).filter((c) => !c.slug.endsWith("-router") && c.category !== "router").map((c) => c.slug)
2540
+ );
2541
+ const pages = [];
2542
+ const childViews = [];
2543
+ const candidates = [];
2544
+ for (let i = 0; i < experience.children.length; i++) {
2545
+ const child = experience.children[i];
2546
+ if (isChildWorkflowView(child, childSlugs)) {
2547
+ childViews.push(child);
2548
+ continue;
2549
+ }
2550
+ const title = findPageTitle(child);
2551
+ const role = detectPageRole(child);
2552
+ const localDefaults = child.config?.localDefaults || {};
2553
+ const roleGuard = extractRoleGuard(child);
2554
+ const dataSources = collectDataSources(child);
2555
+ candidates.push({
2556
+ title,
2557
+ role,
2558
+ index: i,
2559
+ node: child,
2560
+ localDefaults,
2561
+ roleGuard,
2562
+ dataSources: dataSources.length > 0 ? dataSources : void 0
2563
+ });
2564
+ }
2565
+ const assignments = /* @__PURE__ */ new Map();
2566
+ const usedRoutes = /* @__PURE__ */ new Set();
2567
+ for (const cand of candidates) {
2568
+ if (!cand.title) continue;
2569
+ const roleRoutes = routeTable.filter(
2570
+ (r) => r.role === cand.role && !usedRoutes.has(r.stateName)
2571
+ );
2572
+ let bestRoute = null;
2573
+ let bestScore = 0;
2574
+ for (const route of roleRoutes) {
2575
+ const score = scoreRouteMatch(cand.title, route);
2576
+ if (score > bestScore) {
2577
+ bestScore = score;
2578
+ bestRoute = route;
2579
+ }
2580
+ }
2581
+ if (bestRoute && bestScore >= 3) {
2582
+ assignments.set(cand.index, bestRoute);
2583
+ usedRoutes.add(bestRoute.stateName);
2584
+ }
2585
+ }
2586
+ let changed = true;
2587
+ while (changed) {
2588
+ changed = false;
2589
+ for (const cand of candidates) {
2590
+ if (assignments.has(cand.index)) continue;
2591
+ const remaining = routeTable.filter(
2592
+ (r) => r.role === cand.role && !usedRoutes.has(r.stateName)
2593
+ );
2594
+ if (remaining.length === 1) {
2595
+ assignments.set(cand.index, remaining[0]);
2596
+ usedRoutes.add(remaining[0].stateName);
2597
+ changed = true;
2598
+ } else if (remaining.length > 1 && cand.title) {
2599
+ let bestRoute = null;
2600
+ let bestScore = 0;
2601
+ for (const route of remaining) {
2602
+ const score = scoreRouteMatch(cand.title, route);
2603
+ if (score > bestScore) {
2604
+ bestScore = score;
2605
+ bestRoute = route;
2606
+ }
2607
+ }
2608
+ if (bestRoute && bestScore > 0) {
2609
+ assignments.set(cand.index, bestRoute);
2610
+ usedRoutes.add(bestRoute.stateName);
2611
+ changed = true;
2612
+ }
2613
+ }
2614
+ }
2615
+ }
2616
+ for (const cand of candidates) {
2617
+ const route = assignments.get(cand.index);
2618
+ const slug = route?.page || titleToSlug(cand.title || `page-${cand.index}`);
2619
+ const filePath = `app/${cand.role}/${slug}.tsx`;
2620
+ const componentName = pascalCase4(slug) + "Page";
2621
+ pages.push({
2622
+ route: route?.path || `${cand.role}/${slug}`,
2623
+ role: cand.role,
2624
+ slug,
2625
+ title: cand.title || slug,
2626
+ componentName,
2627
+ tree: cand.node,
2628
+ filePath,
2629
+ localDefaults: cand.localDefaults,
2630
+ roleGuard: cand.roleGuard,
2631
+ dataSources: cand.dataSources
2632
+ });
2633
+ }
2634
+ return { pages, childViews };
2635
+ }
2636
+ function extractFromRouterExperience(experience) {
2637
+ if (!experience.children) return null;
2638
+ const routerNode = experience.children.find((c) => c.component === "Router");
2639
+ if (!routerNode?.children) return null;
2640
+ const routes = routerNode.children.filter((c) => c.component === "Route");
2641
+ if (routes.length === 0) return null;
2642
+ const pages = [];
2643
+ const seenPaths = /* @__PURE__ */ new Set();
2644
+ for (const route of routes) {
2645
+ const routePath = route.config?.path || "";
2646
+ if (route.config?.exact && routePath === "/") continue;
2647
+ if (seenPaths.has(routePath)) continue;
2648
+ seenPaths.add(routePath);
2649
+ const segments = routePath.replace(/^\//, "").split("/").filter(Boolean);
2650
+ const role = segments.length > 1 ? segments[0] : void 0;
2651
+ const slug = segments.length > 1 ? segments.slice(1).join("-") : segments[0] || "page0";
2652
+ const filePath = role ? `app/${role}/${slug}.tsx` : `app/${slug}.tsx`;
2653
+ const componentName = pascalCase4(slug) + "Page";
2654
+ const rawPageTree = route.children?.[0] || route;
2655
+ const pageTree = { ...rawPageTree };
2656
+ if (pageTree.config) {
2657
+ const { gap, padding, ...restConfig } = pageTree.config;
2658
+ pageTree.config = Object.keys(restConfig).length > 0 ? restConfig : void 0;
2659
+ }
2660
+ const localDefaults = pageTree.config?.localDefaults || {};
2661
+ pages.push({
2662
+ route: routePath,
2663
+ role: role || "",
2664
+ slug,
2665
+ title: findPageTitle(pageTree) || slug,
2666
+ componentName,
2667
+ tree: pageTree,
2668
+ filePath,
2669
+ localDefaults
2670
+ });
2671
+ }
2672
+ return pages.length > 0 ? { pages, childViews: [] } : null;
2673
+ }
2674
+ function generatePageFileFromSection(page, modelSlugToPath) {
2675
+ const fields = Object.entries(page.localDefaults).filter(([key]) => !key.includes(".")).map(([key, value]) => ({
2676
+ name: key,
2677
+ type: inferFieldType(value),
2678
+ required: false,
2679
+ default_value: value ?? void 0
2680
+ }));
2681
+ const contentTree = getPageContentTree(page.tree);
2682
+ const metadata = {};
2683
+ if (page.dataSources && page.dataSources.length > 0) {
2684
+ metadata.dataSources = page.dataSources;
2685
+ if (modelSlugToPath) {
2686
+ const modelImports = {};
2687
+ for (const ds of page.dataSources) {
2688
+ const dsObj = ds;
2689
+ if (dsObj.type !== "workflow") continue;
2690
+ const slug = dsObj.slug || dsObj.name;
2691
+ if (!slug) continue;
2692
+ const modelPath = modelSlugToPath.get(slug);
2693
+ if (modelPath) {
2694
+ const pageDir = page.filePath.split("/").slice(0, -1).join("/");
2695
+ const rel = computeRelativeImport(pageDir, modelPath);
2696
+ modelImports[slug] = rel;
2697
+ }
2698
+ }
2699
+ if (Object.keys(modelImports).length > 0) {
2700
+ metadata.modelImports = modelImports;
2701
+ }
2702
+ }
2703
+ }
2704
+ const pageInput = {
2705
+ slug: page.slug,
2706
+ name: page.componentName,
2707
+ version: "1.0.0",
2708
+ category: "page",
2709
+ states: [],
2710
+ transitions: [],
2711
+ fields,
2712
+ roles: [],
2713
+ experience: contentTree,
2714
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0
2715
+ };
2716
+ return decompile(pageInput, {
2717
+ componentName: page.componentName,
2718
+ includeAnnotation: false
2719
+ }).code;
2720
+ }
2721
+ function computeRelativeImport(fromDir, toFile) {
2722
+ const fromParts = fromDir.split("/").filter(Boolean);
2723
+ const toParts = toFile.replace(/\.(ts|tsx)$/, "").split("/").filter(Boolean);
2724
+ let common = 0;
2725
+ while (common < fromParts.length && common < toParts.length && fromParts[common] === toParts[common]) {
2726
+ common++;
2727
+ }
2728
+ const ups = fromParts.length - common;
2729
+ const downs = toParts.slice(common);
2730
+ if (ups === 0 && downs.length === 0) return "./index";
2731
+ const prefix = ups > 0 ? "../".repeat(ups) : "./";
2732
+ return prefix + downs.join("/");
2733
+ }
2734
+ function generateMainWithPages(definition, pages, childViews) {
2735
+ const mainChildren = [];
2736
+ for (const page of pages) {
2737
+ if (page.roleGuard) {
2738
+ mainChildren.push({
2739
+ id: `page-${page.slug}`,
2740
+ component: "Show",
2741
+ bindings: { when: `$instance.${page.roleGuard}` },
2742
+ children: [{ id: `${page.slug}-ref`, component: page.componentName }]
2743
+ });
2744
+ } else {
2745
+ mainChildren.push({
2746
+ id: `page-${page.slug}`,
2747
+ component: page.componentName
2748
+ });
2749
+ }
2750
+ }
2751
+ for (const cv of childViews) {
2752
+ mainChildren.push(cv);
2753
+ }
2754
+ const mainTree = {
2755
+ id: "root",
2756
+ component: "Stack",
2757
+ children: mainChildren
2758
+ };
2759
+ const strippedMeta = { ...definition.metadata || {} };
2760
+ delete strippedMeta.dataSources;
2761
+ delete strippedMeta.queries;
2762
+ delete strippedMeta.mutations;
2763
+ delete strippedMeta.mutationTargets;
2764
+ const strippedStates = definition.states.map((s) => ({
2765
+ ...s,
2766
+ on_enter: [],
2767
+ on_exit: [],
2768
+ during: [],
2769
+ on_event: void 0
2770
+ }));
2771
+ const strippedTransitions = definition.transitions.map((t5) => ({
2772
+ ...t5,
2773
+ actions: [],
2774
+ conditions: void 0
2775
+ }));
2776
+ const modifiedDef = {
2777
+ ...definition,
2778
+ states: strippedStates,
2779
+ transitions: strippedTransitions,
2780
+ experience: mainTree,
2781
+ metadata: strippedMeta
2782
+ };
2783
+ const result = decompile(modifiedDef, { includeAnnotation: true });
2784
+ const pageImports = pages.map((p) => `import ${p.componentName} from './${p.filePath.replace(/\.tsx$/, "")}';`).join("\n");
2785
+ const code = result.code;
2786
+ const importRegex = /^import .+$/gm;
2787
+ let lastImportEnd = -1;
2788
+ let m;
2789
+ while ((m = importRegex.exec(code)) !== null) {
2790
+ lastImportEnd = m.index + m[0].length;
2791
+ }
2792
+ if (lastImportEnd > 0) {
2793
+ return code.slice(0, lastImportEnd) + "\n" + pageImports + code.slice(lastImportEnd);
2794
+ }
2795
+ return pageImports + "\n" + code;
2796
+ }
2797
+ var SERVER_ACTION_TYPES = /* @__PURE__ */ new Set([
2798
+ "http_request",
2799
+ "webhook",
2800
+ "call_webhook",
2801
+ "notify",
2802
+ "send_notification",
2803
+ "call_workflow",
2804
+ "spawn_instance",
2805
+ "spawn_subworkflow",
2806
+ "emit_event",
2807
+ "custom"
2808
+ ]);
2809
+ function extractServerActions2(states, transitions) {
2810
+ const seen = /* @__PURE__ */ new Set();
2811
+ const actions = [];
2812
+ function collect(defs) {
2813
+ for (const action of defs) {
2814
+ if (SERVER_ACTION_TYPES.has(action.type) && !seen.has(action.id)) {
2815
+ seen.add(action.id);
2816
+ const slug = String(action.config.name || action.config.event || action.config.slug || action.type);
2817
+ const name = camelCase2(slug.replace(/[^a-zA-Z0-9_-]/g, "-"));
2818
+ actions.push({ name, type: action.type, config: action.config });
2819
+ }
2820
+ }
2821
+ }
2822
+ for (const state of states) {
2823
+ collect(state.on_enter);
2824
+ collect(state.on_exit);
2825
+ for (const during of state.during) {
2826
+ collect(during.actions);
2827
+ }
2828
+ }
2829
+ for (const trans of transitions) {
2830
+ collect(trans.actions);
2831
+ }
2832
+ return actions;
2833
+ }
2834
+ function generateServerActionFile2(slug, actions) {
2835
+ const lines = [
2836
+ `/**`,
2837
+ ` * Server actions for "${slug}".`,
2838
+ ` *`,
2839
+ ` * These run server-side during state transitions.`,
2840
+ ` */`,
2841
+ ``,
2842
+ `import type { TransitionContext } from '@mmapp/react';`,
2843
+ ``
2844
+ ];
2845
+ for (const action of actions) {
2846
+ lines.push(`/** ${actionComment2(action)} */`);
2847
+ lines.push(`export async function ${action.name}(ctx: TransitionContext): Promise<void> {`);
2848
+ lines.push(` const { instance, env } = ctx;`);
2849
+ switch (action.type) {
2850
+ case "http_request":
2851
+ case "webhook":
2852
+ case "call_webhook": {
2853
+ const url = String(action.config.url || "https://api.example.com/webhook");
2854
+ const method = String(action.config.method || "POST");
2855
+ lines.push(` await fetch('${esc2(url)}', {`);
2856
+ lines.push(` method: '${method}',`);
2857
+ lines.push(` headers: { 'Content-Type': 'application/json' },`);
2858
+ lines.push(` body: JSON.stringify({ instanceId: instance.id }),`);
2859
+ lines.push(` });`);
2860
+ break;
2861
+ }
2862
+ case "notify":
2863
+ case "send_notification": {
2864
+ const msg = String(action.config.message || action.config.body || "Notification");
2865
+ lines.push(` await env.notify({ message: '${esc2(msg)}', instanceId: instance.id });`);
2866
+ break;
2867
+ }
2868
+ case "call_workflow":
2869
+ case "spawn_instance":
2870
+ case "spawn_subworkflow": {
2871
+ const target = String(action.config.slug || action.config.workflow || "child-workflow");
2872
+ lines.push(` await env.spawn('${esc2(target)}', { parentId: instance.id });`);
2873
+ break;
2874
+ }
2875
+ case "emit_event": {
2876
+ const event = String(action.config.event || action.config.name || "custom-event");
2877
+ lines.push(` await env.emit('${esc2(event)}', { instanceId: instance.id });`);
2878
+ break;
2879
+ }
2880
+ case "custom": {
2881
+ const expr = action.config.expression || action.config.body;
2882
+ if (expr) {
2883
+ lines.push(` // Original expression: ${String(expr).replace(/\n/g, "\\n").slice(0, 200)}`);
2884
+ }
2885
+ lines.push(` // TODO: Implement custom action logic`);
2886
+ lines.push(` throw new Error('Not implemented \u2014 decompiled stub');`);
2887
+ break;
2888
+ }
2889
+ default:
2890
+ lines.push(` // TODO: Implement ${action.type} logic`);
2891
+ }
2892
+ lines.push(`}`);
2893
+ lines.push(``);
2894
+ }
2895
+ return lines.join("\n");
2896
+ }
2897
+ function actionComment2(action) {
2898
+ switch (action.type) {
2899
+ case "http_request":
2900
+ case "webhook":
2901
+ case "call_webhook":
2902
+ return `HTTP ${action.config.method || "POST"} to ${action.config.url || "webhook"}`;
2903
+ case "notify":
2904
+ case "send_notification":
2905
+ return `Send notification: ${action.config.message || ""}`;
2906
+ case "call_workflow":
2907
+ case "spawn_instance":
2908
+ case "spawn_subworkflow":
2909
+ return `Spawn workflow: ${action.config.slug || action.config.workflow || ""}`;
2910
+ case "emit_event":
2911
+ return `Emit event: ${action.config.event || action.config.name || ""}`;
2912
+ case "custom":
2913
+ return `Custom action: ${action.name}`;
2914
+ default:
2915
+ return `Server action: ${action.type}`;
2916
+ }
2917
+ }
2918
+ function emitComponentDefinitions(definition, files) {
2919
+ const meta = definition.metadata;
2920
+ const componentDefs = meta?.componentDefinitions;
2921
+ if (!componentDefs) return;
2922
+ for (const [name, def] of Object.entries(componentDefs)) {
2923
+ const content = generateComponentFromDefinition(name, def.experience, def.props);
2924
+ files.push({
2925
+ path: `components/${name}.tsx`,
2926
+ role: "component",
2927
+ content
2928
+ });
2929
+ }
2930
+ }
2931
+ function inferPropType(propName, experience) {
2932
+ const usages = [];
2933
+ collectPropUsages(propName, experience, usages);
2934
+ if (propName.startsWith("on") && propName.length > 2 && propName[2] === propName[2].toUpperCase()) {
2935
+ return "(() => void)";
2936
+ }
2937
+ const boolNames = ["show", "is", "has", "enable", "disable", "compact", "track", "follow"];
2938
+ if (boolNames.some((b) => propName.toLowerCase().startsWith(b))) return "boolean";
2939
+ for (const usage of usages) {
2940
+ if (usage.includes(".address") || usage.includes("Address")) return "{ address: string; latitude: number; longitude: number }";
2941
+ if (usage.includes(".toFixed")) return "number";
2942
+ if (usage.includes(".toLocaleString")) return "number";
2943
+ if (usage.includes(".heading")) return "{ latitude: number; longitude: number; heading: number }";
2944
+ if (usage.includes(".length")) return "unknown[]";
2945
+ }
2946
+ if (/url|src|image|avatar|icon/i.test(propName)) return "string";
2947
+ if (/count|amount|total|rating|eta|minutes|radius|zoom/i.test(propName)) return "number";
2948
+ if (/style|sx/i.test(propName)) return "React.CSSProperties";
2949
+ if (/items|list|zones|options/i.test(propName)) return "unknown[]";
2950
+ if (/location|position|point/i.test(propName)) return "{ latitude: number; longitude: number }";
2951
+ return "unknown";
2952
+ }
2953
+ function collectPropUsages(propName, node, out) {
2954
+ if (node.bindings) {
2955
+ for (const expr of Object.values(node.bindings)) {
2956
+ if (typeof expr === "string" && expr.includes(propName)) {
2957
+ out.push(expr);
2958
+ }
2959
+ }
2960
+ }
2961
+ if (node.visible_when && typeof node.visible_when === "string" && node.visible_when.includes(propName)) {
2962
+ out.push(node.visible_when);
2963
+ }
2964
+ if (node.children) {
2965
+ for (const child of node.children) {
2966
+ collectPropUsages(propName, child, out);
2967
+ }
2968
+ }
2969
+ }
2970
+ function generateComponentFromDefinition(name, experience, props) {
2971
+ const pageInput = {
2972
+ slug: name.toLowerCase(),
2973
+ name,
2974
+ version: "1.0.0",
2975
+ category: "component",
2976
+ states: [],
2977
+ transitions: [],
2978
+ fields: [],
2979
+ roles: [],
2980
+ experience
2981
+ };
2982
+ const result = decompile(pageInput, {
2983
+ componentName: name,
2984
+ includeAnnotation: false
2985
+ });
2986
+ if (props.length > 0) {
2987
+ const typedProps = props.map((p) => {
2988
+ const type = inferPropType(p, experience);
2989
+ const isCallback = p.startsWith("on") && p.length > 2 && p[2] === p[2].toUpperCase();
2990
+ const optional = isCallback ? "?" : "";
2991
+ return ` ${p}${optional}: ${type};`;
2992
+ });
2993
+ const propsInterface = `interface ${name}Props {
2994
+ ${typedProps.join("\n")}
2995
+ }
2996
+
2997
+ `;
2998
+ const code = result.code.replace(
2999
+ `export default function ${name}()`,
3000
+ `${propsInterface}export default function ${name}({ ${props.join(", ")} }: ${name}Props)`
3001
+ );
3002
+ return code;
3003
+ }
3004
+ return result.code;
3005
+ }
3006
+ function generateActionFile(child) {
3007
+ const isSimple = isSimpleActionDefinition(child);
3008
+ if (isSimple) {
3009
+ return generateSimpleActionFile(child);
3010
+ }
3011
+ return generateModelFile2(
3012
+ child.slug,
3013
+ child.fields,
3014
+ child.states,
3015
+ child.transitions,
3016
+ {
3017
+ version: child.version,
3018
+ category: "action",
3019
+ description: child.description
3020
+ }
3021
+ );
3022
+ }
3023
+ function isSimpleActionDefinition(child) {
3024
+ const stateNames = child.states.map((s) => s.name);
3025
+ const hasReady = stateNames.includes("ready");
3026
+ const hasDone = stateNames.includes("done");
3027
+ if (!hasReady || !hasDone || stateNames.length !== 2) return false;
3028
+ if (child.transitions.length !== 1) return false;
3029
+ const exec = child.transitions[0];
3030
+ if (exec.name !== "execute") return false;
3031
+ if (exec.actions.length !== 1) return false;
3032
+ const action = exec.actions[0];
3033
+ if (action.type !== "eval") return false;
3034
+ const config = action.config;
3035
+ return typeof config.expression === "string";
3036
+ }
3037
+ function generateSimpleActionFile(child) {
3038
+ const exec = child.transitions[0];
3039
+ const evalAction = exec.actions[0];
3040
+ const expression = String(evalAction.config.expression || "");
3041
+ const params = child.fields.map((f) => {
3042
+ const paramName = camelCase2(f.name);
3043
+ const tsType = fieldTypeToTS2(f.type, f);
3044
+ const optional = f.required === false ? "?" : "";
3045
+ return `${paramName}${optional}: ${tsType}`;
3046
+ });
3047
+ const funcName = camelCase2(child.slug);
3048
+ const meta = child.metadata;
3049
+ const sourceFn = typeof meta?.source_function === "string" ? meta.source_function : funcName;
3050
+ const returnType = typeof meta?.return_type === "string" ? meta.return_type : "void";
3051
+ const lines = [];
3052
+ const bodyLines = expression.split("\n").map((l) => ` ${l}`).join("\n");
3053
+ lines.push(`/**`);
3054
+ if (child.description) {
3055
+ lines.push(` * ${child.description}`);
3056
+ } else {
3057
+ lines.push(` * ${child.name || sourceFn}`);
3058
+ }
3059
+ lines.push(` */`);
3060
+ lines.push(`export default function ${sourceFn}(${params.join(", ")}): ${returnType} {`);
3061
+ if (bodyLines.trim()) {
3062
+ lines.push(bodyLines);
3063
+ } else {
3064
+ lines.push(` // TODO: implement`);
3065
+ }
3066
+ lines.push(`}`);
3067
+ lines.push(``);
3068
+ return lines.join("\n");
3069
+ }
3070
+ function emitServerActionsFromMetadata(definition, files) {
3071
+ const meta = definition.metadata;
3072
+ const serverActions = meta?.serverActions;
3073
+ if (!serverActions || serverActions.length === 0) return [];
3074
+ const actionsWithBodies = serverActions.filter((a) => a.body);
3075
+ if (actionsWithBodies.length === 0) return [];
3076
+ const byFile = /* @__PURE__ */ new Map();
3077
+ for (const action of actionsWithBodies) {
3078
+ const file2 = action.sourceFile || `actions/${action.name}.server.ts`;
3079
+ if (!byFile.has(file2)) byFile.set(file2, []);
3080
+ byFile.get(file2).push(action);
3081
+ }
3082
+ const emittedPaths = [];
3083
+ for (const [filePath, actions] of byFile) {
3084
+ const content = generateServerActionFileFromBodies(filePath, actions);
3085
+ const existing = files.findIndex((f) => f.path === filePath);
3086
+ if (existing !== -1) {
3087
+ files[existing].content = content;
3088
+ } else {
3089
+ files.push({ path: filePath, role: "server-action", content });
3090
+ }
3091
+ emittedPaths.push(filePath);
3092
+ }
3093
+ return emittedPaths;
3094
+ }
3095
+ function generateServerActionFileFromBodies(filePath, actions) {
3096
+ const lines = [
3097
+ `/**`,
3098
+ ` * Server actions \u2014 ${filePath}`,
3099
+ ` *`,
3100
+ ` * Auto-generated from preserved function bodies.`,
3101
+ ` */`,
3102
+ ``,
3103
+ `import type { TransitionContext, ActionResult } from '@mmapp/react';`,
3104
+ ``
3105
+ ];
3106
+ for (const action of actions) {
3107
+ if (action.body) {
3108
+ lines.push(action.body);
3109
+ lines.push(``);
3110
+ }
3111
+ }
3112
+ return lines.join("\n");
3113
+ }
3114
+ function reduceRouterTransitions(transitions, states) {
3115
+ if (transitions.length === 0) return transitions;
3116
+ const stateNames = new Set(states.map((s) => s.name));
3117
+ const roleGroups = /* @__PURE__ */ new Map();
3118
+ for (const state of states) {
3119
+ const parts = state.name.split("_");
3120
+ const role = parts[0];
3121
+ if (!roleGroups.has(role)) roleGroups.set(role, []);
3122
+ roleGroups.get(role).push(state.name);
3123
+ }
3124
+ const homeStates = /* @__PURE__ */ new Set();
3125
+ for (const [, group] of roleGroups) {
3126
+ const home = group.find(
3127
+ (s) => s.includes("HOME") || s.includes("DASHBOARD") || s.includes("ANALYTICS")
3128
+ ) || group[0];
3129
+ homeStates.add(home);
3130
+ }
3131
+ const kept = [];
3132
+ const seenKeys = /* @__PURE__ */ new Set();
3133
+ for (const t5 of transitions) {
3134
+ const fromArr = Array.isArray(t5.from) ? t5.from : [t5.from];
3135
+ const fromState = fromArr[0];
3136
+ const toState = t5.to;
3137
+ if (!fromState || !stateNames.has(fromState) || !stateNames.has(toState)) continue;
3138
+ const fromRole = fromState.split("_")[0];
3139
+ const toRole = toState.split("_")[0];
3140
+ const key = `${fromState}\u2192${toState}`;
3141
+ if (seenKeys.has(key)) continue;
3142
+ const sameRole = fromRole === toRole;
3143
+ const crossRoleHome = homeStates.has(fromState) && homeStates.has(toState);
3144
+ if (sameRole || crossRoleHome) {
3145
+ seenKeys.add(key);
3146
+ kept.push(t5);
3147
+ }
3148
+ }
3149
+ return kept;
3150
+ }
3151
+ function decompileProjectEnhanced(definition) {
3152
+ const slug = definition.slug;
3153
+ const decision = determineSplitStrategy(definition);
3154
+ const files = [];
3155
+ const modelPaths = [];
3156
+ const entryPaths = [];
3157
+ const actionPaths = [];
3158
+ if (decision.tier === "single") {
3159
+ const result = decompile(definition);
3160
+ files.push({
3161
+ path: "main.workflow.tsx",
3162
+ role: "view-entry",
3163
+ content: result.code
3164
+ });
3165
+ emitComponentDefinitions(definition, files);
3166
+ emitServerActionsFromMetadata(definition, files);
3167
+ return { files, entryFile: "main.workflow.tsx", slug, splitDecision: decision };
3168
+ }
3169
+ if (decision.emitModels && definition.fields.length > 0) {
3170
+ const modelPath = `models/${slug}.ts`;
3171
+ const modelContent = generateModelFile2(
3172
+ slug,
3173
+ definition.fields,
3174
+ definition.states,
3175
+ definition.transitions,
3176
+ { version: definition.version, category: Array.isArray(definition.category) ? definition.category[0] : definition.category, description: definition.description }
3177
+ );
3178
+ files.push({ path: modelPath, role: "model", content: modelContent });
3179
+ modelPaths.push(modelPath);
3180
+ }
3181
+ if (decision.emitActions) {
3182
+ const serverActions = extractServerActions2(definition.states, definition.transitions);
3183
+ if (serverActions.length > 0) {
3184
+ const actionPath = `actions/${slug}.server.ts`;
3185
+ const actionContent = generateServerActionFile2(slug, serverActions);
3186
+ files.push({ path: actionPath, role: "server-action", content: actionContent });
3187
+ actionPaths.push(actionPath);
3188
+ }
3189
+ }
3190
+ let pagesExtracted = false;
3191
+ let extractedPages = [];
3192
+ let extractedChildViews = [];
3193
+ let routerIsCompilerGenerated = false;
3194
+ if (decision.emitPages && definition.experience) {
3195
+ const routerNode = definition.experience.children?.find(
3196
+ (c) => c.component === "Router"
3197
+ );
3198
+ routerIsCompilerGenerated = !!routerNode;
3199
+ const { pages, childViews } = extractPageSections(
3200
+ definition.experience,
3201
+ definition.childDefinitions
3202
+ );
3203
+ if (pages.length > 0) {
3204
+ pagesExtracted = true;
3205
+ extractedPages = pages;
3206
+ extractedChildViews = childViews;
3207
+ const modelSlugToPath = /* @__PURE__ */ new Map();
3208
+ modelSlugToPath.set(slug, `models/${slug}.ts`);
3209
+ if (definition.childDefinitions) {
3210
+ for (const child of definition.childDefinitions) {
3211
+ const cs = child.slug;
3212
+ if (!cs.endsWith("-router") && child.category !== "router") {
3213
+ modelSlugToPath.set(cs, `models/${cs}.ts`);
3214
+ }
3215
+ }
3216
+ }
3217
+ for (const page of pages) {
3218
+ const pageContent = generatePageFileFromSection(page, modelSlugToPath);
3219
+ files.push({ path: page.filePath, role: "page", content: pageContent });
3220
+ }
3221
+ }
3222
+ }
3223
+ if (definition.roles && definition.roles.length > 0 && decision.tier === "large") {
3224
+ const layoutContent = generateLayoutFile(slug, definition.roles);
3225
+ files.push({ path: "app/layout.tsx", role: "layout", content: layoutContent });
3226
+ }
3227
+ const emittedModelSlugs = new Set(modelPaths.map((p) => p.replace("models/", "").replace(".ts", "")));
3228
+ const mergedChildren = /* @__PURE__ */ new Map();
3229
+ if (definition.childDefinitions && definition.childDefinitions.length > 0) {
3230
+ for (const child of definition.childDefinitions) {
3231
+ const childSlug = child.slug;
3232
+ if (childSlug === slug) continue;
3233
+ const isRouter = childSlug.endsWith("-router") || child.category === "router";
3234
+ if (isRouter && routerIsCompilerGenerated) continue;
3235
+ if (!mergedChildren.has(childSlug)) {
3236
+ mergedChildren.set(childSlug, { ...child });
3237
+ } else {
3238
+ const existing = mergedChildren.get(childSlug);
3239
+ const fieldMap = new Map(existing.fields.map((f) => [f.name, f]));
3240
+ for (const f of child.fields) {
3241
+ if (fieldMap.has(f.name)) {
3242
+ const ef = fieldMap.get(f.name);
3243
+ if (f.required && !ef.required) ef.required = true;
3244
+ if (f.default_value != null && (ef.default_value == null || ef.default_value === "" || ef.default_value === 0 || ef.default_value === false)) {
3245
+ ef.default_value = f.default_value;
3246
+ }
3247
+ } else {
3248
+ existing.fields.push(f);
3249
+ fieldMap.set(f.name, f);
3250
+ }
3251
+ }
3252
+ if (child.states.length > existing.states.length) {
3253
+ const existingByName = new Map(existing.states.map((s) => [s.name, s]));
3254
+ existing.states = child.states.map((s) => {
3255
+ const ex = existingByName.get(s.name);
3256
+ if (ex && (ex.on_enter.length || ex.on_exit.length || ex.during.length)) {
3257
+ return { ...s, on_enter: ex.on_enter, on_exit: ex.on_exit, during: ex.during };
3258
+ }
3259
+ return s;
3260
+ });
3261
+ const childNames = new Set(child.states.map((s) => s.name));
3262
+ const hasStart = child.states.some((s) => s.type === "START");
3263
+ for (const s of existingByName.values()) {
3264
+ if (!childNames.has(s.name)) {
3265
+ if (s.name === "draft" && s.type === "START" && hasStart) continue;
3266
+ existing.states.push(s);
3267
+ }
3268
+ }
3269
+ } else {
3270
+ const stateNames = new Set(existing.states.map((s) => s.name));
3271
+ for (const s of child.states) {
3272
+ if (!stateNames.has(s.name)) {
3273
+ existing.states.push(s);
3274
+ stateNames.add(s.name);
3275
+ }
3276
+ }
3277
+ }
3278
+ const transNames = new Set(existing.transitions.map((t5) => t5.name));
3279
+ for (const t5 of child.transitions) {
3280
+ if (!transNames.has(t5.name)) {
3281
+ existing.transitions.push(t5);
3282
+ transNames.add(t5.name);
3283
+ }
3284
+ }
3285
+ if (child.version && child.version !== "0.1.0" && (!existing.version || existing.version === "0.1.0")) {
3286
+ existing.version = child.version;
3287
+ }
3288
+ if (child.category && child.category !== "data" && (!existing.category || existing.category === "data")) {
3289
+ existing.category = child.category;
3290
+ }
3291
+ if (child.description && !existing.description) {
3292
+ existing.description = child.description;
3293
+ }
3294
+ }
3295
+ }
3296
+ }
3297
+ for (const [childSlug, child] of mergedChildren) {
3298
+ const isRouter = childSlug.endsWith("-router") || child.category === "router";
3299
+ const isAction = child.category === "action";
3300
+ const childTransitions = isRouter ? reduceRouterTransitions(child.transitions, child.states) : child.transitions;
3301
+ if (isAction) {
3302
+ const actionFilePath = `actions/${childSlug}.ts`;
3303
+ files.push({
3304
+ path: actionFilePath,
3305
+ role: "model",
3306
+ content: generateActionFile({ ...child, transitions: childTransitions })
3307
+ });
3308
+ actionPaths.push(actionFilePath);
3309
+ continue;
3310
+ }
3311
+ if (child.fields.length > 0 && !emittedModelSlugs.has(childSlug)) {
3312
+ const childModelPath = `models/${childSlug}.ts`;
3313
+ files.push({
3314
+ path: childModelPath,
3315
+ role: "model",
3316
+ content: generateModelFile2(
3317
+ childSlug,
3318
+ child.fields,
3319
+ child.states,
3320
+ childTransitions,
3321
+ { version: child.version, category: Array.isArray(child.category) ? child.category[0] : child.category, description: child.description }
3322
+ )
3323
+ });
3324
+ modelPaths.push(childModelPath);
3325
+ emittedModelSlugs.add(childSlug);
3326
+ }
3327
+ const childInput = {
3328
+ ...child,
3329
+ version: child.version || definition.version,
3330
+ category: child.category || definition.category,
3331
+ transitions: childTransitions,
3332
+ experience: child.views?.default
3333
+ };
3334
+ const childResult = decompile(childInput, {
3335
+ componentName: pascalCase4(childSlug),
3336
+ includeAnnotation: true
3337
+ });
3338
+ const childPath = `${childSlug}.workflow.tsx`;
3339
+ files.push({ path: childPath, role: "view-entry", content: childResult.code });
3340
+ entryPaths.push(childPath);
3341
+ }
3342
+ emitComponentDefinitions(definition, files);
3343
+ const emittedActionPaths = emitServerActionsFromMetadata(definition, files);
3344
+ actionPaths.push(...emittedActionPaths);
3345
+ const mainPath = "main.workflow.tsx";
3346
+ if (pagesExtracted) {
3347
+ const mainContent = generateMainWithPages(definition, extractedPages, extractedChildViews);
3348
+ files.push({ path: mainPath, role: "view-entry", content: mainContent });
3349
+ } else {
3350
+ const mainDef = decision.emitModels ? {
3351
+ ...definition,
3352
+ states: definition.states.map((s) => ({
3353
+ ...s,
3354
+ on_enter: [],
3355
+ on_exit: [],
3356
+ during: [],
3357
+ on_event: void 0
3358
+ })),
3359
+ // Also strip transition actions — model file has them
3360
+ transitions: definition.transitions.map((t5) => ({
3361
+ ...t5,
3362
+ actions: [],
3363
+ conditions: void 0
3364
+ }))
3365
+ } : definition;
3366
+ const mainResult = decompile(mainDef);
3367
+ files.push({ path: mainPath, role: "view-entry", content: mainResult.code });
3368
+ }
3369
+ entryPaths.push(mainPath);
3370
+ if (decision.emitConfig) {
3371
+ const configData = extractConfigData(
3372
+ definition,
3373
+ [...new Set(modelPaths)],
3374
+ [...new Set(entryPaths)],
3375
+ [...new Set(actionPaths)]
3376
+ );
3377
+ const configContent = generateMmConfig(configData);
3378
+ files.push({ path: "mm.config.ts", role: "config", content: configContent });
3379
+ }
3380
+ files.push({
3381
+ path: "package.json",
3382
+ role: "config",
3383
+ content: generatePackageJson(definition)
3384
+ });
3385
+ files.push({
3386
+ path: "tsconfig.json",
3387
+ role: "config",
3388
+ content: TSCONFIG_TEMPLATE
3389
+ });
3390
+ return {
3391
+ files,
3392
+ entryFile: mainPath,
3393
+ slug,
3394
+ splitDecision: decision
3395
+ };
3396
+ }
3397
+ var TSCONFIG_TEMPLATE = JSON.stringify({
3398
+ compilerOptions: {
3399
+ target: "ES2022",
3400
+ module: "ESNext",
3401
+ moduleResolution: "bundler",
3402
+ jsx: "react-jsx",
3403
+ strict: true,
3404
+ esModuleInterop: true,
3405
+ skipLibCheck: true,
3406
+ forceConsistentCasingInFileNames: true,
3407
+ declaration: true,
3408
+ sourceMap: true,
3409
+ outDir: "dist",
3410
+ rootDir: ".",
3411
+ baseUrl: ".",
3412
+ paths: { "@/*": ["./*"] }
3413
+ },
3414
+ include: ["**/*.ts", "**/*.tsx"],
3415
+ exclude: ["node_modules", "dist"]
3416
+ }, null, 2) + "\n";
3417
+ function generatePackageJson(def) {
3418
+ const pkg = {
3419
+ name: `@mmapp/blueprint-${def.slug}`,
3420
+ version: def.version || "1.0.0",
3421
+ description: def.description || "",
3422
+ type: "module",
3423
+ main: "main.workflow.tsx",
3424
+ scripts: {
3425
+ "type-check": "tsc --noEmit",
3426
+ build: "mmrc build --src .",
3427
+ deploy: "mmrc deploy --build --src ."
3428
+ },
3429
+ peerDependencies: {
3430
+ react: ">=18.0.0",
3431
+ "@mmapp/react": "workspace:*"
3432
+ },
3433
+ devDependencies: {
3434
+ "@mmapp/react-compiler": "workspace:*",
3435
+ "@types/react": "^19.0.0",
3436
+ typescript: "^5.4.0"
3437
+ }
3438
+ };
3439
+ return JSON.stringify(pkg, null, 2) + "\n";
3440
+ }
3441
+ function esc2(s) {
3442
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
3443
+ }
3444
+
3445
+ export {
3446
+ decompileProject,
3447
+ shouldDecompileAsProject,
3448
+ decompileProjectEnhanced,
3449
+ decompile
3450
+ };