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

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 (265) hide show
  1. package/ATOM-PIPELINE.md +144 -0
  2. package/README.md +88 -40
  3. package/dist/babel/index.js +2814 -277
  4. package/dist/babel/index.mjs +2 -2
  5. package/dist/chunk-3USIFFE4.mjs +2190 -0
  6. package/dist/chunk-45YMGEVT.mjs +186 -0
  7. package/dist/chunk-4FN2AISW.mjs +148 -0
  8. package/dist/chunk-4OPI5L7G.mjs +2593 -0
  9. package/dist/chunk-4RYTKOOJ.mjs +186 -0
  10. package/dist/chunk-52XHYD2V.mjs +214 -0
  11. package/dist/chunk-5GUFFFGL.mjs +148 -0
  12. package/dist/chunk-5RKTOVR5.mjs +244 -0
  13. package/dist/chunk-5YDMOO4X.mjs +214 -0
  14. package/dist/chunk-64ZWEMLJ.mjs +148 -0
  15. package/dist/chunk-6XP4KSWQ.mjs +2190 -0
  16. package/dist/chunk-72QWL54I.mjs +175 -0
  17. package/dist/chunk-7B4TRI7C.mjs +4835 -0
  18. package/dist/chunk-7ZKGHTNB.mjs +4952 -0
  19. package/dist/chunk-CIESM3BP.mjs +33 -0
  20. package/dist/chunk-DE3ZGQAC.mjs +148 -0
  21. package/dist/chunk-DMCY3BBG.mjs +1933 -0
  22. package/dist/chunk-DPIK3PJS.mjs +244 -0
  23. package/dist/chunk-E5IVH4RE.mjs +186 -0
  24. package/dist/chunk-E6FZNUR5.mjs +4953 -0
  25. package/dist/chunk-EJRBDQDP.mjs +2607 -0
  26. package/dist/chunk-ELO4TXJL.mjs +186 -0
  27. package/dist/chunk-EO6SYNCG.mjs +175 -0
  28. package/dist/chunk-FKRO52XH.mjs +3446 -0
  29. package/dist/chunk-FL4YAKU6.mjs +4941 -0
  30. package/dist/chunk-FYT47UBU.mjs +5076 -0
  31. package/dist/chunk-GCLGPOJZ.mjs +148 -0
  32. package/dist/chunk-GXB4JOP7.mjs +5072 -0
  33. package/dist/chunk-HFXOUMTD.mjs +175 -0
  34. package/dist/chunk-HWIZ47US.mjs +214 -0
  35. package/dist/chunk-IB7MNPQL.mjs +4953 -0
  36. package/dist/chunk-ICSIHQCG.mjs +148 -0
  37. package/dist/chunk-J7JUAHS4.mjs +186 -0
  38. package/dist/chunk-JLA5VNQ3.mjs +186 -0
  39. package/dist/chunk-JQLWFCTM.mjs +214 -0
  40. package/dist/chunk-KFJJCQAL.mjs +148 -0
  41. package/dist/chunk-KJUIIEQE.mjs +186 -0
  42. package/dist/chunk-KNWTHRVQ.mjs +175 -0
  43. package/dist/chunk-KSG4XSZF.mjs +175 -0
  44. package/dist/chunk-LF5N6DOU.mjs +175 -0
  45. package/dist/chunk-LJQCM2IM.mjs +214 -0
  46. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  47. package/dist/chunk-NW6555WJ.mjs +186 -0
  48. package/dist/chunk-OMZE6VLQ.mjs +214 -0
  49. package/dist/chunk-OPJKP747.mjs +7506 -0
  50. package/dist/chunk-P4BR7WVO.mjs +2190 -0
  51. package/dist/chunk-QQHVYH2X.mjs +244 -0
  52. package/dist/chunk-S5QLWLLT.mjs +186 -0
  53. package/dist/chunk-SCWGT2FY.mjs +2190 -0
  54. package/dist/chunk-SMKJUSB3.mjs +2190 -0
  55. package/dist/chunk-THFYE5ZX.mjs +244 -0
  56. package/dist/chunk-VCAY2KGM.mjs +175 -0
  57. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  58. package/dist/chunk-WECAV6QB.mjs +148 -0
  59. package/dist/chunk-WMKBXUCE.mjs +3228 -0
  60. package/dist/chunk-XAJ5BKKL.mjs +4947 -0
  61. package/dist/chunk-XG2X7AEA.mjs +175 -0
  62. package/dist/chunk-XG7Z23NQ.mjs +148 -0
  63. package/dist/chunk-XWZAOCQ7.mjs +2607 -0
  64. package/dist/chunk-Y6MA7ULW.mjs +148 -0
  65. package/dist/chunk-YMS7Q7LG.mjs +214 -0
  66. package/dist/chunk-ZA37XTGA.mjs +175 -0
  67. package/dist/cli/index.js +13189 -6838
  68. package/dist/cli/index.mjs +140 -22
  69. package/dist/codemod/cli.mjs +1 -1
  70. package/dist/codemod/index.mjs +1 -1
  71. package/dist/config-PL24KEWL.mjs +219 -0
  72. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  73. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  74. package/dist/dev-server.d.mts +1 -1
  75. package/dist/dev-server.d.ts +1 -1
  76. package/dist/dev-server.js +4135 -440
  77. package/dist/dev-server.mjs +5 -5
  78. package/dist/envelope.js +2812 -275
  79. package/dist/envelope.mjs +3 -3
  80. package/dist/index.d.mts +161 -2
  81. package/dist/index.d.ts +161 -2
  82. package/dist/index.js +4429 -428
  83. package/dist/index.mjs +217 -9
  84. package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
  85. package/dist/init-DQDX3QK6.mjs +369 -0
  86. package/dist/init-EHO4VQ22.mjs +369 -0
  87. package/dist/init-UC3FWPIW.mjs +367 -0
  88. package/dist/init-UNSMVKIK.mjs +366 -0
  89. package/dist/init-UNV5XIDE.mjs +367 -0
  90. package/dist/project-compiler-2P4N4DR7.mjs +10 -0
  91. package/dist/project-compiler-D2LCC27O.mjs +10 -0
  92. package/dist/project-compiler-EJ3GANJE.mjs +10 -0
  93. package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
  94. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  95. package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
  96. package/dist/project-compiler-VWNNCHGO.mjs +10 -0
  97. package/dist/project-compiler-XVAAU4C5.mjs +10 -0
  98. package/dist/project-compiler-YES5FGMD.mjs +10 -0
  99. package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
  100. package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
  101. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  102. package/dist/project-decompiler-VLPR22QF.mjs +7 -0
  103. package/dist/pull-FUS5QYZS.mjs +109 -0
  104. package/dist/pull-LD5ENLGY.mjs +109 -0
  105. package/dist/pull-P44LDRWB.mjs +109 -0
  106. package/dist/testing/index.js +2822 -285
  107. package/dist/testing/index.mjs +2 -2
  108. package/dist/verify-SEIXUGN4.mjs +1833 -0
  109. package/dist/vite/index.js +2815 -278
  110. package/dist/vite/index.mjs +3 -3
  111. package/examples/uber-app/app/admin/fleet.tsx +19 -19
  112. package/package.json +16 -6
  113. package/compile-blueprint-chat.mjs +0 -99
  114. package/compile-blueprint-glass-console.mjs +0 -98
  115. package/compile-chat-defs.mjs +0 -92
  116. package/examples/uber-app/tests/payment.test.tsx +0 -129
  117. package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
  118. package/package.json.backup +0 -86
  119. package/scripts/decompile.ts +0 -226
  120. package/scripts/seed-auth.ts +0 -267
  121. package/scripts/seed-uber.ts +0 -248
  122. package/scripts/validate-uber.ts +0 -119
  123. package/seed-blueprint-chat.mjs +0 -444
  124. package/seed-blueprint-glass-console.mjs +0 -445
  125. package/seed-compiled.mjs +0 -318
  126. package/src/RoundTripValidator.ts +0 -400
  127. package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
  128. package/src/__tests__/auth-module-compilation.test.ts +0 -247
  129. package/src/__tests__/auth-template-compilation.test.ts +0 -589
  130. package/src/__tests__/change-extractor.test.ts +0 -142
  131. package/src/__tests__/cli-pull.test.ts +0 -73
  132. package/src/__tests__/cli-test.test.ts +0 -72
  133. package/src/__tests__/component-extractor.test.ts +0 -331
  134. package/src/__tests__/context-extractor.test.ts +0 -145
  135. package/src/__tests__/decompiler.test.ts +0 -718
  136. package/src/__tests__/define-blueprint.test.ts +0 -133
  137. package/src/__tests__/definition-validator.test.ts +0 -519
  138. package/src/__tests__/during-extractor.test.ts +0 -152
  139. package/src/__tests__/effect-extractor.test.ts +0 -107
  140. package/src/__tests__/event-emission.test.ts +0 -127
  141. package/src/__tests__/examples.test.ts +0 -236
  142. package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
  143. package/src/__tests__/golden-suite.test.ts +0 -403
  144. package/src/__tests__/grammar-island-extractor.test.ts +0 -289
  145. package/src/__tests__/instance-key.test.ts +0 -82
  146. package/src/__tests__/ir-migration.test.ts +0 -255
  147. package/src/__tests__/lock-file.test.ts +0 -117
  148. package/src/__tests__/model-extractor.test.ts +0 -195
  149. package/src/__tests__/model-field-acl.test.ts +0 -237
  150. package/src/__tests__/model-hooks.test.ts +0 -130
  151. package/src/__tests__/model-ref-resolution.test.ts +0 -268
  152. package/src/__tests__/model-roundtrip.test.ts +0 -502
  153. package/src/__tests__/model-runtime.test.ts +0 -112
  154. package/src/__tests__/model-transitions.test.ts +0 -183
  155. package/src/__tests__/nrt-action-trace.test.ts +0 -391
  156. package/src/__tests__/pipeline-hardening.test.ts +0 -413
  157. package/src/__tests__/project-compiler.test.ts +0 -546
  158. package/src/__tests__/project-decompiler.test.ts +0 -343
  159. package/src/__tests__/query-compilation.test.ts +0 -145
  160. package/src/__tests__/round-trip/PLAN.md +0 -158
  161. package/src/__tests__/round-trip/README.md +0 -52
  162. package/src/__tests__/round-trip/RESULTS.md +0 -86
  163. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
  164. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
  165. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
  166. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
  167. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
  168. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
  169. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
  170. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
  171. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
  172. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
  173. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
  174. package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
  175. package/src/__tests__/round-trip-ir.test.ts +0 -300
  176. package/src/__tests__/round-trip.test.ts +0 -1212
  177. package/src/__tests__/route-merging.test.ts +0 -372
  178. package/src/__tests__/router-composition.test.ts +0 -489
  179. package/src/__tests__/router-extractor.test.ts +0 -176
  180. package/src/__tests__/server-action-extractor.test.ts +0 -128
  181. package/src/__tests__/smart-type-inference.test.ts +0 -365
  182. package/src/__tests__/source-envelope.test.ts +0 -284
  183. package/src/__tests__/source-fidelity.test.ts +0 -516
  184. package/src/__tests__/state-extractor.test.ts +0 -115
  185. package/src/__tests__/strict-mode.test.ts +0 -227
  186. package/src/__tests__/transition-effect-extractor.test.ts +0 -119
  187. package/src/__tests__/transition-extractor.test.ts +0 -68
  188. package/src/__tests__/ts-to-expression.test.ts +0 -462
  189. package/src/__tests__/type-generator.test.ts +0 -201
  190. package/src/__tests__/uber-validation.test.ts +0 -502
  191. package/src/action-compiler.ts +0 -361
  192. package/src/babel/emitters/experience-transform.ts +0 -199
  193. package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
  194. package/src/babel/emitters/pure-form-emitter.ts +0 -1023
  195. package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
  196. package/src/babel/extractors/change-extractor.ts +0 -199
  197. package/src/babel/extractors/component-extractor.ts +0 -907
  198. package/src/babel/extractors/computed-extractor.ts +0 -262
  199. package/src/babel/extractors/context-extractor.ts +0 -277
  200. package/src/babel/extractors/during-extractor.ts +0 -295
  201. package/src/babel/extractors/effect-extractor.ts +0 -340
  202. package/src/babel/extractors/event-extractor.ts +0 -235
  203. package/src/babel/extractors/grammar-island-extractor.ts +0 -302
  204. package/src/babel/extractors/model-extractor.ts +0 -1018
  205. package/src/babel/extractors/router-extractor.ts +0 -303
  206. package/src/babel/extractors/server-action-extractor.ts +0 -173
  207. package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
  208. package/src/babel/extractors/server-state-extractor.ts +0 -88
  209. package/src/babel/extractors/state-extractor.ts +0 -214
  210. package/src/babel/extractors/transition-effect-extractor.ts +0 -176
  211. package/src/babel/extractors/transition-extractor.ts +0 -143
  212. package/src/babel/index.ts +0 -24
  213. package/src/babel/transpilers/ts-to-expression.ts +0 -674
  214. package/src/babel/visitor.ts +0 -807
  215. package/src/cli/auth.ts +0 -255
  216. package/src/cli/build.ts +0 -288
  217. package/src/cli/deploy.ts +0 -206
  218. package/src/cli/index.ts +0 -328
  219. package/src/cli/installer.ts +0 -261
  220. package/src/cli/lock-file.ts +0 -94
  221. package/src/cli/mmrc.ts +0 -22
  222. package/src/cli/pull.ts +0 -172
  223. package/src/cli/registry-client.ts +0 -175
  224. package/src/cli/test.ts +0 -397
  225. package/src/cli/type-generator.ts +0 -243
  226. package/src/codemod/__tests__/forward.test.ts +0 -239
  227. package/src/codemod/__tests__/reverse.test.ts +0 -145
  228. package/src/codemod/__tests__/round-trip.test.ts +0 -137
  229. package/src/codemod/annotation.ts +0 -97
  230. package/src/codemod/classify.ts +0 -197
  231. package/src/codemod/cli.ts +0 -207
  232. package/src/codemod/control-flow.ts +0 -409
  233. package/src/codemod/forward.ts +0 -244
  234. package/src/codemod/import-manager.ts +0 -171
  235. package/src/codemod/index.ts +0 -120
  236. package/src/codemod/reverse.ts +0 -197
  237. package/src/codemod/rules.ts +0 -174
  238. package/src/codemod/state-transform.ts +0 -126
  239. package/src/decompiler/ast-builder.ts +0 -538
  240. package/src/decompiler/config-generator.ts +0 -151
  241. package/src/decompiler/index.ts +0 -315
  242. package/src/decompiler/project-decompiler.ts +0 -1776
  243. package/src/decompiler/project.ts +0 -862
  244. package/src/decompiler/split-strategy.ts +0 -140
  245. package/src/decompiler/state-emitter.ts +0 -1053
  246. package/src/decompiler/sx-emitter.ts +0 -318
  247. package/src/decompiler/workspace-hydrator.ts +0 -189
  248. package/src/dev-server.ts +0 -238
  249. package/src/envelope/fs-tree.ts +0 -217
  250. package/src/envelope/source-envelope.ts +0 -264
  251. package/src/envelope.ts +0 -315
  252. package/src/incremental-compiler.ts +0 -401
  253. package/src/index.ts +0 -99
  254. package/src/model-compiler.ts +0 -277
  255. package/src/project-compiler.ts +0 -1629
  256. package/src/route-extractor.ts +0 -333
  257. package/src/testing/index.ts +0 -32
  258. package/src/testing/snapshot.ts +0 -252
  259. package/src/testing/test-utils.ts +0 -226
  260. package/src/types.ts +0 -68
  261. package/src/vite/index.ts +0 -288
  262. package/test-compile.mjs +0 -142
  263. package/tsconfig.json +0 -25
  264. package/tsup.config.ts +0 -23
  265. package/vitest.config.ts +0 -9
@@ -1,333 +0,0 @@
1
- /**
2
- * Route Extractor — parses app/ directory structure into a routing table.
3
- *
4
- * Supports Next.js-style file-based routing conventions:
5
- * - app/page.tsx → route '/'
6
- * - app/rider/home.tsx → route '/rider/home'
7
- * - app/[id]/page.tsx → route '/:id'
8
- * - app/layout.tsx → wrapping layout boundary
9
- * - app/admin/layout.tsx → nested layout boundary
10
- *
11
- * Generates a router configuration that maps to IR states and transitions.
12
- */
13
-
14
- import type { IRWorkflowDefinition, IRStateDefinition, IRTransitionDefinition, IRFieldDefinition } from '@mindmatrix/player-core';
15
-
16
- // =============================================================================
17
- // Types
18
- // =============================================================================
19
-
20
- export interface RouteEntry {
21
- /** URL path pattern (e.g., '/rider/home', '/chat/:channelId'). */
22
- path: string;
23
- /** IR state name (e.g., 'RIDER_HOME', 'CHAT_CHANNELID'). */
24
- stateName: string;
25
- /** Source file relative path. */
26
- sourceFile: string;
27
- /** Dynamic route parameters. */
28
- params: string[];
29
- /** Nearest layout boundary file. */
30
- layoutBoundary?: string;
31
- /** Route segment depth (0 = root). */
32
- depth: number;
33
- }
34
-
35
- export interface LayoutEntry {
36
- /** Relative path to layout file. */
37
- sourceFile: string;
38
- /** Directory this layout covers. */
39
- directory: string;
40
- /** Depth in the layout hierarchy. */
41
- depth: number;
42
- }
43
-
44
- export interface RouteExtractionResult {
45
- /** All extracted routes. */
46
- routes: RouteEntry[];
47
- /** All discovered layout boundaries. */
48
- layouts: LayoutEntry[];
49
- /** Generated router workflow IR. */
50
- routerIR: IRWorkflowDefinition;
51
- /** Route parameters discovered (for field generation). */
52
- allParams: string[];
53
- }
54
-
55
- export interface RouteExtractorOptions {
56
- /** Blueprint slug prefix for the router. */
57
- slugPrefix?: string;
58
- /** Root directory name (default: 'app'). */
59
- rootDir?: string;
60
- /** Page file naming convention (default: 'page.tsx'). */
61
- pageFileName?: string;
62
- /** Layout file naming convention (default: 'layout.tsx'). */
63
- layoutFileName?: string;
64
- }
65
-
66
- // =============================================================================
67
- // Path Utilities
68
- // =============================================================================
69
-
70
- /**
71
- * Converts a file path to a URL pattern.
72
- * 'app/rider/home.tsx' → '/rider/home'
73
- * 'app/chat/[channelId]/page.tsx' → '/chat/:channelId'
74
- */
75
- function filePathToUrlPattern(filePath: string, rootDir: string): string {
76
- let route = filePath;
77
-
78
- // Remove root directory prefix
79
- const rootPrefix = rootDir + '/';
80
- if (route.startsWith(rootPrefix)) {
81
- route = route.slice(rootPrefix.length);
82
- }
83
-
84
- // Remove file extension
85
- route = route.replace(/\.(tsx?|jsx?)$/, '');
86
-
87
- // Remove trailing /page or /index
88
- route = route.replace(/\/(page|index)$/, '');
89
-
90
- // Handle root page
91
- if (route === 'page' || route === 'index' || route === '') {
92
- return '/';
93
- }
94
-
95
- // Convert [param] to :param
96
- route = route.replace(/\[([^\]]+)\]/g, ':$1');
97
-
98
- // Ensure leading slash
99
- return '/' + route;
100
- }
101
-
102
- /**
103
- * Converts a URL path to an IR state name.
104
- * '/rider/home' → 'RIDER_HOME'
105
- * '/' → 'HOME'
106
- * '/chat/:channelId' → 'CHAT_CHANNELID'
107
- */
108
- function urlToStateName(urlPath: string): string {
109
- if (urlPath === '/') return 'HOME';
110
-
111
- return urlPath
112
- .split('/')
113
- .filter(Boolean)
114
- .map(segment => segment.replace(/^:/, ''))
115
- .join('_')
116
- .replace(/([a-z])([A-Z])/g, '$1_$2')
117
- .replace(/-/g, '_')
118
- .toUpperCase();
119
- }
120
-
121
- /**
122
- * Extracts dynamic parameters from a file path.
123
- * 'app/chat/[channelId]/[messageId]/page.tsx' → ['channelId', 'messageId']
124
- */
125
- function extractRouteParams(filePath: string): string[] {
126
- const params: string[] = [];
127
- const regex = /\[([^\]]+)\]/g;
128
- let match;
129
- while ((match = regex.exec(filePath)) !== null) {
130
- params.push(match[1]);
131
- }
132
- return params;
133
- }
134
-
135
- /**
136
- * Calculates route depth from URL path.
137
- * '/' → 0, '/rider' → 1, '/rider/home' → 2
138
- */
139
- function routeDepth(urlPath: string): number {
140
- if (urlPath === '/') return 0;
141
- return urlPath.split('/').filter(Boolean).length;
142
- }
143
-
144
- // =============================================================================
145
- // File Classification
146
- // =============================================================================
147
-
148
- function isPageFile(filename: string, pageFileName: string): boolean {
149
- // Match exact page file name or any .tsx/.ts in the app directory
150
- if (filename.endsWith('/' + pageFileName)) return true;
151
- // Also match direct component files like app/rider/home.tsx
152
- return /\.(tsx|ts|jsx|js)$/.test(filename)
153
- && !filename.endsWith('.test.ts')
154
- && !filename.endsWith('.test.tsx')
155
- && !filename.includes('layout');
156
- }
157
-
158
- function isLayoutFile(filename: string, layoutFileName: string): boolean {
159
- return filename.endsWith('/' + layoutFileName) || filename.endsWith(layoutFileName);
160
- }
161
-
162
- // =============================================================================
163
- // Main API
164
- // =============================================================================
165
-
166
- /**
167
- * Extracts routes from a set of files in an app/ directory structure.
168
- *
169
- * @param files - Record of filename → source code (only app/ files needed)
170
- * @param options - Extraction options
171
- * @returns RouteExtractionResult with routes, layouts, and router IR
172
- */
173
- export function extractRoutes(
174
- files: Record<string, string>,
175
- options: RouteExtractorOptions = {},
176
- ): RouteExtractionResult {
177
- const rootDir = options.rootDir || 'app';
178
- const pageFileName = options.pageFileName || 'page.tsx';
179
- const layoutFileName = options.layoutFileName || 'layout.tsx';
180
- const slugPrefix = options.slugPrefix || 'app';
181
-
182
- // Discover layouts
183
- const layouts: LayoutEntry[] = [];
184
- for (const filename of Object.keys(files)) {
185
- if (isLayoutFile(filename, layoutFileName)) {
186
- const dir = filename.replace(/\/[^/]+$/, '');
187
- layouts.push({
188
- sourceFile: filename,
189
- directory: dir,
190
- depth: dir.split('/').filter(Boolean).length,
191
- });
192
- }
193
- }
194
- // Sort layouts by depth (shallowest first) for boundary lookup
195
- layouts.sort((a, b) => a.depth - b.depth);
196
-
197
- // Discover routes
198
- const routes: RouteEntry[] = [];
199
- const allParams = new Set<string>();
200
-
201
- for (const filename of Object.keys(files)) {
202
- // Skip layout files and non-app files
203
- if (isLayoutFile(filename, layoutFileName)) continue;
204
- if (!filename.startsWith(rootDir + '/') && !filename.startsWith(rootDir)) continue;
205
- if (!isPageFile(filename, pageFileName)) continue;
206
-
207
- const urlPath = filePathToUrlPattern(filename, rootDir);
208
- const stateName = urlToStateName(urlPath);
209
- const params = extractRouteParams(filename);
210
- const depth = routeDepth(urlPath);
211
-
212
- // Find nearest layout boundary
213
- const fileDir = filename.replace(/\/[^/]+$/, '');
214
- let layoutBoundary: string | undefined;
215
- for (let i = layouts.length - 1; i >= 0; i--) {
216
- if (fileDir.startsWith(layouts[i].directory)) {
217
- layoutBoundary = layouts[i].sourceFile;
218
- break;
219
- }
220
- }
221
-
222
- for (const p of params) allParams.add(p);
223
-
224
- routes.push({
225
- path: urlPath,
226
- stateName,
227
- sourceFile: filename,
228
- params,
229
- layoutBoundary,
230
- depth,
231
- });
232
- }
233
-
234
- // Sort routes by path for deterministic output
235
- routes.sort((a, b) => a.path.localeCompare(b.path));
236
-
237
- // Generate router workflow IR
238
- const routerIR = buildRouterIR(routes, layouts, Array.from(allParams), slugPrefix);
239
-
240
- return {
241
- routes,
242
- layouts,
243
- routerIR,
244
- allParams: Array.from(allParams),
245
- };
246
- }
247
-
248
- /**
249
- * Builds a router IRWorkflowDefinition from extracted routes.
250
- */
251
- function buildRouterIR(
252
- routes: RouteEntry[],
253
- layouts: LayoutEntry[],
254
- allParams: string[],
255
- slugPrefix: string,
256
- ): IRWorkflowDefinition {
257
- // Build states — one per route
258
- const states: IRStateDefinition[] = routes.map(route => ({
259
- name: route.stateName,
260
- type: route.path === '/' ? 'START' as const : 'REGULAR' as const,
261
- description: 'Route: ' + route.path,
262
- on_enter: [],
263
- during: [],
264
- on_exit: [],
265
- }));
266
-
267
- // Ensure at least one START state
268
- if (states.length > 0 && !states.some(s => s.type === 'START')) {
269
- states[0].type = 'START';
270
- }
271
-
272
- // Build transitions — navigate from any state to any other state
273
- const transitions: IRTransitionDefinition[] = [];
274
- for (const from of routes) {
275
- for (const to of routes) {
276
- if (from.stateName === to.stateName) continue;
277
- transitions.push({
278
- name: 'nav_' + from.stateName.toLowerCase() + '_to_' + to.stateName.toLowerCase(),
279
- from: [from.stateName],
280
- to: to.stateName,
281
- actions: [],
282
- conditions: [],
283
- });
284
- }
285
- }
286
-
287
- // Build fields for route parameters
288
- const fields: IRFieldDefinition[] = allParams.map(param => ({
289
- name: param,
290
- type: 'text' as const,
291
- label: param.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase()),
292
- required: false,
293
- default_value: '',
294
- }));
295
-
296
- // Add current_route field
297
- fields.push({
298
- name: 'current_route',
299
- type: 'text',
300
- label: 'Current Route',
301
- required: false,
302
- default_value: '/',
303
- });
304
-
305
- return {
306
- slug: slugPrefix + '-router',
307
- name: 'Router',
308
- version: '1.0.0',
309
- description: 'Auto-generated router from app/ directory structure',
310
- category: 'router',
311
- fields,
312
- states,
313
- transitions,
314
- roles: [],
315
- tags: [{ tag_name: 'auto-derived' }, { tag_name: 'router' }],
316
- metadata: {
317
- runtime: 'local',
318
- routes: routes.map(r => ({
319
- state: r.stateName,
320
- path: r.path,
321
- params: r.params,
322
- layoutBoundary: r.layoutBoundary,
323
- sourceFile: r.sourceFile,
324
- })),
325
- layoutBoundaries: layouts.map(l => l.sourceFile),
326
- provenance: {
327
- frontend: 'react-compiler',
328
- source: 'route-extractor',
329
- compiler_version: '2.0.0',
330
- },
331
- },
332
- };
333
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Testing utilities — public API for @mindmatrix/react-compiler/testing.
3
- */
4
-
5
- // Compilation helpers
6
- export {
7
- compileWorkflow,
8
- compileModel,
9
- compileServerAction,
10
- compileRaw,
11
- extractFields,
12
- extractStates,
13
- extractTransitions,
14
- createMockBlueprintContext,
15
- createMockWorkflowContext,
16
- } from './test-utils';
17
- export type {
18
- CompileOptions,
19
- MockBlueprintProviderProps,
20
- MockWorkflowProviderProps,
21
- } from './test-utils';
22
-
23
- // Snapshot utilities
24
- export {
25
- snapshotIR,
26
- irToSnapshot,
27
- installIRMatchers,
28
- } from './snapshot';
29
- export type {
30
- IRSnapshot,
31
- IRMatcher,
32
- } from './snapshot';
@@ -1,252 +0,0 @@
1
- /**
2
- * IR Snapshot Utilities — snapshot and compare compiled IR for regression testing.
3
- *
4
- * snapshotIR() compiles a .workflow.tsx source and returns a stable, serializable
5
- * IR representation suitable for vitest's toMatchSnapshot() or the custom
6
- * toMatchIR() matcher.
7
- *
8
- * Usage:
9
- * import { snapshotIR, installIRMatchers } from '@mindmatrix/react-compiler/testing';
10
- *
11
- * // In vitest setup or test file:
12
- * installIRMatchers();
13
- *
14
- * it('counter IR is stable', () => {
15
- * const snapshot = snapshotIR(`
16
- * import { useState } from 'react';
17
- * export function Counter() {
18
- * const [count, setCount] = useState(0);
19
- * return <div>{count}</div>;
20
- * }
21
- * `);
22
- * expect(snapshot).toMatchSnapshot();
23
- * });
24
- *
25
- * it('IR matches expected shape', () => {
26
- * const ir = compileWorkflow(`...`);
27
- * expect(ir).toMatchIR({
28
- * fields: [{ name: 'count', type: 'number' }],
29
- * states: expect.arrayContaining([expect.objectContaining({ name: 'draft' })]),
30
- * });
31
- * });
32
- */
33
-
34
- import { compileWorkflow, compileModel, type CompileOptions } from './test-utils';
35
- import type { IRWorkflowDefinition } from '@mindmatrix/player-core';
36
-
37
- // =============================================================================
38
- // Snapshot
39
- // =============================================================================
40
-
41
- export interface IRSnapshot {
42
- /** Workflow slug. */
43
- slug: string;
44
- /** Version. */
45
- version: string;
46
- /** Category. */
47
- category: string;
48
- /** Field names and types. */
49
- fields: Array<{ name: string; type: string; required?: boolean }>;
50
- /** State names and types. */
51
- states: Array<{ name: string; type: string; enterActions: number; exitActions: number }>;
52
- /** Transition names with from→to. */
53
- transitions: Array<{ name: string; from: string | string[]; to: string; conditions: number }>;
54
- /** Whether experience (JSX) is present. */
55
- hasExperience: boolean;
56
- /** Extension tags present (cedar, cron, etc.). */
57
- extensionTags: string[];
58
- /** Number of data sources. */
59
- dataSourceCount: number;
60
- /** Number of mutation targets. */
61
- mutationTargetCount: number;
62
- /** Error count. */
63
- errorCount: number;
64
- }
65
-
66
- /**
67
- * Compile workflow source and extract a stable snapshot of its IR structure.
68
- *
69
- * The snapshot omits volatile data (action IDs, generated code) and keeps only
70
- * the structural shape — making it safe for snapshot testing across refactors.
71
- */
72
- export function snapshotIR(code: string, options?: CompileOptions): IRSnapshot {
73
- const ir = options?.filename?.includes('models/')
74
- ? compileModel(code, options)
75
- : compileWorkflow(code, options);
76
-
77
- return irToSnapshot(ir);
78
- }
79
-
80
- /**
81
- * Convert an already-compiled IR to a snapshot.
82
- */
83
- export function irToSnapshot(ir: IRWorkflowDefinition): IRSnapshot {
84
- const meta = (ir.metadata ?? {}) as Record<string, unknown>;
85
-
86
- return {
87
- slug: ir.slug ?? '',
88
- version: ir.version ?? '',
89
- category: Array.isArray(ir.category) ? ir.category[0] : (ir.category ?? 'workflow'),
90
- fields: ir.fields.map((f) => ({
91
- name: f.name,
92
- type: f.type,
93
- ...(f.required === false ? { required: false } : {}),
94
- })),
95
- states: ir.states.map((s) => ({
96
- name: s.name,
97
- type: s.type,
98
- enterActions: s.on_enter?.length ?? 0,
99
- exitActions: s.on_exit?.length ?? 0,
100
- })),
101
- transitions: ir.transitions.map((t) => ({
102
- name: t.name,
103
- from: t.from,
104
- to: t.to,
105
- conditions: t.conditions?.length ?? 0,
106
- })),
107
- hasExperience: !!meta.experience || !!(ir as any).experience,
108
- extensionTags: ir.extensions ? Object.keys(ir.extensions) : [],
109
- dataSourceCount: Array.isArray(meta.dataSources) ? meta.dataSources.length : 0,
110
- mutationTargetCount: Array.isArray(meta.mutationTargets) ? meta.mutationTargets.length : 0,
111
- errorCount: Array.isArray(meta.errors) ? meta.errors.length : 0,
112
- };
113
- }
114
-
115
- // =============================================================================
116
- // Custom Matcher: toMatchIR
117
- // =============================================================================
118
-
119
- /**
120
- * Partial IR shape for matching.
121
- * Every field is optional — only specified fields are checked.
122
- */
123
- export interface IRMatcher {
124
- slug?: string;
125
- version?: string;
126
- category?: string;
127
- fields?: Array<Record<string, unknown>>;
128
- states?: Array<Record<string, unknown>>;
129
- transitions?: Array<Record<string, unknown>>;
130
- description?: string;
131
- [key: string]: unknown;
132
- }
133
-
134
- /**
135
- * Install the toMatchIR() custom matcher into vitest's expect.
136
- *
137
- * Call this once in your test setup file or at the top of a test file:
138
- * import { installIRMatchers } from '@mindmatrix/react-compiler/testing';
139
- * installIRMatchers();
140
- */
141
- export function installIRMatchers(): void {
142
- // Dynamic import of expect to avoid requiring vitest at module parse time
143
- try {
144
- // eslint-disable-next-line @typescript-eslint/no-require-imports
145
- const { expect } = require('vitest');
146
-
147
- expect.extend({
148
- toMatchIR(received: IRWorkflowDefinition, expected: IRMatcher) {
149
- const failures: string[] = [];
150
-
151
- if (expected.slug !== undefined && received.slug !== expected.slug) {
152
- failures.push(`slug: expected "${expected.slug}", got "${received.slug}"`);
153
- }
154
- if (expected.version !== undefined && received.version !== expected.version) {
155
- failures.push(`version: expected "${expected.version}", got "${received.version}"`);
156
- }
157
- if (expected.category !== undefined && received.category !== expected.category) {
158
- failures.push(`category: expected "${expected.category}", got "${received.category}"`);
159
- }
160
- if (expected.description !== undefined && received.description !== expected.description) {
161
- failures.push(`description: expected "${expected.description}", got "${received.description}"`);
162
- }
163
-
164
- // Field matching — check each expected field exists with matching properties
165
- if (expected.fields !== undefined) {
166
- const expectedFields = expected.fields as Array<Record<string, unknown>>;
167
- for (const exp of expectedFields) {
168
- const match = received.fields.find((f) => f.name === exp.name);
169
- if (!match) {
170
- failures.push(`fields: expected field "${exp.name}" not found`);
171
- } else {
172
- const matchRec = match as unknown as Record<string, unknown>;
173
- for (const [key, val] of Object.entries(exp)) {
174
- if (matchRec[key] !== val) {
175
- failures.push(`fields.${exp.name}.${key}: expected ${JSON.stringify(val)}, got ${JSON.stringify(matchRec[key])}`);
176
- }
177
- }
178
- }
179
- }
180
- }
181
-
182
- // State matching
183
- if (expected.states !== undefined && Array.isArray(expected.states)) {
184
- const expectedStates = expected.states as Array<Record<string, unknown>>;
185
- for (const exp of expectedStates) {
186
- const match = received.states.find((s) => s.name === exp.name);
187
- if (!match) {
188
- failures.push(`states: expected state "${exp.name}" not found`);
189
- } else {
190
- const matchRec = match as unknown as Record<string, unknown>;
191
- for (const [key, val] of Object.entries(exp)) {
192
- if (key === 'on_enter' || key === 'on_exit' || key === 'during') {
193
- const arr = matchRec[key] as unknown[] | undefined;
194
- if (typeof val === 'number' && (arr?.length ?? 0) !== val) {
195
- failures.push(`states.${exp.name}.${key}: expected length ${val}, got ${arr?.length ?? 0}`);
196
- }
197
- } else if (matchRec[key] !== val) {
198
- failures.push(`states.${exp.name}.${key}: expected ${JSON.stringify(val)}, got ${JSON.stringify(matchRec[key])}`);
199
- }
200
- }
201
- }
202
- }
203
- }
204
-
205
- // Transition matching
206
- if (expected.transitions !== undefined && Array.isArray(expected.transitions)) {
207
- const expectedTransitions = expected.transitions as Array<Record<string, unknown>>;
208
- for (const exp of expectedTransitions) {
209
- const match = received.transitions.find((t) => t.name === exp.name);
210
- if (!match) {
211
- failures.push(`transitions: expected transition "${exp.name}" not found`);
212
- } else {
213
- if (exp.from !== undefined) {
214
- const fromMatch = JSON.stringify(match.from) === JSON.stringify(exp.from);
215
- if (!fromMatch) {
216
- failures.push(`transitions.${exp.name}.from: expected ${JSON.stringify(exp.from)}, got ${JSON.stringify(match.from)}`);
217
- }
218
- }
219
- if (exp.to !== undefined && match.to !== exp.to) {
220
- failures.push(`transitions.${exp.name}.to: expected "${exp.to}", got "${match.to}"`);
221
- }
222
- }
223
- }
224
- }
225
-
226
- const pass = failures.length === 0;
227
- return {
228
- pass,
229
- message: () =>
230
- pass
231
- ? `expected IR not to match, but it did`
232
- : `IR mismatch:\n${failures.map((f) => ` - ${f}`).join('\n')}`,
233
- };
234
- },
235
- });
236
- } catch {
237
- // vitest not available — skip matcher installation
238
- }
239
- }
240
-
241
- // =============================================================================
242
- // Type augmentation for vitest
243
- // =============================================================================
244
-
245
- // Note: To use the toMatchIR() custom matcher with full type support,
246
- // add the following to your test setup or a global .d.ts file:
247
- //
248
- // import type { IRMatcher } from '@mindmatrix/react-compiler/testing';
249
- // declare module 'vitest' {
250
- // interface Assertion<T> { toMatchIR(expected: IRMatcher): void; }
251
- // interface AsymmetricMatchersContaining { toMatchIR(expected: IRMatcher): void; }
252
- // }