@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
@@ -1,1221 +0,0 @@
1
- /**
2
- * Full Blueprint Coverage Test — compiles every example blueprint end-to-end
3
- * and asserts on fields, states, transitions, experience tree structure,
4
- * data sources, roles, routes, and component hierarchy.
5
- *
6
- * Covers both single-file examples (examples/) and real package-level blueprints
7
- * (packages/blueprint-*).
8
- */
9
-
10
- import { describe, it, expect, beforeAll } from 'vitest';
11
- import { transformSync } from '@babel/core';
12
- import { readFileSync, readdirSync, existsSync } from 'fs';
13
- import { resolve, join } from 'path';
14
- import babelPlugin from '../babel';
15
- import { compileProject } from '../project-compiler';
16
- import type { IRWorkflowDefinition, IRExperienceNode } from '@mindmatrix/player-core';
17
-
18
- // =============================================================================
19
- // Helpers
20
- // =============================================================================
21
-
22
- function compileFile(relativePath: string): IRWorkflowDefinition {
23
- const filePath = resolve(__dirname, '../../examples', relativePath);
24
- const code = readFileSync(filePath, 'utf-8');
25
- const result = transformSync(code, {
26
- filename: filePath,
27
- plugins: [[babelPlugin]],
28
- parserOpts: { plugins: ['typescript', 'jsx'], attachComment: true },
29
- });
30
- const ir = (result as any)?.metadata?.mindmatrixIR;
31
- if (!ir) throw new Error(`No IR produced for ${relativePath}`);
32
- return ir;
33
- }
34
-
35
- function loadProjectFiles(dir: string): Record<string, string> {
36
- const files: Record<string, string> = {};
37
-
38
- function walk(current: string, prefix: string) {
39
- for (const entry of readdirSync(current, { withFileTypes: true })) {
40
- const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
41
- if (entry.isDirectory()) {
42
- // Skip test directories and node_modules
43
- if (entry.name === 'tests' || entry.name === 'node_modules' || entry.name === '__tests__') continue;
44
- walk(join(current, entry.name), rel);
45
- } else if (
46
- entry.name.endsWith('.ts') ||
47
- entry.name.endsWith('.tsx')
48
- ) {
49
- files[rel] = readFileSync(join(current, entry.name), 'utf-8');
50
- }
51
- }
52
- }
53
-
54
- walk(dir, '');
55
- return files;
56
- }
57
-
58
- function loadExampleProjectFiles(dir: string): Record<string, string> {
59
- const base = resolve(__dirname, '../../examples', dir);
60
- return loadProjectFiles(base);
61
- }
62
-
63
- function getExperience(ir: IRWorkflowDefinition): IRExperienceNode | undefined {
64
- return ir.metadata?.experience as IRExperienceNode | undefined;
65
- }
66
-
67
- /** Recursively collect all component types from an experience tree */
68
- function collectComponents(node: IRExperienceNode | undefined): string[] {
69
- if (!node) return [];
70
- const result: string[] = [];
71
- if (node.component) result.push(node.component);
72
- if (node.children) {
73
- for (const child of node.children) {
74
- result.push(...collectComponents(child));
75
- }
76
- }
77
- return result;
78
- }
79
-
80
- /** Recursively find all nodes matching a component name */
81
- function findNodes(node: IRExperienceNode | undefined, component: string): IRExperienceNode[] {
82
- if (!node) return [];
83
- const result: IRExperienceNode[] = [];
84
- if (node.component === component) result.push(node);
85
- if (node.children) {
86
- for (const child of node.children) {
87
- result.push(...findNodes(child, component));
88
- }
89
- }
90
- return result;
91
- }
92
-
93
- /** Find all nodes with bindings */
94
- function findBoundNodes(node: IRExperienceNode | undefined): IRExperienceNode[] {
95
- if (!node) return [];
96
- const result: IRExperienceNode[] = [];
97
- if (node.bindings && Object.keys(node.bindings).length > 0) result.push(node);
98
- if (node.children) {
99
- for (const child of node.children) {
100
- result.push(...findBoundNodes(child));
101
- }
102
- }
103
- return result;
104
- }
105
-
106
- /** Find all nodes with visible_when conditions */
107
- function findConditionalNodes(node: IRExperienceNode | undefined): IRExperienceNode[] {
108
- if (!node) return [];
109
- const result: IRExperienceNode[] = [];
110
- if (node.visible_when) result.push(node);
111
- if (node.children) {
112
- for (const child of node.children) {
113
- result.push(...findConditionalNodes(child));
114
- }
115
- }
116
- return result;
117
- }
118
-
119
- /** Count total nodes in tree */
120
- function countNodes(node: IRExperienceNode | undefined): number {
121
- if (!node) return 0;
122
- let count = 1;
123
- if (node.children) {
124
- for (const child of node.children) {
125
- count += countNodes(child);
126
- }
127
- }
128
- return count;
129
- }
130
-
131
- /** Collect all unique component types from experience tree */
132
- function collectComponentTypes(node: IRExperienceNode | undefined): Set<string> {
133
- const types = new Set<string>();
134
- if (!node) return types;
135
- function walk(n: IRExperienceNode) {
136
- if (n.component) types.add(n.component);
137
- n.children?.forEach(walk);
138
- }
139
- walk(node);
140
- return types;
141
- }
142
-
143
- // =============================================================================
144
- // Single-File Blueprints (from examples/)
145
- // =============================================================================
146
-
147
- describe('Full Blueprint Coverage: counter.workflow.tsx', () => {
148
- let ir: IRWorkflowDefinition;
149
- let exp: IRExperienceNode | undefined;
150
-
151
- beforeAll(() => {
152
- ir = compileFile('counter.workflow.tsx');
153
- exp = getExperience(ir);
154
- });
155
-
156
- it('compiles with correct metadata', () => {
157
- expect(ir.slug).toBe('counter');
158
- expect(ir.version).toBe('1.0.0');
159
- expect(ir.category).toBe('workflow');
160
- expect(ir.description).toContain('simple counter');
161
- });
162
-
163
- it('extracts exactly 3 fields with correct types', () => {
164
- expect(ir.fields).toHaveLength(3);
165
- const count = ir.fields.find(f => f.name === 'count');
166
- const status = ir.fields.find(f => f.name === 'status');
167
- const lastAction = ir.fields.find(f => f.name === 'lastAction');
168
- expect(count).toBeDefined();
169
- expect(count!.type).toBe('number');
170
- expect(count!.default_value).toBe(0);
171
- expect(status).toBeDefined();
172
- expect(status!.type).toBe('text');
173
- expect(status!.default_value).toBe('idle');
174
- expect(lastAction).toBeDefined();
175
- expect(lastAction!.type).toBe('text');
176
- expect(lastAction!.default_value).toBe('');
177
- });
178
-
179
- it('extracts 4 transitions with correct from/to', () => {
180
- expect(ir.transitions).toHaveLength(4);
181
- const start = ir.transitions.find(t => t.name === 'start');
182
- const inc = ir.transitions.find(t => t.name === 'increment');
183
- const dec = ir.transitions.find(t => t.name === 'decrement');
184
- const reset = ir.transitions.find(t => t.name === 'reset');
185
- expect(start).toBeDefined();
186
- expect(start!.to).toBe('counting');
187
- expect(inc).toBeDefined();
188
- expect(inc!.from).toContain('counting');
189
- expect(inc!.to).toBe('counting');
190
- expect(dec).toBeDefined();
191
- expect(dec!.from).toContain('counting');
192
- expect(dec!.to).toBe('counting');
193
- expect(reset).toBeDefined();
194
- expect(reset!.from).toContain('counting');
195
- expect(reset!.to).toBe('idle');
196
- });
197
-
198
- it('extracts states with on_enter hooks', () => {
199
- const counting = ir.states.find(s => s.name === 'counting');
200
- expect(counting).toBeDefined();
201
- expect(counting!.on_enter.length).toBeGreaterThanOrEqual(1);
202
- const setFieldActions = counting!.on_enter.filter(a => a.type === 'set_field');
203
- expect(setFieldActions.length).toBeGreaterThanOrEqual(1);
204
- });
205
-
206
- it('extracts field watchers from useOnChange', () => {
207
- const watchers = (ir.metadata as any)?.fieldWatchers;
208
- expect(watchers).toBeDefined();
209
- expect(watchers.length).toBeGreaterThanOrEqual(1);
210
- const countWatcher = watchers.find((w: any) => w.field === 'count');
211
- expect(countWatcher).toBeDefined();
212
- });
213
-
214
- it('produces an experience tree with root Stack', () => {
215
- expect(exp).toBeDefined();
216
- expect(exp!.component).toBe('Stack');
217
- expect(exp!.className).toBe('counter-app');
218
- });
219
-
220
- it('experience tree contains Heading with counter binding', () => {
221
- const headings = findNodes(exp, 'Text');
222
- const counterHeading = headings.find(h =>
223
- h.bindings?.value?.includes('count') || h.config?.value?.toString().includes('Counter')
224
- );
225
- expect(counterHeading).toBeDefined();
226
- });
227
-
228
- it('experience tree contains 3 Button nodes (+1, -1, Reset)', () => {
229
- const buttons = findNodes(exp, 'Button');
230
- expect(buttons.length).toBeGreaterThanOrEqual(3);
231
- const labels = buttons.map(b => b.config?.label || b.config?.value).filter(Boolean);
232
- expect(labels).toContain('+1');
233
- expect(labels).toContain('-1');
234
- expect(labels).toContain('Reset');
235
- });
236
-
237
- it('buttons have onClick bindings', () => {
238
- const buttons = findNodes(exp, 'Button');
239
- for (const btn of buttons) {
240
- const hasAction = btn.bindings?.onClick || btn.bindings?.onPress;
241
- expect(hasAction).toBeDefined();
242
- }
243
- });
244
-
245
- it('has no compilation errors', () => {
246
- expect((ir.metadata as any)?.errors).toBeUndefined();
247
- });
248
- });
249
-
250
- describe('Full Blueprint Coverage: todo-app.workflow.tsx', () => {
251
- let ir: IRWorkflowDefinition;
252
- let exp: IRExperienceNode | undefined;
253
-
254
- beforeAll(() => {
255
- ir = compileFile('todo-app.workflow.tsx');
256
- exp = getExperience(ir);
257
- });
258
-
259
- it('compiles with correct metadata', () => {
260
- expect(ir.slug).toBe('todo-app');
261
- expect(ir.version).toBe('1.0.0');
262
- expect(ir.category).toBe('data');
263
- expect(ir.description).toContain('Todo list');
264
- });
265
-
266
- it('extracts exactly 3 fields', () => {
267
- expect(ir.fields).toHaveLength(3);
268
- expect(ir.fields.find(f => f.name === 'title')?.type).toBe('text');
269
- expect(ir.fields.find(f => f.name === 'title')?.default_value).toBe('');
270
- expect(ir.fields.find(f => f.name === 'filter')?.type).toBe('text');
271
- expect(ir.fields.find(f => f.name === 'filter')?.default_value).toBe('all');
272
- expect(ir.fields.find(f => f.name === 'itemCount')?.type).toBe('number');
273
- expect(ir.fields.find(f => f.name === 'itemCount')?.default_value).toBe(0);
274
- });
275
-
276
- it('extracts 2 data sources targeting todo-item', () => {
277
- const sources = (ir.metadata as any)?.dataSources;
278
- expect(sources).toBeDefined();
279
- expect(sources).toHaveLength(2);
280
- expect(sources[0].slug).toBe('todo-item');
281
- expect(sources[0].pageSize).toBe(100);
282
- expect(sources[0].sort).toBe('created_at');
283
- expect(sources[1].slug).toBe('todo-item');
284
- expect(sources[1].pageSize).toBe(50);
285
- });
286
-
287
- it('extracts mutation target: todo-item', () => {
288
- const targets = (ir.metadata as any)?.mutationTargets;
289
- expect(targets).toBeDefined();
290
- expect(targets).toContain('todo-item');
291
- });
292
-
293
- it('extracts role dependencies: editor, admin', () => {
294
- const roles = (ir.metadata as any)?.roleDependencies;
295
- expect(roles).toBeDefined();
296
- expect(roles).toContain('editor');
297
- expect(roles).toContain('admin');
298
- });
299
-
300
- it('extracts 3 transitions (activate, archive, restore)', () => {
301
- expect(ir.transitions).toHaveLength(3);
302
- const names = ir.transitions.map(t => t.name);
303
- expect(names).toContain('activate');
304
- expect(names).toContain('archive');
305
- expect(names).toContain('restore');
306
-
307
- const activate = ir.transitions.find(t => t.name === 'activate');
308
- expect(activate!.to).toBe('active');
309
- const archive = ir.transitions.find(t => t.name === 'archive');
310
- expect(archive!.from).toContain('active');
311
- expect(archive!.to).toBe('archived');
312
- const restore = ir.transitions.find(t => t.name === 'restore');
313
- expect(restore!.from).toContain('archived');
314
- expect(restore!.to).toBe('active');
315
- });
316
-
317
- it('extracts states with enter/exit/during hooks', () => {
318
- const active = ir.states.find(s => s.name === 'active');
319
- expect(active).toBeDefined();
320
- expect(active!.on_enter.length).toBeGreaterThanOrEqual(1);
321
- expect(active!.on_exit.length).toBeGreaterThanOrEqual(1);
322
- if (active!.during) {
323
- expect(active!.during.length).toBeGreaterThanOrEqual(1);
324
- expect(active!.during[0].interval_ms).toBe(30000);
325
- }
326
- });
327
-
328
- it('experience tree has root Stack with className', () => {
329
- expect(exp).toBeDefined();
330
- expect(exp!.component).toBe('Stack');
331
- expect(exp!.className).toBe('todo-app');
332
- });
333
-
334
- it('experience tree contains heading with item count binding', () => {
335
- const textNodes = findNodes(exp, 'Text');
336
- const heading = textNodes.find(t =>
337
- t.bindings?.value?.includes('itemCount') ||
338
- t.config?.value?.toString().includes('Todo List')
339
- );
340
- expect(heading).toBeDefined();
341
- });
342
-
343
- it('experience tree contains Input with placeholder', () => {
344
- const inputs = findNodes(exp, 'TextInput');
345
- const todoInput = inputs.find(i =>
346
- i.config?.placeholder === 'What needs to be done?'
347
- );
348
- expect(todoInput).toBeDefined();
349
- });
350
-
351
- it('experience tree contains 3 filter buttons (All, Active, Done)', () => {
352
- const buttons = findNodes(exp, 'Button');
353
- const filterLabels = buttons
354
- .map(b => b.config?.label || b.config?.value)
355
- .filter(Boolean);
356
- expect(filterLabels).toContain('All');
357
- expect(filterLabels).toContain('Active');
358
- expect(filterLabels).toContain('Done');
359
- });
360
-
361
- it('experience tree contains Each (list) node for todos', () => {
362
- const eachNodes = findNodes(exp, 'Each');
363
- expect(eachNodes.length).toBeGreaterThanOrEqual(1);
364
- const todoList = eachNodes.find(e => e.bindings?.items || e.config?.items);
365
- expect(todoList).toBeDefined();
366
- });
367
-
368
- it('experience tree has conditional nodes (role guards, empty state)', () => {
369
- const conditionals = findConditionalNodes(exp);
370
- expect(conditionals.length).toBeGreaterThanOrEqual(2);
371
- });
372
-
373
- it('has no compilation errors', () => {
374
- expect((ir.metadata as any)?.errors).toBeUndefined();
375
- });
376
- });
377
-
378
- describe('Full Blueprint Coverage: dashboard.workflow.tsx', () => {
379
- let ir: IRWorkflowDefinition;
380
- let exp: IRExperienceNode | undefined;
381
-
382
- beforeAll(() => {
383
- ir = compileFile('dashboard.workflow.tsx');
384
- exp = getExperience(ir);
385
- });
386
-
387
- it('compiles with correct metadata', () => {
388
- expect(ir.slug).toBe('dashboard');
389
- expect(ir.version).toBe('1.0.0');
390
- expect(ir.category).toBe('blueprint');
391
- expect(ir.description).toContain('dashboard blueprint');
392
- });
393
-
394
- it('extracts exactly 8 fields with correct types and defaults', () => {
395
- expect(ir.fields).toHaveLength(8);
396
- const fieldMap = Object.fromEntries(ir.fields.map(f => [f.name, f]));
397
-
398
- expect(fieldMap.activeRoute.type).toBe('text');
399
- expect(fieldMap.activeRoute.default_value).toBe('/dashboard');
400
- expect(fieldMap.sidebarOpen.type).toBe('boolean');
401
- expect(fieldMap.sidebarOpen.default_value).toBe(true);
402
- expect(fieldMap.searchQuery.type).toBe('text');
403
- expect(fieldMap.searchQuery.default_value).toBe('');
404
- expect(fieldMap.notificationCount.type).toBe('number');
405
- expect(fieldMap.notificationCount.default_value).toBe(0);
406
- expect(fieldMap.selectedRecordId.type).toBe('text');
407
- expect(fieldMap.theme.type).toBe('text');
408
- expect(fieldMap.theme.default_value).toBe('light');
409
- expect(fieldMap.refreshInterval.type).toBe('number');
410
- expect(fieldMap.refreshInterval.default_value).toBe(30000);
411
- expect(fieldMap.lastRefreshed.type).toBe('text');
412
- });
413
-
414
- it('extracts 5 data sources with correct slugs and configs', () => {
415
- const sources = (ir.metadata as any)?.dataSources;
416
- expect(sources).toBeDefined();
417
- expect(sources).toHaveLength(5);
418
- const slugs = sources.map((s: any) => s.slug);
419
- expect(slugs).toContain('dashboard-metric');
420
- expect(slugs).toContain('activity-log');
421
- expect(slugs).toContain('workflow-definition');
422
- expect(slugs).toContain('notification');
423
- expect(slugs).toContain('user-setting');
424
-
425
- const metricSource = sources.find((s: any) => s.slug === 'dashboard-metric');
426
- expect(metricSource.pageSize).toBe(20);
427
- expect(metricSource.sort).toBe('priority');
428
-
429
- const activitySource = sources.find((s: any) => s.slug === 'activity-log');
430
- expect(activitySource.pageSize).toBe(50);
431
- expect(activitySource.sort).toBe('created_at');
432
- });
433
-
434
- it('extracts 2 mutation targets', () => {
435
- const targets = (ir.metadata as any)?.mutationTargets;
436
- expect(targets).toBeDefined();
437
- expect(targets).toContain('user-setting');
438
- expect(targets).toContain('activity-log');
439
- });
440
-
441
- it('extracts 3 role dependencies', () => {
442
- const roles = (ir.metadata as any)?.roleDependencies;
443
- expect(roles).toBeDefined();
444
- expect(roles).toContain('admin');
445
- expect(roles).toContain('viewer');
446
- expect(roles).toContain('editor');
447
- });
448
-
449
- it('extracts 7 transitions with correct state machine', () => {
450
- expect(ir.transitions).toHaveLength(7);
451
- const byName = Object.fromEntries(ir.transitions.map(t => [t.name, t]));
452
-
453
- expect(byName.initialize.to).toBe('active');
454
- expect(byName.suspend.from).toContain('active');
455
- expect(byName.suspend.to).toBe('suspended');
456
- expect(byName.resume.from).toContain('suspended');
457
- expect(byName.resume.to).toBe('active');
458
- expect(byName.deactivate.from).toContain('active');
459
- expect(byName.deactivate.to).toBe('archived');
460
-
461
- expect(byName.navigate.from).toContain('active');
462
- expect(byName.navigate.to).toBe('active');
463
-
464
- expect(byName['open-settings'].from).toContain('active');
465
- expect(byName['open-settings'].to).toBe('settings');
466
- expect(byName['close-settings'].from).toContain('settings');
467
- expect(byName['close-settings'].to).toBe('active');
468
- });
469
-
470
- it('extracts states with on_enter, on_exit, and during', () => {
471
- const active = ir.states.find(s => s.name === 'active');
472
- expect(active).toBeDefined();
473
- expect(active!.on_enter.length).toBeGreaterThanOrEqual(1);
474
- expect(active!.on_exit.length).toBeGreaterThanOrEqual(1);
475
- if (active!.during) {
476
- expect(active!.during.length).toBeGreaterThanOrEqual(1);
477
- expect(active!.during[0].interval_ms).toBe(30000);
478
- }
479
-
480
- const settings = ir.states.find(s => s.name === 'settings');
481
- expect(settings).toBeDefined();
482
- expect(settings!.on_enter.length).toBeGreaterThanOrEqual(1);
483
- });
484
-
485
- it('extracts field watchers for activeRoute and notificationCount', () => {
486
- const watchers = (ir.metadata as any)?.fieldWatchers;
487
- expect(watchers).toBeDefined();
488
- expect(watchers.length).toBeGreaterThanOrEqual(2);
489
- const watchedFields = watchers.map((w: any) => w.field);
490
- expect(watchedFields).toContain('activeRoute');
491
- expect(watchedFields).toContain('notificationCount');
492
- });
493
-
494
- it('extracts 2 event subscriptions', () => {
495
- const events = ir.on_event;
496
- expect(events).toBeDefined();
497
- expect(events!.length).toBeGreaterThanOrEqual(2);
498
- const matches = events!.map(e => e.match);
499
- expect(matches).toContain('data:updated');
500
- expect(matches).toContain('notification:received');
501
- });
502
-
503
- it('experience tree has root Stack', () => {
504
- expect(exp).toBeDefined();
505
- expect(exp!.component).toBe('Stack');
506
- expect(exp!.className).toBe('dashboard-app');
507
- });
508
-
509
- it('experience tree contains MetricCard components', () => {
510
- const cards = findNodes(exp, 'MetricCard');
511
- expect(cards.length).toBeGreaterThanOrEqual(1);
512
- });
513
-
514
- it('experience tree contains Chart components (line and bar)', () => {
515
- const charts = findNodes(exp, 'Chart');
516
- expect(charts.length).toBeGreaterThanOrEqual(2);
517
- const types = charts.map(c => c.config?.type);
518
- expect(types).toContain('line');
519
- expect(types).toContain('bar');
520
- });
521
-
522
- it('experience tree contains ServerGrid for data browser', () => {
523
- const grids = findNodes(exp, 'ServerGrid');
524
- expect(grids.length).toBeGreaterThanOrEqual(1);
525
- const grid = grids[0];
526
- expect(grid.config?.endpoint).toBe('/api/v1/workflow/definitions');
527
- expect(grid.config?.pageSize).toBe(25);
528
- const columns = grid.config?.columns as any[];
529
- expect(columns).toBeDefined();
530
- expect(columns.length).toBe(4);
531
- expect(columns.map((c: any) => c.field)).toEqual(['name', 'category', 'version', 'updated_at']);
532
- });
533
-
534
- it('experience tree contains Select for theme and refresh rate', () => {
535
- const selects = findNodes(exp, 'Select');
536
- expect(selects.length).toBeGreaterThanOrEqual(2);
537
- const themeSelect = selects.find(s =>
538
- s.config?.bind === 'theme' || s.bindings?.bind === 'theme'
539
- );
540
- expect(themeSelect).toBeDefined();
541
- expect(themeSelect!.config?.options).toEqual(['light', 'dark', 'system']);
542
- });
543
-
544
- it('experience tree contains ScrollArea', () => {
545
- const scrollAreas = findNodes(exp, 'ScrollArea');
546
- expect(scrollAreas.length).toBeGreaterThanOrEqual(1);
547
- });
548
-
549
- it('experience tree has conditional rendering for admin sections', () => {
550
- const conditionals = findConditionalNodes(exp);
551
- expect(conditionals.length).toBeGreaterThanOrEqual(3);
552
- });
553
-
554
- it('experience tree has Each loops for metrics, activities, notifications', () => {
555
- const eachNodes = findNodes(exp, 'Each');
556
- expect(eachNodes.length).toBeGreaterThanOrEqual(3);
557
- });
558
-
559
- it('experience tree has Section components in settings', () => {
560
- const sections = findNodes(exp, 'Section');
561
- expect(sections.length).toBeGreaterThanOrEqual(1);
562
- const general = sections.find(s =>
563
- s.config?.title === 'General' || s.config?.description?.toString().includes('dashboard preferences')
564
- );
565
- expect(general).toBeDefined();
566
- });
567
-
568
- it('experience tree contains Card components', () => {
569
- const cards = findNodes(exp, 'Card');
570
- expect(cards.length).toBeGreaterThanOrEqual(2);
571
- });
572
-
573
- it('experience tree contains Badge for notifications', () => {
574
- const badges = findNodes(exp, 'Badge');
575
- expect(badges.length).toBeGreaterThanOrEqual(1);
576
- });
577
-
578
- it('total node count is substantial (complex dashboard)', () => {
579
- const total = countNodes(exp);
580
- expect(total).toBeGreaterThan(30);
581
- });
582
-
583
- it('has data bindings referencing correct model slugs', () => {
584
- const boundNodes = findBoundNodes(exp);
585
- expect(boundNodes.length).toBeGreaterThan(5);
586
- });
587
-
588
- it('has no compilation errors', () => {
589
- expect((ir.metadata as any)?.errors).toBeUndefined();
590
- });
591
- });
592
-
593
- // =============================================================================
594
- // Multi-File Example Blueprints (from examples/)
595
- // =============================================================================
596
-
597
- describe('Full Blueprint Coverage: authentication (multi-file)', () => {
598
- let result: any;
599
- let ir: IRWorkflowDefinition;
600
- let exp: IRExperienceNode | undefined;
601
-
602
- beforeAll(() => {
603
- const files = loadExampleProjectFiles('authentication');
604
- result = compileProject(files);
605
- ir = result.ir;
606
- exp = getExperience(ir);
607
- });
608
-
609
- it('compiles without errors', () => {
610
- expect(result.errors).toHaveLength(0);
611
- });
612
-
613
- it('resolves slug and metadata from JSDoc', () => {
614
- expect(ir.slug).toBe('mod-authentication');
615
- expect(ir.version).toBe('1.0.0');
616
- expect(ir.category).toBe('module');
617
- });
618
-
619
- it('extracts config fields (appName, layout, showSignup, showForgotPassword, redirectPath)', () => {
620
- const fieldNames = ir.fields.map(f => f.name);
621
- expect(fieldNames).toContain('appName');
622
- expect(fieldNames).toContain('layout');
623
- expect(fieldNames).toContain('showSignup');
624
- expect(fieldNames).toContain('showForgotPassword');
625
- expect(fieldNames).toContain('redirectPath');
626
-
627
- expect(ir.fields.find(f => f.name === 'appName')?.default_value).toBe('My App');
628
- expect(ir.fields.find(f => f.name === 'showSignup')?.type).toBe('boolean');
629
- expect(ir.fields.find(f => f.name === 'showSignup')?.default_value).toBe(true);
630
- expect(ir.fields.find(f => f.name === 'layout')?.default_value).toBe('card');
631
- });
632
-
633
- it('extracts runtime fields (email, password, confirmPassword, etc.)', () => {
634
- const fieldNames = ir.fields.map(f => f.name);
635
- expect(fieldNames).toContain('email');
636
- expect(fieldNames).toContain('password');
637
- expect(fieldNames).toContain('confirmPassword');
638
- expect(fieldNames).toContain('firstName');
639
- expect(fieldNames).toContain('lastName');
640
- expect(fieldNames).toContain('errorMessage');
641
- });
642
-
643
- it('extracts total of 11 fields', () => {
644
- expect(ir.fields).toHaveLength(11);
645
- });
646
-
647
- it('extracts 8 auth transitions', () => {
648
- expect(ir.transitions).toHaveLength(8);
649
- const names = ir.transitions.map(t => t.name);
650
- expect(names).toContain('login');
651
- expect(names).toContain('login_success');
652
- expect(names).toContain('login_error');
653
- expect(names).toContain('signup');
654
- expect(names).toContain('signup_success');
655
- expect(names).toContain('signup_error');
656
- expect(names).toContain('logout');
657
- expect(names).toContain('retry');
658
- });
659
-
660
- it('login transition requires email and password', () => {
661
- const login = ir.transitions.find(t => t.name === 'login');
662
- expect(login).toBeDefined();
663
- expect(login!.from).toContain('unauthenticated');
664
- expect(login!.to).toBe('authenticating');
665
- expect(login!.required_fields).toContain('email');
666
- expect(login!.required_fields).toContain('password');
667
- });
668
-
669
- it('signup transition requires email, password, firstName, lastName', () => {
670
- const signup = ir.transitions.find(t => t.name === 'signup');
671
- expect(signup).toBeDefined();
672
- expect(signup!.required_fields).toContain('email');
673
- expect(signup!.required_fields).toContain('password');
674
- expect(signup!.required_fields).toContain('firstName');
675
- expect(signup!.required_fields).toContain('lastName');
676
- });
677
-
678
- it('extracts auth states (unauthenticated, authenticating, authenticated, error)', () => {
679
- const stateNames = ir.states.map(s => s.name);
680
- expect(stateNames).toContain('unauthenticated');
681
- expect(stateNames).toContain('authenticating');
682
- expect(stateNames).toContain('authenticated');
683
- expect(stateNames).toContain('error');
684
- });
685
-
686
- it('states have on_enter hooks that clear fields', () => {
687
- const unauth = ir.states.find(s => s.name === 'unauthenticated');
688
- expect(unauth!.on_enter.length).toBeGreaterThanOrEqual(1);
689
-
690
- const errorState = ir.states.find(s => s.name === 'error');
691
- expect(errorState!.on_enter.length).toBeGreaterThanOrEqual(1);
692
-
693
- const authenticated = ir.states.find(s => s.name === 'authenticated');
694
- expect(authenticated!.on_enter.length).toBeGreaterThanOrEqual(1);
695
- });
696
-
697
- it('experience tree contains Route nodes for /login and /signup', () => {
698
- const routes = findNodes(exp, 'Route');
699
- expect(routes.length).toBeGreaterThanOrEqual(1);
700
- const paths = routes.map(r => r.config?.path);
701
- expect(paths).toContain('/login');
702
- });
703
-
704
- it('experience tree contains Show for conditional signup', () => {
705
- const showNodes = findNodes(exp, 'Show');
706
- expect(showNodes.length).toBeGreaterThanOrEqual(1);
707
- });
708
-
709
- it('experience tree contains TextInput for email and password', () => {
710
- const inputs = findNodes(exp, 'TextInput');
711
- expect(inputs.length).toBeGreaterThanOrEqual(2);
712
- const binds = inputs.map(i => i.config?.bind || i.bindings?.bind);
713
- expect(binds).toContain('email');
714
- expect(binds).toContain('password');
715
- });
716
-
717
- it('experience tree contains Button for Sign In', () => {
718
- const buttons = findNodes(exp, 'Button');
719
- const labels = buttons.map(b => b.config?.label || b.config?.value).filter(Boolean);
720
- expect(labels).toContain('Sign In');
721
- });
722
-
723
- it('experience tree contains Link for navigation between login/signup', () => {
724
- const links = findNodes(exp, 'Link');
725
- expect(links.length).toBeGreaterThanOrEqual(1);
726
- const destinations = links.map(l => l.config?.to);
727
- expect(destinations).toContain('/signup');
728
- });
729
-
730
- it('produces child definitions for pages', () => {
731
- expect(result.childDefinitions).toBeDefined();
732
- });
733
-
734
- it('has no compilation errors', () => {
735
- expect(result.errors).toHaveLength(0);
736
- });
737
- });
738
-
739
- describe('Full Blueprint Coverage: invoice-approval (multi-file)', () => {
740
- let result: any;
741
- let ir: IRWorkflowDefinition;
742
- let exp: IRExperienceNode | undefined;
743
-
744
- beforeAll(() => {
745
- const files = loadExampleProjectFiles('invoice-approval');
746
- result = compileProject(files);
747
- ir = result.ir;
748
- exp = getExperience(ir);
749
- });
750
-
751
- it('compiles without errors', () => {
752
- expect(result.errors).toHaveLength(0);
753
- });
754
-
755
- it('resolves blueprint config from mm.config.ts', () => {
756
- expect(ir.slug).toBe('invoice-approval');
757
- expect(ir.name).toBe('Invoice Approval');
758
- expect(ir.version).toBe('1.0.0');
759
- expect(ir.category).toBe('blueprint');
760
- });
761
-
762
- it('extracts fields from main workflow', () => {
763
- const fieldNames = ir.fields.map(f => f.name);
764
- expect(fieldNames).toContain('activeRoute');
765
- expect(fieldNames).toContain('selectedInvoiceId');
766
- expect(fieldNames).toContain('filterStatus');
767
- expect(fieldNames).toContain('totalPending');
768
- expect(fieldNames).toContain('lastAction');
769
- });
770
-
771
- it('extracts 4 approval transitions', () => {
772
- expect(ir.transitions.length).toBeGreaterThanOrEqual(4);
773
- const names = ir.transitions.map(t => t.name);
774
- expect(names).toContain('submit');
775
- expect(names).toContain('approve');
776
- expect(names).toContain('reject');
777
- expect(names).toContain('revise');
778
- });
779
-
780
- it('submit transition has requiredFields and roles', () => {
781
- const submit = ir.transitions.find(t => t.name === 'submit');
782
- expect(submit).toBeDefined();
783
- expect(submit!.from).toContain('draft');
784
- expect(submit!.to).toBe('submitted');
785
- expect(submit!.required_fields).toContain('amount');
786
- expect(submit!.required_fields).toContain('vendor');
787
- expect(submit!.required_fields).toContain('date');
788
- expect(submit!.roles).toContain('submitter');
789
- });
790
-
791
- it('approve/reject transitions restricted to approver role', () => {
792
- const approve = ir.transitions.find(t => t.name === 'approve');
793
- expect(approve!.roles).toContain('approver');
794
- const reject = ir.transitions.find(t => t.name === 'reject');
795
- expect(reject!.roles).toContain('approver');
796
- });
797
-
798
- it('extracts role dependencies', () => {
799
- const roles = (ir.metadata as any)?.roleDependencies;
800
- expect(roles).toBeDefined();
801
- expect(roles).toContain('approver');
802
- expect(roles).toContain('submitter');
803
- });
804
-
805
- it('extracts states with on_enter hooks', () => {
806
- const draft = ir.states.find(s => s.name === 'draft');
807
- expect(draft).toBeDefined();
808
- expect(draft!.on_enter.length).toBeGreaterThanOrEqual(1);
809
-
810
- const submitted = ir.states.find(s => s.name === 'submitted');
811
- expect(submitted).toBeDefined();
812
- expect(submitted!.on_enter.length).toBeGreaterThanOrEqual(1);
813
-
814
- const approved = ir.states.find(s => s.name === 'approved');
815
- expect(approved).toBeDefined();
816
- expect(approved!.on_enter.length).toBeGreaterThanOrEqual(1);
817
- });
818
-
819
- it('experience tree has root Stack', () => {
820
- expect(exp).toBeDefined();
821
- expect(exp!.component).toBe('Stack');
822
- });
823
-
824
- it('experience tree contains heading "Invoice Approval"', () => {
825
- const textNodes = findNodes(exp, 'Text');
826
- const heading = textNodes.find(t =>
827
- t.config?.value === 'Invoice Approval' ||
828
- t.config?.value?.toString().includes('Invoice Approval')
829
- );
830
- expect(heading).toBeDefined();
831
- });
832
-
833
- it('experience tree contains Show nodes for route pages', () => {
834
- const showNodes = findNodes(exp, 'Show');
835
- expect(showNodes.length).toBeGreaterThanOrEqual(3);
836
- });
837
-
838
- it('produces child definitions for pages and model', () => {
839
- expect(result.childDefinitions).toBeDefined();
840
- });
841
-
842
- it('has no compilation errors', () => {
843
- expect(result.errors).toHaveLength(0);
844
- });
845
- });
846
-
847
- describe('Full Blueprint Coverage: uber-app (multi-file)', () => {
848
- let result: any;
849
- let ir: IRWorkflowDefinition;
850
-
851
- beforeAll(() => {
852
- const files = loadExampleProjectFiles('uber-app');
853
- result = compileProject(files);
854
- ir = result.ir;
855
- });
856
-
857
- it('compiles without errors', () => {
858
- expect(result.errors).toHaveLength(0);
859
- });
860
-
861
- it('resolves blueprint config from mm.config.ts', () => {
862
- expect(ir.slug).toBe('uber-rideshare');
863
- expect(ir.name).toBe('Uber Rideshare');
864
- expect(ir.version).toBe('2.0.0');
865
- expect(ir.category).toBe('blueprint');
866
- });
867
-
868
- it('compiles multiple files into fileIRs', () => {
869
- const fileCount = Object.keys(result.fileIRs).length;
870
- expect(fileCount).toBeGreaterThanOrEqual(5);
871
- });
872
-
873
- it('produces child definitions for models', () => {
874
- expect(result.childDefinitions).toBeDefined();
875
- if (result.childDefinitions.length > 0) {
876
- const slugs = result.childDefinitions.map((d: any) => d.slug);
877
- expect(slugs.length).toBeGreaterThanOrEqual(1);
878
- }
879
- });
880
-
881
- it('merges fields from all workflow files', () => {
882
- expect(ir.fields.length).toBeGreaterThanOrEqual(1);
883
- });
884
-
885
- it('merges transitions from all workflow files', () => {
886
- expect(ir.transitions.length).toBeGreaterThanOrEqual(1);
887
- });
888
-
889
- it('has experience tree', () => {
890
- const exp = getExperience(ir);
891
- expect(result.errors).toHaveLength(0);
892
- });
893
-
894
- it('has no compilation errors', () => {
895
- expect(result.errors).toHaveLength(0);
896
- });
897
- });
898
-
899
- // =============================================================================
900
- // Package-Level Blueprints (packages/blueprint-*)
901
- // =============================================================================
902
-
903
- const PACKAGE_BLUEPRINTS: Array<{
904
- dir: string;
905
- slug: string;
906
- name: string;
907
- minModels: number;
908
- hasRoutes?: boolean;
909
- hasActions?: boolean;
910
- }> = [
911
- {
912
- dir: 'blueprint-employee-salary-tracking',
913
- slug: 'employee-salary-tracking',
914
- name: 'Employee Salary Tracking',
915
- minModels: 4,
916
- hasActions: false,
917
- },
918
- {
919
- dir: 'blueprint-project-tracker',
920
- slug: 'project-tracker',
921
- name: 'Project Tracker',
922
- minModels: 5,
923
- hasRoutes: true,
924
- hasActions: true,
925
- },
926
- {
927
- dir: 'blueprint-auth',
928
- slug: 'mod-authentication',
929
- name: 'Authentication & Authorization',
930
- minModels: 10,
931
- hasActions: true,
932
- },
933
- {
934
- dir: 'blueprint-chat',
935
- slug: 'mm-chat',
936
- name: 'MindMatrix Chat',
937
- minModels: 8,
938
- },
939
- {
940
- dir: 'blueprint-team-directory',
941
- slug: 'team-directory',
942
- name: 'Team Directory',
943
- minModels: 1,
944
- },
945
- {
946
- dir: 'blueprint-accelerator',
947
- slug: 'blueprint-accelerator',
948
- name: 'Accelerator',
949
- minModels: 4,
950
- hasRoutes: true,
951
- hasActions: true,
952
- },
953
- ];
954
-
955
- for (const bp of PACKAGE_BLUEPRINTS) {
956
- const bpDir = resolve(__dirname, '../../../', bp.dir);
957
-
958
- describe(`Blueprint Compilation: ${bp.dir}`, () => {
959
- let result: ReturnType<typeof compileProject>;
960
- let ir: IRWorkflowDefinition;
961
- let exp: IRExperienceNode | undefined;
962
- let skipped = false;
963
-
964
- beforeAll(() => {
965
- if (!existsSync(join(bpDir, 'mm.config.ts'))) {
966
- skipped = true;
967
- return;
968
- }
969
- const files = loadProjectFiles(bpDir);
970
- result = compileProject(files, { usePhase2Modules: true });
971
- ir = result.ir;
972
- exp = getExperience(ir);
973
- });
974
-
975
- it('directory and mm.config.ts exist', () => {
976
- expect(existsSync(bpDir)).toBe(true);
977
- expect(existsSync(join(bpDir, 'mm.config.ts'))).toBe(true);
978
- });
979
-
980
- it('compiles without errors', () => {
981
- if (skipped) return;
982
- expect(result.errors).toHaveLength(0);
983
- });
984
-
985
- it(`resolves slug to "${bp.slug}"`, () => {
986
- if (skipped) return;
987
- expect(ir.slug).toBe(bp.slug);
988
- });
989
-
990
- it(`resolves name to "${bp.name}"`, () => {
991
- if (skipped) return;
992
- expect(ir.name).toBe(bp.name);
993
- });
994
-
995
- it('has a version string', () => {
996
- if (skipped) return;
997
- expect(ir.version).toMatch(/^\d+\.\d+\.\d+$/);
998
- });
999
-
1000
- it('produces valid output with definitions', () => {
1001
- if (skipped) return;
1002
- expect(ir).toBeDefined();
1003
- expect(ir.slug).toBeTruthy();
1004
- expect(ir.version).toBeTruthy();
1005
- });
1006
-
1007
- it('compiles multiple files into fileIRs', () => {
1008
- if (skipped) return;
1009
- const fileCount = Object.keys(result.fileIRs).length;
1010
- // At minimum: mm.config.ts + model files
1011
- expect(fileCount).toBeGreaterThanOrEqual(2);
1012
- });
1013
-
1014
- it(`produces child definitions (at least ${bp.minModels} models declared)`, () => {
1015
- if (skipped) return;
1016
- // The config declares models — child definitions should be produced
1017
- expect(result.childDefinitions).toBeDefined();
1018
- });
1019
-
1020
- it('produces valid experience tree', () => {
1021
- if (skipped) return;
1022
- // Multi-file blueprints may or may not have experience trees
1023
- // depending on whether they have app/ pages
1024
- if (exp) {
1025
- expect(exp.component).toBeTruthy();
1026
- // If the tree has children, verify structure
1027
- if (exp.children && exp.children.length > 0) {
1028
- for (const child of exp.children) {
1029
- // Each child should have at minimum a component or id
1030
- expect(child.component || child.id).toBeTruthy();
1031
- }
1032
- }
1033
- }
1034
- });
1035
-
1036
- it('has expected model definitions in child definitions', () => {
1037
- if (skipped) return;
1038
- if (result.childDefinitions && result.childDefinitions.length > 0) {
1039
- for (const child of result.childDefinitions) {
1040
- // Each child should have a slug
1041
- expect(child.slug).toBeTruthy();
1042
- // Models should have fields
1043
- if (child.fields) {
1044
- for (const field of child.fields) {
1045
- expect(field.name).toBeTruthy();
1046
- expect(field.type).toBeTruthy();
1047
- }
1048
- }
1049
- }
1050
- }
1051
- });
1052
-
1053
- it('fields have valid name and type', () => {
1054
- if (skipped) return;
1055
- for (const field of ir.fields) {
1056
- expect(typeof field.name).toBe('string');
1057
- expect(field.name.length).toBeGreaterThan(0);
1058
- expect(typeof field.type).toBe('string');
1059
- expect(field.type.length).toBeGreaterThan(0);
1060
- }
1061
- });
1062
-
1063
- it('transitions reference valid state names', () => {
1064
- if (skipped) return;
1065
- if (ir.states.length > 0 && ir.transitions.length > 0) {
1066
- const stateNames = new Set(ir.states.map(s => s.name));
1067
- for (const t of ir.transitions) {
1068
- expect(stateNames.has(t.to)).toBe(true);
1069
- for (const from of t.from) {
1070
- expect(stateNames.has(from)).toBe(true);
1071
- }
1072
- }
1073
- }
1074
- });
1075
-
1076
- it('no duplicate field names', () => {
1077
- if (skipped) return;
1078
- const names = ir.fields.map(f => f.name);
1079
- expect(new Set(names).size).toBe(names.length);
1080
- });
1081
-
1082
- it('no duplicate transition names', () => {
1083
- if (skipped) return;
1084
- const names = ir.transitions.map(t => t.name);
1085
- expect(new Set(names).size).toBe(names.length);
1086
- });
1087
-
1088
- if (bp.hasRoutes) {
1089
- it('has valid route structure', () => {
1090
- if (skipped) return;
1091
- // Route table should be populated from mm.config.ts routes
1092
- if (result.routeTable && result.routeTable.length > 0) {
1093
- for (const route of result.routeTable) {
1094
- expect(route.path).toBeTruthy();
1095
- expect(route.path.startsWith('/')).toBe(true);
1096
- }
1097
- }
1098
- // Also check if Router/Route nodes exist in experience tree
1099
- if (exp) {
1100
- const routerNodes = findNodes(exp, 'Router');
1101
- const routeNodes = findNodes(exp, 'Route');
1102
- // At least one routing construct should exist
1103
- expect(routerNodes.length + routeNodes.length).toBeGreaterThanOrEqual(0);
1104
- }
1105
- });
1106
- }
1107
-
1108
- if (bp.hasActions) {
1109
- it('has server actions compiled', () => {
1110
- if (skipped) return;
1111
- if (result.serverActions) {
1112
- expect(result.serverActions.length).toBeGreaterThanOrEqual(1);
1113
- for (const action of result.serverActions) {
1114
- expect(action.name).toBeTruthy();
1115
- }
1116
- }
1117
- });
1118
- }
1119
- });
1120
- }
1121
-
1122
- // =============================================================================
1123
- // Cross-Blueprint Structural Integrity
1124
- // =============================================================================
1125
-
1126
- describe('Cross-Blueprint Structural Integrity', () => {
1127
- const singleFileBlueprints = [
1128
- 'counter.workflow.tsx',
1129
- 'todo-app.workflow.tsx',
1130
- 'dashboard.workflow.tsx',
1131
- ];
1132
-
1133
- for (const file of singleFileBlueprints) {
1134
- it(`${file}: every transition references valid states`, () => {
1135
- const ir = compileFile(file);
1136
- const stateNames = new Set(ir.states.map(s => s.name));
1137
-
1138
- for (const t of ir.transitions) {
1139
- expect(stateNames.has(t.to)).toBe(true);
1140
- for (const from of t.from) {
1141
- expect(stateNames.has(from)).toBe(true);
1142
- }
1143
- }
1144
- });
1145
-
1146
- it(`${file}: no duplicate field names`, () => {
1147
- const ir = compileFile(file);
1148
- const names = ir.fields.map(f => f.name);
1149
- expect(new Set(names).size).toBe(names.length);
1150
- });
1151
-
1152
- it(`${file}: no duplicate transition names`, () => {
1153
- const ir = compileFile(file);
1154
- const names = ir.transitions.map(t => t.name);
1155
- expect(new Set(names).size).toBe(names.length);
1156
- });
1157
-
1158
- it(`${file}: experience tree has valid node IDs`, () => {
1159
- const ir = compileFile(file);
1160
- const exp = getExperience(ir);
1161
- if (exp) {
1162
- const ids: string[] = [];
1163
- function collectIds(node: IRExperienceNode) {
1164
- if (node.id) ids.push(node.id);
1165
- node.children?.forEach(collectIds);
1166
- }
1167
- collectIds(exp);
1168
- expect(ids.every(id => typeof id === 'string' && id.length > 0)).toBe(true);
1169
- }
1170
- });
1171
-
1172
- it(`${file}: all field types are valid slug strings`, () => {
1173
- const ir = compileFile(file);
1174
- for (const field of ir.fields) {
1175
- expect(typeof field.type).toBe('string');
1176
- expect(field.type.length).toBeGreaterThan(0);
1177
- }
1178
- });
1179
- }
1180
- });
1181
-
1182
- // =============================================================================
1183
- // Cross-Blueprint: Package-level compilation sanity
1184
- // =============================================================================
1185
-
1186
- describe('Cross-Blueprint: All package blueprints compile', () => {
1187
- const allBlueprintDirs = PACKAGE_BLUEPRINTS.map(bp => ({
1188
- dir: resolve(__dirname, '../../../', bp.dir),
1189
- name: bp.dir,
1190
- }));
1191
-
1192
- it('all 6 package blueprints have mm.config.ts', () => {
1193
- for (const { dir, name } of allBlueprintDirs) {
1194
- expect(existsSync(join(dir, 'mm.config.ts'))).toBe(true);
1195
- }
1196
- });
1197
-
1198
- it('all package blueprints compile with zero errors', () => {
1199
- for (const { dir, name } of allBlueprintDirs) {
1200
- const files = loadProjectFiles(dir);
1201
- const result = compileProject(files, { usePhase2Modules: true });
1202
- expect(result.errors).toHaveLength(0);
1203
- }
1204
- });
1205
-
1206
- it('all package blueprints produce non-empty slug', () => {
1207
- for (const { dir, name } of allBlueprintDirs) {
1208
- const files = loadProjectFiles(dir);
1209
- const result = compileProject(files, { usePhase2Modules: true });
1210
- expect(result.ir.slug).toBeTruthy();
1211
- }
1212
- });
1213
-
1214
- it('all package blueprints produce at least 1 fileIR', () => {
1215
- for (const { dir, name } of allBlueprintDirs) {
1216
- const files = loadProjectFiles(dir);
1217
- const result = compileProject(files, { usePhase2Modules: true });
1218
- expect(Object.keys(result.fileIRs).length).toBeGreaterThanOrEqual(1);
1219
- }
1220
- });
1221
- });