@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
@@ -0,0 +1,2190 @@
1
+ import {
2
+ babelPlugin,
3
+ transpileBlock
4
+ } from "./chunk-XAJ5BKKL.mjs";
5
+
6
+ // src/project-compiler.ts
7
+ import { transformSync as transformSync3 } from "@babel/core";
8
+
9
+ // src/babel/extractors/router-extractor.ts
10
+ function pathToStateName(routePath) {
11
+ if (!routePath || routePath === "" || routePath === "/") return "HOME";
12
+ return routePath.split("/").filter(Boolean).map((segment) => {
13
+ if (segment.startsWith("[") && segment.endsWith("]")) {
14
+ return segment.slice(1, -1);
15
+ }
16
+ return segment;
17
+ }).join("_").replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase().replace(/-/g, "_");
18
+ }
19
+ function pathToUrlPattern(relativePath, pageFileName = "page.tsx") {
20
+ let routePath = relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
21
+ routePath = routePath.replace(/\[([^\]]+)\]/g, ":$1");
22
+ return "/" + routePath;
23
+ }
24
+ function extractParams(relativePath) {
25
+ const params = [];
26
+ const paramRegex = /\[([^\]]+)\]/g;
27
+ let match;
28
+ while ((match = paramRegex.exec(relativePath)) !== null) {
29
+ params.push(match[1]);
30
+ }
31
+ return params;
32
+ }
33
+ function extractRouterWorkflow(pages, options = {}) {
34
+ const {
35
+ slug = "app-router",
36
+ pageFileName = "page.tsx",
37
+ layoutFileName = "layout.tsx"
38
+ } = options;
39
+ const pageFiles = pages.filter((p) => !p.isLayout && p.relativePath.endsWith(pageFileName));
40
+ const layoutFiles = pages.filter((p) => p.isLayout || p.relativePath.endsWith(layoutFileName));
41
+ const routeStates = [];
42
+ const stateMap = /* @__PURE__ */ new Map();
43
+ for (const page of pageFiles) {
44
+ const routePath = page.relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
45
+ const stateName = pathToStateName(routePath);
46
+ const urlPattern = pathToUrlPattern(page.relativePath, pageFileName);
47
+ const params = extractParams(page.relativePath);
48
+ const layoutBoundary = findLayoutBoundary(page.relativePath, layoutFiles);
49
+ const routeState = {
50
+ name: stateName,
51
+ path: urlPattern,
52
+ params,
53
+ layoutBoundary,
54
+ sourceFile: page.relativePath,
55
+ type: stateName === "HOME" ? "START" : "REGULAR"
56
+ };
57
+ routeStates.push(routeState);
58
+ stateMap.set(stateName, routeState);
59
+ }
60
+ if (!stateMap.has("HOME") && routeStates.length > 0) {
61
+ routeStates[0].type = "START";
62
+ }
63
+ const states = routeStates.map((rs) => ({
64
+ name: rs.name,
65
+ type: rs.type,
66
+ description: `Route: ${rs.path}`,
67
+ on_enter: [],
68
+ during: [],
69
+ on_exit: []
70
+ }));
71
+ const transitions = [];
72
+ let transitionId = 0;
73
+ for (const page of pageFiles) {
74
+ if (!page.navigationTargets) continue;
75
+ const fromPath = page.relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
76
+ const fromState = pathToStateName(fromPath);
77
+ for (const target of page.navigationTargets) {
78
+ const targetPath = target.replace(/^\//, "").replace(/\/page\.(tsx|ts|jsx|js)$/, "");
79
+ const toState = pathToStateName(targetPath);
80
+ if (stateMap.has(toState) || toState === fromState) continue;
81
+ const transitionName = `navigate_to_${toState.toLowerCase()}`;
82
+ transitions.push({
83
+ name: transitionName,
84
+ from: [fromState],
85
+ to: toState,
86
+ actions: [],
87
+ conditions: []
88
+ });
89
+ transitionId++;
90
+ }
91
+ }
92
+ if (routeStates.length > 1) {
93
+ for (const rs of routeStates) {
94
+ if (rs.type !== "START") {
95
+ transitions.push({
96
+ name: `navigate_home_from_${rs.name.toLowerCase()}`,
97
+ from: [rs.name],
98
+ to: "HOME",
99
+ actions: [],
100
+ conditions: []
101
+ });
102
+ }
103
+ }
104
+ }
105
+ const fields = [];
106
+ const seenParams = /* @__PURE__ */ new Set();
107
+ for (const rs of routeStates) {
108
+ for (const param of rs.params) {
109
+ if (seenParams.has(param)) continue;
110
+ seenParams.add(param);
111
+ fields.push({
112
+ name: param,
113
+ type: "text",
114
+ required: false,
115
+ default_value: ""
116
+ });
117
+ }
118
+ }
119
+ const routeMetadata = routeStates.map((rs) => ({
120
+ state: rs.name,
121
+ path: rs.path,
122
+ params: rs.params,
123
+ layoutBoundary: rs.layoutBoundary,
124
+ sourceFile: rs.sourceFile
125
+ }));
126
+ return {
127
+ slug,
128
+ name: "Router",
129
+ version: "1.0.0",
130
+ description: "Auto-derived router workflow from file-based routing",
131
+ category: "router",
132
+ fields,
133
+ states,
134
+ transitions,
135
+ roles: [],
136
+ tags: [{ tag_name: "auto-derived" }, { tag_name: "router" }],
137
+ metadata: {
138
+ runtime: "local",
139
+ routes: routeMetadata,
140
+ layoutBoundaries: layoutFiles.map((l) => l.relativePath)
141
+ }
142
+ };
143
+ }
144
+ function findLayoutBoundary(pageRelativePath, layoutFiles) {
145
+ const layoutPaths = layoutFiles.map((l) => l.relativePath);
146
+ const parts = pageRelativePath.split("/");
147
+ for (let i = parts.length - 1; i >= 0; i--) {
148
+ const dir = parts.slice(0, i).join("/");
149
+ const layoutPath = dir ? `${dir}/layout.tsx` : "layout.tsx";
150
+ if (layoutPaths.includes(layoutPath)) {
151
+ return layoutPath;
152
+ }
153
+ }
154
+ return void 0;
155
+ }
156
+
157
+ // src/model-compiler.ts
158
+ import { transformSync } from "@babel/core";
159
+ function extractInterfaceName(source) {
160
+ const match = source.match(/export\s+interface\s+(\w+)/);
161
+ return match ? match[1] : "Unknown";
162
+ }
163
+ function extractWorkflowAnnotation(source) {
164
+ const result = {};
165
+ const workflowMatch = source.match(/@workflow\s+(.+)/);
166
+ if (workflowMatch) {
167
+ const attrs = workflowMatch[1];
168
+ const slugM = attrs.match(/slug="([^"]+)"/);
169
+ if (slugM) result.slug = slugM[1];
170
+ const versionM = attrs.match(/version="([^"]+)"/);
171
+ if (versionM) result.version = versionM[1];
172
+ const categoryM = attrs.match(/category="([^"]+)"/);
173
+ if (categoryM) result.category = categoryM[1];
174
+ }
175
+ const descMatch = source.match(/@description\s+(.+)/);
176
+ if (descMatch) result.description = descMatch[1].trim();
177
+ return result;
178
+ }
179
+ function extractFieldOptionsRaw(source) {
180
+ const result = {};
181
+ const match = source.match(/export\s+const\s+fieldOptions\s*=\s*\{([\s\S]*?)\};/);
182
+ if (!match) return result;
183
+ const body = match[1];
184
+ const fieldRegex = /(\w+)\s*:\s*\{([^}]*)\}/g;
185
+ let fieldMatch;
186
+ while ((fieldMatch = fieldRegex.exec(body)) !== null) {
187
+ const fieldName = fieldMatch[1];
188
+ const optionsBody = fieldMatch[2];
189
+ const opts = {};
190
+ const kvRegex = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|\[([^\]]*)\]|(\d+(?:\.\d+)?)|(\w+))/g;
191
+ let kvMatch;
192
+ while ((kvMatch = kvRegex.exec(optionsBody)) !== null) {
193
+ const key = kvMatch[1];
194
+ const strVal = kvMatch[2] ?? kvMatch[3];
195
+ const arrVal = kvMatch[4];
196
+ const numVal = kvMatch[5];
197
+ const identVal = kvMatch[6];
198
+ if (strVal !== void 0) {
199
+ opts[key] = strVal;
200
+ } else if (arrVal !== void 0) {
201
+ opts[key] = arrVal.split(",").map((s) => s.trim().replace(/['"]/g, "")).filter(Boolean);
202
+ } else if (numVal !== void 0) {
203
+ opts[key] = Number(numVal);
204
+ } else if (identVal !== void 0) {
205
+ if (identVal === "true") opts[key] = true;
206
+ else if (identVal === "false") opts[key] = false;
207
+ else opts[key] = identVal;
208
+ }
209
+ }
210
+ result[fieldName] = opts;
211
+ }
212
+ return result;
213
+ }
214
+ function compileModel(filename, source, options = {}) {
215
+ const mode = options.mode || "infer";
216
+ const interfaceName = extractInterfaceName(source);
217
+ const annotation = extractWorkflowAnnotation(source);
218
+ const rawFieldOptions = extractFieldOptionsRaw(source);
219
+ const parserPlugins = filename.endsWith(".tsx") ? ["typescript", "jsx"] : ["typescript"];
220
+ const babelResult = transformSync(source, {
221
+ filename,
222
+ plugins: [[babelPlugin, { mode }]],
223
+ parserOpts: { plugins: parserPlugins, attachComment: true }
224
+ });
225
+ const ir = babelResult?.metadata?.mindmatrixIR ?? createEmptyModelIR(interfaceName);
226
+ ir.category = options.categoryOverride || annotation.category || "data";
227
+ if (options.slugOverride) {
228
+ ir.slug = options.slugOverride;
229
+ }
230
+ if (ir.states.length === 0) {
231
+ ir.states.push({
232
+ name: "draft",
233
+ type: "START",
234
+ on_enter: [],
235
+ during: [],
236
+ on_exit: []
237
+ });
238
+ }
239
+ const hasStart = ir.states.some((s) => s.type === "START");
240
+ if (!hasStart && ir.states.length > 0) {
241
+ ir.states[0].type = "START";
242
+ }
243
+ if (!ir.metadata) ir.metadata = {};
244
+ const meta = ir.metadata;
245
+ meta.sourceInterface = interfaceName;
246
+ meta.fieldOptions = rawFieldOptions;
247
+ meta.provenance = {
248
+ frontend: "react-compiler",
249
+ source: "model",
250
+ compiler_version: "2.0.0"
251
+ };
252
+ return {
253
+ ir,
254
+ interfaceName,
255
+ fieldNames: ir.fields.map((f) => f.name),
256
+ transitionNames: ir.transitions.map((t2) => t2.name),
257
+ stateNames: ir.states.map((s) => s.name),
258
+ hasFieldOptions: Object.keys(rawFieldOptions).length > 0,
259
+ fieldOptions: rawFieldOptions
260
+ };
261
+ }
262
+ function compileModels(files, options = {}) {
263
+ const results = /* @__PURE__ */ new Map();
264
+ for (const [filename, source] of Object.entries(files)) {
265
+ try {
266
+ const compiled = compileModel(filename, source, options);
267
+ results.set(filename, compiled);
268
+ } catch (_err) {
269
+ const iface = extractInterfaceName(source);
270
+ results.set(filename, {
271
+ ir: createEmptyModelIR(iface),
272
+ interfaceName: iface,
273
+ fieldNames: [],
274
+ transitionNames: [],
275
+ stateNames: ["draft"],
276
+ hasFieldOptions: false,
277
+ fieldOptions: {}
278
+ });
279
+ }
280
+ }
281
+ return results;
282
+ }
283
+ function createEmptyModelIR(interfaceName) {
284
+ const slug = interfaceName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
285
+ return {
286
+ slug,
287
+ name: interfaceName,
288
+ version: "1.0.0",
289
+ description: "Data model: " + interfaceName,
290
+ category: "data",
291
+ fields: [],
292
+ states: [{
293
+ name: "draft",
294
+ type: "START",
295
+ on_enter: [],
296
+ during: [],
297
+ on_exit: []
298
+ }],
299
+ transitions: [],
300
+ roles: [],
301
+ tags: [{ tag_name: "data" }, { tag_name: "model" }],
302
+ metadata: {
303
+ sourceInterface: interfaceName,
304
+ provenance: {
305
+ frontend: "react-compiler",
306
+ source: "model",
307
+ compiler_version: "2.0.0"
308
+ }
309
+ }
310
+ };
311
+ }
312
+
313
+ // src/route-extractor.ts
314
+ function filePathToUrlPattern(filePath, rootDir) {
315
+ let route = filePath;
316
+ const rootPrefix = rootDir + "/";
317
+ if (route.startsWith(rootPrefix)) {
318
+ route = route.slice(rootPrefix.length);
319
+ }
320
+ route = route.replace(/\.(tsx?|jsx?)$/, "");
321
+ route = route.replace(/\/(page|index)$/, "");
322
+ if (route === "page" || route === "index" || route === "") {
323
+ return "/";
324
+ }
325
+ route = route.replace(/\[([^\]]+)\]/g, ":$1");
326
+ return "/" + route;
327
+ }
328
+ function urlToStateName(urlPath) {
329
+ if (urlPath === "/") return "HOME";
330
+ return urlPath.split("/").filter(Boolean).map((segment) => segment.replace(/^:/, "")).join("_").replace(/([a-z])([A-Z])/g, "$1_$2").replace(/-/g, "_").toUpperCase();
331
+ }
332
+ function extractRouteParams(filePath) {
333
+ const params = [];
334
+ const regex = /\[([^\]]+)\]/g;
335
+ let match;
336
+ while ((match = regex.exec(filePath)) !== null) {
337
+ params.push(match[1]);
338
+ }
339
+ return params;
340
+ }
341
+ function routeDepth(urlPath) {
342
+ if (urlPath === "/") return 0;
343
+ return urlPath.split("/").filter(Boolean).length;
344
+ }
345
+ function isPageFile(filename, pageFileName) {
346
+ if (filename.endsWith("/" + pageFileName)) return true;
347
+ return /\.(tsx|ts|jsx|js)$/.test(filename) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx") && !filename.includes("layout");
348
+ }
349
+ function isLayoutFile(filename, layoutFileName) {
350
+ return filename.endsWith("/" + layoutFileName) || filename.endsWith(layoutFileName);
351
+ }
352
+ function extractRoutes(files, options = {}) {
353
+ const rootDir = options.rootDir || "app";
354
+ const pageFileName = options.pageFileName || "page.tsx";
355
+ const layoutFileName = options.layoutFileName || "layout.tsx";
356
+ const slugPrefix = options.slugPrefix || "app";
357
+ const layouts = [];
358
+ for (const filename of Object.keys(files)) {
359
+ if (isLayoutFile(filename, layoutFileName)) {
360
+ const dir = filename.replace(/\/[^/]+$/, "");
361
+ layouts.push({
362
+ sourceFile: filename,
363
+ directory: dir,
364
+ depth: dir.split("/").filter(Boolean).length
365
+ });
366
+ }
367
+ }
368
+ layouts.sort((a, b) => a.depth - b.depth);
369
+ const routes = [];
370
+ const allParams = /* @__PURE__ */ new Set();
371
+ for (const filename of Object.keys(files)) {
372
+ if (isLayoutFile(filename, layoutFileName)) continue;
373
+ if (!filename.startsWith(rootDir + "/") && !filename.startsWith(rootDir)) continue;
374
+ if (!isPageFile(filename, pageFileName)) continue;
375
+ const urlPath = filePathToUrlPattern(filename, rootDir);
376
+ const stateName = urlToStateName(urlPath);
377
+ const params = extractRouteParams(filename);
378
+ const depth = routeDepth(urlPath);
379
+ const fileDir = filename.replace(/\/[^/]+$/, "");
380
+ let layoutBoundary;
381
+ for (let i = layouts.length - 1; i >= 0; i--) {
382
+ if (fileDir.startsWith(layouts[i].directory)) {
383
+ layoutBoundary = layouts[i].sourceFile;
384
+ break;
385
+ }
386
+ }
387
+ for (const p of params) allParams.add(p);
388
+ routes.push({
389
+ path: urlPath,
390
+ stateName,
391
+ sourceFile: filename,
392
+ params,
393
+ layoutBoundary,
394
+ depth
395
+ });
396
+ }
397
+ routes.sort((a, b) => a.path.localeCompare(b.path));
398
+ const routerIR = buildRouterIR(routes, layouts, Array.from(allParams), slugPrefix);
399
+ return {
400
+ routes,
401
+ layouts,
402
+ routerIR,
403
+ allParams: Array.from(allParams)
404
+ };
405
+ }
406
+ function buildRouterIR(routes, layouts, allParams, slugPrefix) {
407
+ const states = routes.map((route) => ({
408
+ name: route.stateName,
409
+ type: route.path === "/" ? "START" : "REGULAR",
410
+ description: "Route: " + route.path,
411
+ on_enter: [],
412
+ during: [],
413
+ on_exit: []
414
+ }));
415
+ if (states.length > 0 && !states.some((s) => s.type === "START")) {
416
+ states[0].type = "START";
417
+ }
418
+ const transitions = [];
419
+ for (const from of routes) {
420
+ for (const to of routes) {
421
+ if (from.stateName === to.stateName) continue;
422
+ transitions.push({
423
+ name: "nav_" + from.stateName.toLowerCase() + "_to_" + to.stateName.toLowerCase(),
424
+ from: [from.stateName],
425
+ to: to.stateName,
426
+ actions: [],
427
+ conditions: []
428
+ });
429
+ }
430
+ }
431
+ const fields = allParams.map((param) => ({
432
+ name: param,
433
+ type: "text",
434
+ label: param.replace(/([A-Z])/g, " $1").replace(/^./, (c) => c.toUpperCase()),
435
+ required: false,
436
+ default_value: ""
437
+ }));
438
+ fields.push({
439
+ name: "current_route",
440
+ type: "text",
441
+ label: "Current Route",
442
+ required: false,
443
+ default_value: "/"
444
+ });
445
+ return {
446
+ slug: slugPrefix + "-router",
447
+ name: "Router",
448
+ version: "1.0.0",
449
+ description: "Auto-generated router from app/ directory structure",
450
+ category: "router",
451
+ fields,
452
+ states,
453
+ transitions,
454
+ roles: [],
455
+ tags: [{ tag_name: "auto-derived" }, { tag_name: "router" }],
456
+ metadata: {
457
+ runtime: "local",
458
+ routes: routes.map((r) => ({
459
+ state: r.stateName,
460
+ path: r.path,
461
+ params: r.params,
462
+ layoutBoundary: r.layoutBoundary,
463
+ sourceFile: r.sourceFile
464
+ })),
465
+ layoutBoundaries: layouts.map((l) => l.sourceFile),
466
+ provenance: {
467
+ frontend: "react-compiler",
468
+ source: "route-extractor",
469
+ compiler_version: "2.0.0"
470
+ }
471
+ }
472
+ };
473
+ }
474
+
475
+ // src/action-compiler.ts
476
+ import { transformSync as transformSync2 } from "@babel/core";
477
+ function extractActionGroup(filename) {
478
+ const base = filename.split("/").pop() || filename;
479
+ return base.replace(/\.server\.(ts|tsx|js|jsx)$/, "");
480
+ }
481
+ function generateEndpoint(blueprintSlug, group, actionName, apiBasePath) {
482
+ return apiBasePath + "/" + blueprintSlug + "/" + group + "/" + actionName;
483
+ }
484
+ function generateActionId(group, actionName) {
485
+ return group + ":" + actionName;
486
+ }
487
+ function extractFunctionBodies(source) {
488
+ const result = /* @__PURE__ */ new Map();
489
+ const funcHeaderRegex = /export\s+(async\s+)?function\s+(\w+)\s*\(/g;
490
+ let headerMatch;
491
+ while ((headerMatch = funcHeaderRegex.exec(source)) !== null) {
492
+ const funcName = headerMatch[2];
493
+ const startIdx = headerMatch.index;
494
+ let braceStart = source.indexOf("{", headerMatch.index + headerMatch[0].length);
495
+ if (braceStart === -1) continue;
496
+ const signatureSlice = source.slice(headerMatch.index + headerMatch[0].length, braceStart);
497
+ const returnTypeMatch = signatureSlice.match(/\)\s*:\s*([^{]+)/);
498
+ const returnType = returnTypeMatch ? returnTypeMatch[1].trim() : void 0;
499
+ let depth = 0;
500
+ let endIdx = braceStart;
501
+ for (let i = braceStart; i < source.length; i++) {
502
+ if (source[i] === "{") depth++;
503
+ else if (source[i] === "}") {
504
+ depth--;
505
+ if (depth === 0) {
506
+ endIdx = i + 1;
507
+ break;
508
+ }
509
+ }
510
+ }
511
+ const fullText = source.slice(startIdx, endIdx);
512
+ result.set(funcName, { body: fullText, returnType });
513
+ }
514
+ return result;
515
+ }
516
+ function extractActionsViaRegex(source) {
517
+ const actions = [];
518
+ const funcRegex = /(?:\/\*\*([\s\S]*?)\*\/\s*)?export\s+(async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g;
519
+ let match;
520
+ while ((match = funcRegex.exec(source)) !== null) {
521
+ const jsdoc = match[1];
522
+ const isAsync = !!match[2];
523
+ const name = match[3];
524
+ const paramsStr = match[4];
525
+ const params = paramsStr.split(",").map((p) => p.trim().split(":")[0].trim().split("?")[0].trim()).filter(Boolean);
526
+ let description;
527
+ if (jsdoc) {
528
+ const lines = jsdoc.split("\n");
529
+ for (const line of lines) {
530
+ const trimmed = line.replace(/^\s*\*\s?/, "").trim();
531
+ if (trimmed && !trimmed.startsWith("@")) {
532
+ description = trimmed;
533
+ break;
534
+ }
535
+ }
536
+ }
537
+ actions.push({ name, async: isAsync, params, description });
538
+ }
539
+ const arrowRegex = /export\s+const\s+(\w+)\s*=\s*(async\s+)?\([^)]*\)/g;
540
+ while ((match = arrowRegex.exec(source)) !== null) {
541
+ const name = match[1];
542
+ const isAsync = !!match[2];
543
+ const arrowParamMatch = source.slice(match.index).match(/\(([^)]*)\)/);
544
+ const params = arrowParamMatch ? arrowParamMatch[1].split(",").map((p) => p.trim().split(":")[0].trim()).filter(Boolean) : [];
545
+ actions.push({ name, async: isAsync, params });
546
+ }
547
+ return actions;
548
+ }
549
+ function compileActions(files, options = {}) {
550
+ const mode = options.mode || "infer";
551
+ const blueprintSlug = options.blueprintSlug || "app";
552
+ const apiBasePath = options.apiBasePath || "/api/v1/actions";
553
+ const allActions = [];
554
+ const byFile = /* @__PURE__ */ new Map();
555
+ const byGroup = /* @__PURE__ */ new Map();
556
+ const errors = [];
557
+ for (const [filename, source] of Object.entries(files)) {
558
+ const group = extractActionGroup(filename);
559
+ const fileActions = [];
560
+ const bodies = extractFunctionBodies(source);
561
+ try {
562
+ const parserPlugins = filename.endsWith(".tsx") ? ["typescript", "jsx"] : ["typescript"];
563
+ const babelResult = transformSync2(source, {
564
+ filename,
565
+ plugins: [[babelPlugin, { mode }]],
566
+ parserOpts: { plugins: parserPlugins, attachComment: true }
567
+ });
568
+ const ir = babelResult?.metadata?.mindmatrixIR ?? null;
569
+ const meta = ir?.metadata;
570
+ const babelActions = meta?.serverActions;
571
+ if (babelActions && babelActions.length > 0) {
572
+ for (const action of babelActions) {
573
+ const bodyInfo = bodies.get(action.name);
574
+ const reg = {
575
+ name: action.name,
576
+ sourceFile: filename,
577
+ async: action.async,
578
+ params: action.params,
579
+ contextType: action.contextType,
580
+ description: action.description,
581
+ body: bodyInfo?.body,
582
+ returnType: bodyInfo?.returnType,
583
+ endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
584
+ group,
585
+ actionId: generateActionId(group, action.name)
586
+ };
587
+ fileActions.push(reg);
588
+ }
589
+ } else {
590
+ const regexActions = extractActionsViaRegex(source);
591
+ for (const action of regexActions) {
592
+ const bodyInfo = bodies.get(action.name);
593
+ const reg = {
594
+ name: action.name,
595
+ sourceFile: filename,
596
+ async: action.async,
597
+ params: action.params,
598
+ description: action.description,
599
+ body: bodyInfo?.body,
600
+ returnType: bodyInfo?.returnType,
601
+ endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
602
+ group,
603
+ actionId: generateActionId(group, action.name)
604
+ };
605
+ fileActions.push(reg);
606
+ }
607
+ }
608
+ } catch (err) {
609
+ errors.push({ file: filename, message: err.message });
610
+ const regexActions = extractActionsViaRegex(source);
611
+ for (const action of regexActions) {
612
+ const bodyInfo = bodies.get(action.name);
613
+ const reg = {
614
+ name: action.name,
615
+ sourceFile: filename,
616
+ async: action.async,
617
+ params: action.params,
618
+ description: action.description,
619
+ body: bodyInfo?.body,
620
+ returnType: bodyInfo?.returnType,
621
+ endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
622
+ group,
623
+ actionId: generateActionId(group, action.name)
624
+ };
625
+ fileActions.push(reg);
626
+ }
627
+ }
628
+ byFile.set(filename, fileActions);
629
+ if (!byGroup.has(group)) byGroup.set(group, []);
630
+ byGroup.get(group).push(...fileActions);
631
+ allActions.push(...fileActions);
632
+ }
633
+ return { actions: allActions, byFile, byGroup, errors };
634
+ }
635
+ function resolveActionReferences(actionNames, registrations) {
636
+ const result = /* @__PURE__ */ new Map();
637
+ const byName = new Map(registrations.map((r) => [r.name, r]));
638
+ for (const name of actionNames) {
639
+ if (byName.has(name)) {
640
+ result.set(name, byName.get(name));
641
+ continue;
642
+ }
643
+ const colonIdx = name.indexOf(":");
644
+ if (colonIdx > 0) {
645
+ const actionName = name.slice(colonIdx + 1);
646
+ if (byName.has(actionName)) {
647
+ result.set(name, byName.get(actionName));
648
+ continue;
649
+ }
650
+ }
651
+ result.set(name, null);
652
+ }
653
+ return result;
654
+ }
655
+
656
+ // src/babel/extractors/action-extractor.ts
657
+ import { parse } from "@babel/parser";
658
+ import * as t from "@babel/types";
659
+ function extractAction(source, filename) {
660
+ let ast;
661
+ try {
662
+ ast = parse(source, {
663
+ sourceType: "module",
664
+ plugins: ["typescript"],
665
+ strictMode: false
666
+ });
667
+ } catch {
668
+ return null;
669
+ }
670
+ const result = findDefaultExportedFunction(ast);
671
+ if (!result) return null;
672
+ const { name, params, body, returnTypeAnnotation } = result;
673
+ const slug = toKebabCase(name);
674
+ const warnings = [];
675
+ const fields = params.map((p) => paramToField(p));
676
+ const transpileResult = transpileBlock(body, {});
677
+ const bodyExpr = transpileResult.expression;
678
+ if (!transpileResult.pure) {
679
+ warnings.push(`Action '${name}' body contains untranslatable JS \u2014 wrapped in $expr() markers`);
680
+ }
681
+ const states = [
682
+ {
683
+ name: "ready",
684
+ type: "START",
685
+ on_enter: [],
686
+ during: [],
687
+ on_exit: []
688
+ },
689
+ {
690
+ name: "done",
691
+ type: "END",
692
+ on_enter: [],
693
+ during: [],
694
+ on_exit: []
695
+ }
696
+ ];
697
+ const transitions = [
698
+ {
699
+ name: "execute",
700
+ from: ["ready"],
701
+ to: "done",
702
+ actions: [
703
+ {
704
+ id: "body",
705
+ type: "eval",
706
+ mode: "auto",
707
+ config: {
708
+ expression: bodyExpr
709
+ }
710
+ }
711
+ ]
712
+ }
713
+ ];
714
+ const humanName = name.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
715
+ const metadata = {
716
+ source_file: filename,
717
+ source_function: name,
718
+ provenance: {
719
+ frontend: "react-compiler",
720
+ source: "action-extractor",
721
+ compiler_version: "2.0.0"
722
+ }
723
+ };
724
+ if (returnTypeAnnotation) {
725
+ metadata.return_type = returnTypeAnnotation;
726
+ }
727
+ const ir = {
728
+ slug,
729
+ name: humanName,
730
+ version: "0.1.0",
731
+ category: "action",
732
+ fields,
733
+ states,
734
+ transitions,
735
+ roles: [],
736
+ metadata
737
+ };
738
+ return { ir, bodyIsPure: transpileResult.pure, warnings };
739
+ }
740
+ function findDefaultExportedFunction(ast) {
741
+ const topLevelVars = /* @__PURE__ */ new Map();
742
+ for (const node of ast.program.body) {
743
+ if (t.isVariableDeclaration(node)) {
744
+ for (const decl of node.declarations) {
745
+ if (t.isIdentifier(decl.id) && decl.init) {
746
+ if (t.isArrowFunctionExpression(decl.init) || t.isFunctionExpression(decl.init)) {
747
+ topLevelVars.set(decl.id.name, decl.init);
748
+ }
749
+ }
750
+ }
751
+ }
752
+ }
753
+ for (const node of ast.program.body) {
754
+ if (t.isExportDefaultDeclaration(node)) {
755
+ const decl = node.declaration;
756
+ if (t.isFunctionDeclaration(decl) && decl.body) {
757
+ const name = decl.id?.name ?? inferNameFromFile(decl) ?? "action";
758
+ return extractFromFunction(name, decl.params, decl.body, decl.returnType);
759
+ }
760
+ if (t.isArrowFunctionExpression(decl) || t.isFunctionExpression(decl)) {
761
+ const name = t.isFunctionExpression(decl) && decl.id ? decl.id.name : "action";
762
+ const body = ensureBlock(decl.body);
763
+ if (!body) return null;
764
+ return extractFromFunction(name, decl.params, body, decl.returnType);
765
+ }
766
+ if (t.isIdentifier(decl)) {
767
+ const fn = topLevelVars.get(decl.name);
768
+ if (fn) {
769
+ const body = ensureBlock(fn.body);
770
+ if (!body) return null;
771
+ return extractFromFunction(decl.name, fn.params, body, fn.returnType);
772
+ }
773
+ }
774
+ }
775
+ }
776
+ return null;
777
+ }
778
+ function extractFromFunction(name, params, body, returnType) {
779
+ const extracted = params.map((p) => extractParam(p));
780
+ const returnTypeAnnotation = returnType && t.isTSTypeAnnotation(returnType) ? serializeTSType(returnType.typeAnnotation) : void 0;
781
+ return { name, params: extracted, body, returnTypeAnnotation };
782
+ }
783
+ function ensureBlock(body) {
784
+ if (t.isBlockStatement(body)) return body;
785
+ const ret = t.returnStatement(body);
786
+ return t.blockStatement([ret]);
787
+ }
788
+ function inferNameFromFile(_node) {
789
+ return null;
790
+ }
791
+ function extractParam(param) {
792
+ if (t.isIdentifier(param)) {
793
+ return {
794
+ name: param.name,
795
+ typeAnnotation: param.typeAnnotation ? param.typeAnnotation.typeAnnotation : null,
796
+ optional: param.optional ?? false
797
+ };
798
+ }
799
+ if (t.isAssignmentPattern(param)) {
800
+ const inner = param.left;
801
+ if (t.isIdentifier(inner)) {
802
+ return {
803
+ name: inner.name,
804
+ typeAnnotation: inner.typeAnnotation ? inner.typeAnnotation.typeAnnotation : null,
805
+ optional: true,
806
+ defaultValue: param.right
807
+ };
808
+ }
809
+ }
810
+ if (t.isRestElement(param)) {
811
+ const arg = param.argument;
812
+ return {
813
+ name: t.isIdentifier(arg) ? arg.name : "rest",
814
+ typeAnnotation: null,
815
+ optional: true
816
+ };
817
+ }
818
+ return { name: "params", typeAnnotation: null, optional: false };
819
+ }
820
+ function paramToField(param) {
821
+ const fieldType = param.typeAnnotation ? tsTypeToFieldType(param.typeAnnotation) : "text";
822
+ const field = {
823
+ name: toSnakeCase(param.name),
824
+ type: fieldType,
825
+ required: !param.optional
826
+ };
827
+ if (param.defaultValue !== void 0) {
828
+ field.default_value = extractLiteralDefault(param.defaultValue);
829
+ }
830
+ return field;
831
+ }
832
+ function extractLiteralDefault(expr) {
833
+ if (!expr) return void 0;
834
+ if (t.isStringLiteral(expr)) return expr.value;
835
+ if (t.isNumericLiteral(expr)) return expr.value;
836
+ if (t.isBooleanLiteral(expr)) return expr.value;
837
+ if (t.isNullLiteral(expr)) return null;
838
+ if (t.isArrayExpression(expr) && expr.elements.length === 0) return [];
839
+ if (t.isObjectExpression(expr) && expr.properties.length === 0) return {};
840
+ return void 0;
841
+ }
842
+ function tsTypeToFieldType(tsType) {
843
+ if (t.isTSStringKeyword(tsType)) return "text";
844
+ if (t.isTSNumberKeyword(tsType)) return "number";
845
+ if (t.isTSBooleanKeyword(tsType)) return "boolean";
846
+ if (t.isTSObjectKeyword(tsType)) return "json";
847
+ if (t.isTSAnyKeyword(tsType) || t.isTSUnknownKeyword(tsType)) return "json";
848
+ if (t.isTSArrayType(tsType)) return "json";
849
+ if (t.isTSUnionType(tsType)) {
850
+ const nonNullable = tsType.types.filter(
851
+ (t2) => !t.isTSNullKeyword(t2) && !t.isTSUndefinedKeyword(t2)
852
+ );
853
+ if (nonNullable.length === 1) {
854
+ return tsTypeToFieldType(nonNullable[0]);
855
+ }
856
+ if (nonNullable.every((t2) => t.isTSLiteralType(t2) && t.isStringLiteral(t2.literal))) {
857
+ return "select";
858
+ }
859
+ return "text";
860
+ }
861
+ if (t.isTSTypeReference(tsType) && t.isIdentifier(tsType.typeName)) {
862
+ const name = tsType.typeName.name;
863
+ if (name === "Date") return "datetime";
864
+ if (name === "string") return "text";
865
+ if (name === "number") return "number";
866
+ if (name === "boolean") return "boolean";
867
+ }
868
+ return "text";
869
+ }
870
+ function serializeTSType(tsType) {
871
+ if (t.isTSStringKeyword(tsType)) return "string";
872
+ if (t.isTSNumberKeyword(tsType)) return "number";
873
+ if (t.isTSBooleanKeyword(tsType)) return "boolean";
874
+ if (t.isTSVoidKeyword(tsType)) return "void";
875
+ if (t.isTSAnyKeyword(tsType)) return "any";
876
+ if (t.isTSTypeReference(tsType) && t.isIdentifier(tsType.typeName)) {
877
+ return tsType.typeName.name;
878
+ }
879
+ if (t.isTSArrayType(tsType)) return `${serializeTSType(tsType.elementType)}[]`;
880
+ if (t.isTSPromiseType(tsType)) {
881
+ return `Promise<${tsType.typeParameter ? serializeTSType(tsType.typeParameter.params[0]) : "unknown"}>`;
882
+ }
883
+ return "unknown";
884
+ }
885
+ function toKebabCase(name) {
886
+ return name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
887
+ }
888
+ function toSnakeCase(name) {
889
+ return name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
890
+ }
891
+
892
+ // src/incremental-compiler.ts
893
+ function hashContent(content) {
894
+ let hash = 5381;
895
+ for (let i = 0; i < content.length; i++) {
896
+ hash = (hash << 5) + hash + content.charCodeAt(i);
897
+ hash |= 0;
898
+ }
899
+ return (hash >>> 0).toString(36);
900
+ }
901
+ function extractImports(source) {
902
+ const imports = [];
903
+ const regex = /import\s+.*?from\s+['"](\.[^'"]+)['"]/g;
904
+ let match;
905
+ while ((match = regex.exec(source)) !== null) {
906
+ imports.push(match[1]);
907
+ }
908
+ return imports;
909
+ }
910
+ function resolveImport(fromFile, importPath, availableFiles) {
911
+ const parts = fromFile.split("/");
912
+ parts.pop();
913
+ const dir = parts.join("/");
914
+ const segments = (dir ? dir + "/" + importPath : importPath).split("/");
915
+ const resolved = [];
916
+ for (const seg of segments) {
917
+ if (seg === ".") continue;
918
+ if (seg === "..") {
919
+ resolved.pop();
920
+ continue;
921
+ }
922
+ resolved.push(seg);
923
+ }
924
+ const basePath = resolved.join("/");
925
+ const extensions = ["", ".ts", ".tsx", ".js", ".jsx"];
926
+ for (const ext of extensions) {
927
+ const candidate = basePath + ext;
928
+ if (availableFiles.includes(candidate)) {
929
+ return candidate;
930
+ }
931
+ }
932
+ for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
933
+ const candidate = basePath + "/index" + ext;
934
+ if (availableFiles.includes(candidate)) {
935
+ return candidate;
936
+ }
937
+ }
938
+ return null;
939
+ }
940
+ function buildDependencyGraph(files) {
941
+ const availableFiles = Object.keys(files);
942
+ const edges = [];
943
+ const dependents = /* @__PURE__ */ new Map();
944
+ const dependencies = /* @__PURE__ */ new Map();
945
+ for (const filename of availableFiles) {
946
+ dependents.set(filename, /* @__PURE__ */ new Set());
947
+ dependencies.set(filename, /* @__PURE__ */ new Set());
948
+ }
949
+ for (const [filename, source] of Object.entries(files)) {
950
+ const imports = extractImports(source);
951
+ for (const imp of imports) {
952
+ const resolved = resolveImport(filename, imp, availableFiles);
953
+ if (resolved && resolved !== filename) {
954
+ edges.push({ from: filename, to: resolved });
955
+ dependencies.get(filename).add(resolved);
956
+ if (!dependents.has(resolved)) dependents.set(resolved, /* @__PURE__ */ new Set());
957
+ dependents.get(resolved).add(filename);
958
+ }
959
+ }
960
+ }
961
+ return { edges, dependents, dependencies };
962
+ }
963
+ function computeTransitiveDirtySet(directlyChanged, dependents) {
964
+ const dirty = new Set(directlyChanged);
965
+ const queue = [...directlyChanged];
966
+ while (queue.length > 0) {
967
+ const file = queue.shift();
968
+ const deps = dependents.get(file);
969
+ if (!deps) continue;
970
+ for (const dep of deps) {
971
+ if (!dirty.has(dep)) {
972
+ dirty.add(dep);
973
+ queue.push(dep);
974
+ }
975
+ }
976
+ }
977
+ return dirty;
978
+ }
979
+ function topologicalSort(files, dependencies) {
980
+ const inDegree = /* @__PURE__ */ new Map();
981
+ const dependentMap = /* @__PURE__ */ new Map();
982
+ for (const f of files) {
983
+ inDegree.set(f, 0);
984
+ dependentMap.set(f, /* @__PURE__ */ new Set());
985
+ }
986
+ for (const f of files) {
987
+ const deps = dependencies.get(f);
988
+ if (!deps) continue;
989
+ for (const dep of deps) {
990
+ if (files.includes(dep)) {
991
+ inDegree.set(f, (inDegree.get(f) || 0) + 1);
992
+ if (!dependentMap.has(dep)) dependentMap.set(dep, /* @__PURE__ */ new Set());
993
+ dependentMap.get(dep).add(f);
994
+ }
995
+ }
996
+ }
997
+ const queue = [];
998
+ for (const [f, degree] of inDegree) {
999
+ if (degree === 0) queue.push(f);
1000
+ }
1001
+ const order = [];
1002
+ while (queue.length > 0) {
1003
+ const f = queue.shift();
1004
+ order.push(f);
1005
+ for (const dependent of dependentMap.get(f) || []) {
1006
+ const newDegree = (inDegree.get(dependent) || 1) - 1;
1007
+ inDegree.set(dependent, newDegree);
1008
+ if (newDegree === 0) queue.push(dependent);
1009
+ }
1010
+ }
1011
+ for (const f of files) {
1012
+ if (!order.includes(f)) {
1013
+ order.push(f);
1014
+ }
1015
+ }
1016
+ return order;
1017
+ }
1018
+ var IncrementalCache = class {
1019
+ constructor() {
1020
+ this.hashes = /* @__PURE__ */ new Map();
1021
+ this.results = /* @__PURE__ */ new Map();
1022
+ this.lastStats = {
1023
+ totalCached: 0,
1024
+ lastRecompiled: 0,
1025
+ lastCacheHits: 0,
1026
+ lastCompileMs: 0,
1027
+ avgPerFileMs: 0,
1028
+ cacheHitRate: 0
1029
+ };
1030
+ }
1031
+ /**
1032
+ * Determines which files are dirty given current file contents.
1033
+ */
1034
+ detectDirtyFiles(files, dependents) {
1035
+ const currentFiles = new Set(Object.keys(files));
1036
+ const contentChanged = [];
1037
+ const removed = [];
1038
+ const added = [];
1039
+ for (const [filename, source] of Object.entries(files)) {
1040
+ const hash = hashContent(source);
1041
+ const cached = this.hashes.get(filename);
1042
+ if (!cached) {
1043
+ added.push(filename);
1044
+ } else if (cached.hash !== hash) {
1045
+ contentChanged.push(filename);
1046
+ }
1047
+ }
1048
+ for (const cached of this.hashes.keys()) {
1049
+ if (!currentFiles.has(cached)) {
1050
+ removed.push(cached);
1051
+ }
1052
+ }
1053
+ const directlyDirty = /* @__PURE__ */ new Set([...contentChanged, ...added]);
1054
+ let dependencyDirty = [];
1055
+ if (dependents && directlyDirty.size > 0) {
1056
+ const allDirty = computeTransitiveDirtySet(directlyDirty, dependents);
1057
+ dependencyDirty = [...allDirty].filter((f) => !directlyDirty.has(f));
1058
+ }
1059
+ return { contentChanged, dependencyDirty, removed, added };
1060
+ }
1061
+ /**
1062
+ * Updates the cache with a compilation result.
1063
+ */
1064
+ set(filename, source, result) {
1065
+ this.hashes.set(filename, {
1066
+ hash: hashContent(source),
1067
+ compiledAt: Date.now(),
1068
+ size: source.length
1069
+ });
1070
+ this.results.set(filename, result);
1071
+ }
1072
+ /**
1073
+ * Gets a cached result.
1074
+ */
1075
+ get(filename) {
1076
+ return this.results.get(filename);
1077
+ }
1078
+ /**
1079
+ * Checks if a file is in cache and not dirty.
1080
+ */
1081
+ has(filename) {
1082
+ return this.results.has(filename);
1083
+ }
1084
+ /**
1085
+ * Removes a file from cache.
1086
+ */
1087
+ delete(filename) {
1088
+ this.hashes.delete(filename);
1089
+ this.results.delete(filename);
1090
+ }
1091
+ /**
1092
+ * Clears all cached data.
1093
+ */
1094
+ clear() {
1095
+ this.hashes.clear();
1096
+ this.results.clear();
1097
+ }
1098
+ /**
1099
+ * Updates stats after a compilation run.
1100
+ */
1101
+ updateStats(recompiled, cacheHits, compileMs) {
1102
+ const total = recompiled + cacheHits;
1103
+ this.lastStats = {
1104
+ totalCached: this.results.size,
1105
+ lastRecompiled: recompiled,
1106
+ lastCacheHits: cacheHits,
1107
+ lastCompileMs: compileMs,
1108
+ avgPerFileMs: recompiled > 0 ? compileMs / recompiled : 0,
1109
+ cacheHitRate: total > 0 ? cacheHits / total : 0
1110
+ };
1111
+ }
1112
+ /**
1113
+ * Gets compilation statistics.
1114
+ */
1115
+ getStats() {
1116
+ return { ...this.lastStats };
1117
+ }
1118
+ /**
1119
+ * Gets all cached filenames.
1120
+ */
1121
+ getCachedFiles() {
1122
+ return [...this.results.keys()];
1123
+ }
1124
+ /**
1125
+ * Gets all cached results.
1126
+ */
1127
+ getAllResults() {
1128
+ return new Map(this.results);
1129
+ }
1130
+ };
1131
+
1132
+ // src/project-compiler.ts
1133
+ function parseConfig(source) {
1134
+ const config = {};
1135
+ const slugMatch = source.match(/slug:\s*['"]([^'"]+)['"]/);
1136
+ if (slugMatch) config.slug = slugMatch[1];
1137
+ const nameMatch = source.match(/name:\s*['"]([^'"]+)['"]/);
1138
+ if (nameMatch) config.name = nameMatch[1];
1139
+ const versionMatch = source.match(/version:\s*['"]([^'"]+)['"]/);
1140
+ if (versionMatch) config.version = versionMatch[1];
1141
+ const descMatch = source.match(/description:\s*['"]([^'"]+)['"]/);
1142
+ if (descMatch) config.description = descMatch[1];
1143
+ const categoryArrayMatch = source.match(/category:\s*\[([^\]]+)\]/);
1144
+ if (categoryArrayMatch) {
1145
+ const items = categoryArrayMatch[1].match(/['"]([^'"]+)['"]/g);
1146
+ if (items) {
1147
+ config.category = items.map((s) => s.replace(/['"]/g, ""));
1148
+ }
1149
+ } else {
1150
+ const categoryMatch = source.match(/category:\s*['"]([^'"]+)['"]/);
1151
+ if (categoryMatch) config.category = categoryMatch[1];
1152
+ }
1153
+ const modeMatch = source.match(/mode:\s*['"]([^'"]+)['"]/);
1154
+ if (modeMatch && (modeMatch[1] === "strict" || modeMatch[1] === "infer")) {
1155
+ config.mode = modeMatch[1];
1156
+ }
1157
+ return config;
1158
+ }
1159
+ function parseModuleManifest(source) {
1160
+ if (!source.includes("defineBlueprint") && !source.includes("defineModule")) return null;
1161
+ const manifest = {};
1162
+ const stringFields = ["slug", "name", "version", "description", "author", "license", "icon"];
1163
+ for (const field of stringFields) {
1164
+ const match = source.match(new RegExp(`${field}:\\s*['"]([^'"]+)['"]`));
1165
+ if (match) manifest[field] = match[1];
1166
+ }
1167
+ const catArrayMatch = source.match(/category:\s*\[([^\]]+)\]/);
1168
+ if (catArrayMatch) {
1169
+ const items = catArrayMatch[1].match(/['"]([^'"]+)['"]/g);
1170
+ if (items) {
1171
+ manifest.category = items.map((s) => s.replace(/['"]/g, ""));
1172
+ }
1173
+ } else {
1174
+ const catMatch = source.match(/category:\s*['"]([^'"]+)['"]/);
1175
+ if (catMatch) manifest.category = catMatch[1];
1176
+ }
1177
+ const tagsMatch = source.match(/tags:\s*\[([^\]]*)\]/);
1178
+ if (tagsMatch) {
1179
+ manifest.tags = tagsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1180
+ }
1181
+ const modelsMatch = source.match(/models:\s*\[([^\]]*)\]/);
1182
+ if (modelsMatch) {
1183
+ manifest.models = modelsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1184
+ }
1185
+ const capsMatch = source.match(/capabilities:\s*\[([^\]]*)\]/);
1186
+ if (capsMatch) {
1187
+ manifest.capabilities = capsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1188
+ }
1189
+ const routesBlock = extractArrayBlock(source, "routes");
1190
+ if (routesBlock) {
1191
+ const routes = [];
1192
+ const routeRegex = /\{\s*path:\s*['"]([^'"]+)['"][^}]*\}/g;
1193
+ let rm;
1194
+ while ((rm = routeRegex.exec(routesBlock)) !== null) {
1195
+ const entry = { path: rm[1] };
1196
+ const labelMatch = rm[0].match(/label:\s*['"]([^'"]+)['"]/);
1197
+ if (labelMatch) entry.label = labelMatch[1];
1198
+ const groupMatch = rm[0].match(/group:\s*['"]([^'"]+)['"]/);
1199
+ if (groupMatch) entry.group = groupMatch[1];
1200
+ const iconMatch = rm[0].match(/icon:\s*['"]([^'"]+)['"]/);
1201
+ if (iconMatch) entry.icon = iconMatch[1];
1202
+ routes.push(entry);
1203
+ }
1204
+ if (routes.length > 0) manifest.routes = routes;
1205
+ }
1206
+ const actionsBlock = extractArrayBlock(source, "actions");
1207
+ if (actionsBlock) {
1208
+ const actions = [];
1209
+ const actionRegex = /\{\s*id:\s*['"]([^'"]+)['"][^}]*\}/g;
1210
+ let am;
1211
+ while ((am = actionRegex.exec(actionsBlock)) !== null) {
1212
+ const entry = { id: am[1] };
1213
+ const descMatch = am[0].match(/description:\s*['"]([^'"]+)['"]/);
1214
+ if (descMatch) entry.description = descMatch[1];
1215
+ actions.push(entry);
1216
+ }
1217
+ if (actions.length > 0) manifest.actions = actions;
1218
+ }
1219
+ const contribsBlock = extractArrayBlock(source, "contributions");
1220
+ if (contribsBlock) {
1221
+ const contributions = [];
1222
+ const contribRegex = /\{\s*slot:\s*['"]([^'"]+)['"][^}]*\}/g;
1223
+ let cm;
1224
+ while ((cm = contribRegex.exec(contribsBlock)) !== null) {
1225
+ const entry = { slot: cm[1], view: "" };
1226
+ const viewMatch = cm[0].match(/view:\s*['"]([^'"]+)['"]/);
1227
+ if (viewMatch) entry.view = viewMatch[1];
1228
+ const prioMatch = cm[0].match(/priority:\s*(\d+)/);
1229
+ if (prioMatch) entry.priority = parseInt(prioMatch[1], 10);
1230
+ contributions.push(entry);
1231
+ }
1232
+ if (contributions.length > 0) manifest.contributions = contributions;
1233
+ }
1234
+ const depsBlock = extractArrayBlock(source, "dependencies");
1235
+ if (depsBlock) {
1236
+ const dependencies = [];
1237
+ const depRegex = /\{\s*slug:\s*['"]([^'"]+)['"][^}]*\}/g;
1238
+ let dm;
1239
+ while ((dm = depRegex.exec(depsBlock)) !== null) {
1240
+ const entry = { slug: dm[1] };
1241
+ const verMatch = dm[0].match(/version:\s*['"]([^'"]+)['"]/);
1242
+ if (verMatch) entry.version = verMatch[1];
1243
+ const reqMatch = dm[0].match(/required:\s*(true|false)/);
1244
+ if (reqMatch) entry.required = reqMatch[1] === "true";
1245
+ const prefixMatch = dm[0].match(/prefix:\s*['"]([^'"]+)['"]/);
1246
+ if (prefixMatch) entry.routeConfig = { prefix: prefixMatch[1] };
1247
+ dependencies.push(entry);
1248
+ }
1249
+ if (dependencies.length > 0) manifest.dependencies = dependencies;
1250
+ }
1251
+ const hasRichFields = manifest.routes || manifest.actions || manifest.contributions || manifest.capabilities || manifest.dependencies;
1252
+ return hasRichFields ? manifest : null;
1253
+ }
1254
+ function parseDependencyRouteConfigs(files) {
1255
+ const result = /* @__PURE__ */ new Map();
1256
+ let configSource;
1257
+ for (const [filename, source] of Object.entries(files)) {
1258
+ if (isConfigFile(filename)) {
1259
+ configSource = source;
1260
+ break;
1261
+ }
1262
+ }
1263
+ if (!configSource) return result;
1264
+ const depsBlock = extractArrayBlock(configSource, "dependencies");
1265
+ if (!depsBlock) return result;
1266
+ const depObjects = extractNestedObjects(depsBlock);
1267
+ for (const depSrc of depObjects) {
1268
+ const slugMatch = depSrc.match(/slug:\s*['"]([^'"]+)['"]/);
1269
+ if (!slugMatch) continue;
1270
+ const slug = slugMatch[1];
1271
+ const rcBlock = extractObjectBlock(depSrc, "routeConfig");
1272
+ if (!rcBlock) continue;
1273
+ const entry = {};
1274
+ const prefixMatch = rcBlock.match(/prefix:\s*['"]([^'"]+)['"]/);
1275
+ if (prefixMatch) entry.prefix = prefixMatch[1];
1276
+ const routesBlock = extractObjectBlock(rcBlock, "routes");
1277
+ if (routesBlock) {
1278
+ const overrides = {};
1279
+ const overrideRegex = /['"]([^'"]+)['"]\s*:\s*(?:['"]([^'"]+)['"]|(false))/g;
1280
+ let om;
1281
+ while ((om = overrideRegex.exec(routesBlock)) !== null) {
1282
+ overrides[om[1]] = om[3] === "false" ? false : om[2];
1283
+ }
1284
+ if (Object.keys(overrides).length > 0) entry.routes = overrides;
1285
+ }
1286
+ result.set(slug, entry);
1287
+ }
1288
+ return result;
1289
+ }
1290
+ function extractNestedObjects(source) {
1291
+ const objects = [];
1292
+ let depth = 0;
1293
+ let start = -1;
1294
+ for (let i = 0; i < source.length; i++) {
1295
+ if (source[i] === "{") {
1296
+ if (depth === 0) start = i;
1297
+ depth++;
1298
+ } else if (source[i] === "}") {
1299
+ depth--;
1300
+ if (depth === 0 && start >= 0) {
1301
+ objects.push(source.slice(start, i + 1));
1302
+ start = -1;
1303
+ }
1304
+ }
1305
+ }
1306
+ return objects;
1307
+ }
1308
+ function extractObjectBlock(source, fieldName) {
1309
+ const pattern = new RegExp(`${fieldName}:\\s*\\{`);
1310
+ const match = pattern.exec(source);
1311
+ if (!match) return null;
1312
+ let depth = 1;
1313
+ const startIdx = match.index + match[0].length;
1314
+ for (let i = startIdx; i < source.length; i++) {
1315
+ if (source[i] === "{") depth++;
1316
+ else if (source[i] === "}") {
1317
+ depth--;
1318
+ if (depth === 0) return source.slice(startIdx, i);
1319
+ }
1320
+ }
1321
+ return null;
1322
+ }
1323
+ function extractArrayBlock(source, fieldName) {
1324
+ const startPattern = new RegExp(`${fieldName}:\\s*\\[`);
1325
+ const match = startPattern.exec(source);
1326
+ if (!match) return null;
1327
+ let depth = 1;
1328
+ const startIdx = match.index + match[0].length;
1329
+ for (let i = startIdx; i < source.length; i++) {
1330
+ if (source[i] === "[") depth++;
1331
+ else if (source[i] === "]") {
1332
+ depth--;
1333
+ if (depth === 0) return source.slice(startIdx, i);
1334
+ }
1335
+ }
1336
+ return null;
1337
+ }
1338
+ function isWorkflowFile(filename) {
1339
+ return /\.workflow\.(tsx?|jsx?)$/.test(filename);
1340
+ }
1341
+ function isModelFile(filename) {
1342
+ return /models\/.*\.(ts|tsx)$/.test(filename) && !filename.endsWith(".test.ts");
1343
+ }
1344
+ function isServerActionFile(filename) {
1345
+ return /\.server\.(ts|tsx)$/.test(filename);
1346
+ }
1347
+ function isActionFile(filename) {
1348
+ return /^actions\/.*\.(ts|tsx)$/.test(filename) && !filename.endsWith(".server.ts") && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx");
1349
+ }
1350
+ function isComponentFile(filename) {
1351
+ return /components\/.*\.(tsx?|jsx?)$/.test(filename) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx");
1352
+ }
1353
+ function isPageFile2(filename) {
1354
+ return (/pages\/.*\.(tsx?|jsx?)$/.test(filename) || /app\/.*\.(tsx?|jsx?)$/.test(filename)) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx") && !filename.includes("layout");
1355
+ }
1356
+ function isAppDirFile(filename) {
1357
+ return /^app\//.test(filename);
1358
+ }
1359
+ function isConfigFile(filename) {
1360
+ return /mm\.config\.(ts|tsx|js)$/.test(filename);
1361
+ }
1362
+ function isModuleManifestFile(filename) {
1363
+ return /mm\.module\.(ts|tsx|js)$/.test(filename);
1364
+ }
1365
+ function isCompilableFile(filename) {
1366
+ return isWorkflowFile(filename) || isModelFile(filename) || isServerActionFile(filename) || isActionFile(filename) || isPageFile2(filename) || isComponentFile(filename);
1367
+ }
1368
+ function compileFile(filename, source, mode) {
1369
+ const errors = [];
1370
+ try {
1371
+ const parserPlugins = filename.endsWith(".tsx") || filename.endsWith(".jsx") ? ["typescript", "jsx"] : ["typescript"];
1372
+ const result = transformSync3(source, {
1373
+ filename,
1374
+ plugins: [[babelPlugin, { mode }]],
1375
+ parserOpts: { plugins: parserPlugins, attachComment: true }
1376
+ });
1377
+ const ir = result?.metadata?.mindmatrixIR ?? null;
1378
+ if (ir?.metadata) {
1379
+ const meta = ir.metadata;
1380
+ const fileErrors = meta.errors;
1381
+ const fileWarnings = meta.warnings;
1382
+ if (fileErrors) {
1383
+ for (const e of fileErrors) {
1384
+ errors.push({
1385
+ file: filename,
1386
+ message: e.message,
1387
+ line: e.line,
1388
+ column: e.column,
1389
+ endLine: e.line,
1390
+ endColumn: e.column !== void 0 ? e.column + 10 : void 0,
1391
+ severity: "error"
1392
+ });
1393
+ }
1394
+ }
1395
+ if (fileWarnings) {
1396
+ for (const w of fileWarnings) {
1397
+ errors.push({
1398
+ file: filename,
1399
+ message: w.message,
1400
+ line: w.line,
1401
+ column: w.column,
1402
+ endLine: w.line,
1403
+ endColumn: w.column !== void 0 ? w.column + 10 : void 0,
1404
+ severity: "warning"
1405
+ });
1406
+ }
1407
+ }
1408
+ }
1409
+ return { ir, errors };
1410
+ } catch (err) {
1411
+ const errMsg = err.message;
1412
+ const locMatch = errMsg.match(/\((\d+):(\d+)\)/);
1413
+ const line = locMatch ? parseInt(locMatch[1], 10) : void 0;
1414
+ const column = locMatch ? parseInt(locMatch[2], 10) : void 0;
1415
+ errors.push({
1416
+ file: filename,
1417
+ message: "Compilation failed: " + errMsg,
1418
+ line,
1419
+ column,
1420
+ endLine: line,
1421
+ endColumn: column !== void 0 ? column + 1 : void 0,
1422
+ severity: "error"
1423
+ });
1424
+ return { ir: null, errors };
1425
+ }
1426
+ }
1427
+ function deduplicateActions(actions) {
1428
+ const seen = /* @__PURE__ */ new Set();
1429
+ const result = [];
1430
+ for (const action of actions) {
1431
+ const key = JSON.stringify(action);
1432
+ if (!seen.has(key)) {
1433
+ seen.add(key);
1434
+ result.push(action);
1435
+ }
1436
+ }
1437
+ return result;
1438
+ }
1439
+ function mergeIRs(irs, config) {
1440
+ if (irs.length === 0) {
1441
+ return createEmptyIR(config);
1442
+ }
1443
+ if (irs.length === 1) {
1444
+ return applyConfig(irs[0], config);
1445
+ }
1446
+ const fieldMap = /* @__PURE__ */ new Map();
1447
+ for (const ir of irs) {
1448
+ for (const field of ir.fields) {
1449
+ if (!fieldMap.has(field.name)) {
1450
+ fieldMap.set(field.name, field);
1451
+ } else {
1452
+ const existing = fieldMap.get(field.name);
1453
+ if (field.required && !existing.required) {
1454
+ existing.required = true;
1455
+ }
1456
+ if (field.default_value != null && field.default_value !== "" && field.default_value !== 0 && field.default_value !== false) {
1457
+ if (existing.default_value == null || existing.default_value === "" || existing.default_value === 0 || existing.default_value === false) {
1458
+ existing.default_value = field.default_value;
1459
+ }
1460
+ }
1461
+ }
1462
+ }
1463
+ }
1464
+ const stateMap = /* @__PURE__ */ new Map();
1465
+ for (const ir of irs) {
1466
+ for (const state of ir.states) {
1467
+ if (stateMap.has(state.name)) {
1468
+ const existing = stateMap.get(state.name);
1469
+ existing.on_enter = deduplicateActions([...existing.on_enter, ...state.on_enter]);
1470
+ existing.on_exit = deduplicateActions([...existing.on_exit, ...state.on_exit]);
1471
+ existing.during = deduplicateActions([...existing.during, ...state.during]);
1472
+ if (state.on_event) {
1473
+ existing.on_event = [...existing.on_event || [], ...state.on_event];
1474
+ }
1475
+ } else {
1476
+ stateMap.set(state.name, { ...state });
1477
+ }
1478
+ }
1479
+ }
1480
+ const transitionMap = /* @__PURE__ */ new Map();
1481
+ for (const ir of irs) {
1482
+ for (const transition of ir.transitions) {
1483
+ if (!transitionMap.has(transition.name)) {
1484
+ transitionMap.set(transition.name, transition);
1485
+ }
1486
+ }
1487
+ }
1488
+ const events = [];
1489
+ for (const ir of irs) {
1490
+ if (ir.on_event) {
1491
+ events.push(...ir.on_event);
1492
+ }
1493
+ }
1494
+ const viewTrees = [];
1495
+ for (const ir of irs) {
1496
+ const views = ir.views;
1497
+ if (views?.default) {
1498
+ viewTrees.push(views.default);
1499
+ }
1500
+ }
1501
+ const extensions = {};
1502
+ for (const ir of irs) {
1503
+ if (ir.extensions) {
1504
+ for (const [key, islands] of Object.entries(ir.extensions)) {
1505
+ if (!extensions[key]) extensions[key] = [];
1506
+ extensions[key].push(...islands);
1507
+ }
1508
+ }
1509
+ }
1510
+ const metadata = {};
1511
+ for (const ir of irs) {
1512
+ if (ir.metadata) {
1513
+ for (const [key, value] of Object.entries(ir.metadata)) {
1514
+ if (key === "errors" || key === "warnings") continue;
1515
+ metadata[key] = value;
1516
+ }
1517
+ }
1518
+ }
1519
+ const roleMap = /* @__PURE__ */ new Map();
1520
+ for (const ir of irs) {
1521
+ for (const role of ir.roles) {
1522
+ if (!roleMap.has(role.name)) {
1523
+ roleMap.set(role.name, role);
1524
+ }
1525
+ }
1526
+ }
1527
+ const base = irs[0];
1528
+ const merged = {
1529
+ slug: base.slug,
1530
+ name: base.name,
1531
+ version: base.version,
1532
+ description: base.description,
1533
+ category: base.category,
1534
+ fields: Array.from(fieldMap.values()),
1535
+ states: Array.from(stateMap.values()),
1536
+ transitions: Array.from(transitionMap.values()),
1537
+ roles: Array.from(roleMap.values()),
1538
+ tags: base.tags,
1539
+ metadata
1540
+ };
1541
+ if (events.length > 0) {
1542
+ merged.on_event = events;
1543
+ }
1544
+ if (Object.keys(extensions).length > 0) {
1545
+ merged.extensions = extensions;
1546
+ }
1547
+ if (viewTrees.length === 1) {
1548
+ merged.views = { default: viewTrees[0] };
1549
+ } else if (viewTrees.length > 1) {
1550
+ const rootView = {
1551
+ id: "project-root",
1552
+ component: "Stack",
1553
+ children: viewTrees
1554
+ };
1555
+ merged.views = { default: rootView };
1556
+ }
1557
+ return applyConfig(merged, config);
1558
+ }
1559
+ function applyConfig(ir, config) {
1560
+ if (config.slug) ir.slug = config.slug;
1561
+ if (config.name) ir.name = config.name;
1562
+ if (config.version) ir.version = config.version;
1563
+ if (config.description !== void 0) ir.description = config.description;
1564
+ if (config.category) ir.category = config.category;
1565
+ if (!ir.metadata) ir.metadata = {};
1566
+ ir.metadata.stable_id = "def-" + ir.slug;
1567
+ ir.metadata.provenance = {
1568
+ frontend: "react-compiler",
1569
+ source: "project",
1570
+ compiler_version: "2.0.0"
1571
+ };
1572
+ return ir;
1573
+ }
1574
+ function createEmptyIR(config) {
1575
+ return {
1576
+ slug: config.slug || "project",
1577
+ name: config.name || "Project",
1578
+ version: config.version || "0.1.0",
1579
+ description: config.description,
1580
+ category: config.category || "workflow",
1581
+ fields: [],
1582
+ states: [{
1583
+ name: "draft",
1584
+ type: "START",
1585
+ on_enter: [],
1586
+ during: [],
1587
+ on_exit: []
1588
+ }],
1589
+ transitions: [],
1590
+ roles: [],
1591
+ tags: [],
1592
+ metadata: {
1593
+ stable_id: "def-" + (config.slug || "project"),
1594
+ provenance: {
1595
+ frontend: "react-compiler",
1596
+ source: "project",
1597
+ compiler_version: "2.0.0"
1598
+ }
1599
+ }
1600
+ };
1601
+ }
1602
+ function resolveImportLinks(files, compilableFiles) {
1603
+ const links = [];
1604
+ for (const filename of compilableFiles) {
1605
+ const source = files[filename];
1606
+ const importRegex = /import\s+(?:type\s+)?(?:\{([^}]+)\}|(\w+))\s+from\s+['"](\.[^'"]+)['"]/g;
1607
+ let match;
1608
+ while ((match = importRegex.exec(source)) !== null) {
1609
+ const namedImports = match[1];
1610
+ const defaultImport = match[2];
1611
+ const importPath = match[3];
1612
+ const resolved = resolveImport(filename, importPath, Object.keys(files));
1613
+ if (!resolved || resolved === filename) continue;
1614
+ let linkType = "unknown";
1615
+ if (isModelFile(resolved)) {
1616
+ linkType = "data-source";
1617
+ } else if (isServerActionFile(resolved)) {
1618
+ linkType = "action";
1619
+ } else if (/components\//.test(resolved)) {
1620
+ linkType = "component";
1621
+ }
1622
+ const symbols = [];
1623
+ if (namedImports) {
1624
+ symbols.push(...namedImports.split(",").map((s) => s.trim().split(" as ")[0].trim()).filter(Boolean));
1625
+ }
1626
+ if (defaultImport) {
1627
+ symbols.push(defaultImport);
1628
+ }
1629
+ links.push({
1630
+ fromFile: filename,
1631
+ toFile: resolved,
1632
+ linkType,
1633
+ symbols
1634
+ });
1635
+ }
1636
+ }
1637
+ return links;
1638
+ }
1639
+ function resolveCompilationOrder(files, compilableFiles) {
1640
+ const { dependencies } = buildDependencyGraph(files);
1641
+ return topologicalSort(compilableFiles, dependencies);
1642
+ }
1643
+ function extractComponentProps(source) {
1644
+ const match = source.match(/function\s+\w+\s*\(\s*\{([^}]+)\}/);
1645
+ if (!match) return [];
1646
+ return match[1].split(",").map((p) => p.trim().split(/[\s=:]/)[0].replace(/^\.{3}/, "").trim()).filter(Boolean);
1647
+ }
1648
+ function buildComposedResult(files, fileIRs, config, errors, warnings, options = {}) {
1649
+ const usePhase2 = options.usePhase2Modules !== false;
1650
+ const workflowIRs = [];
1651
+ const modelIRs = [];
1652
+ const actionDefinitionIRs = [];
1653
+ const serverActionEntries = [];
1654
+ let modelResults;
1655
+ let actionResult;
1656
+ let routeResult;
1657
+ const componentDefinitions = {};
1658
+ for (const [filename, ir] of Object.entries(fileIRs)) {
1659
+ if (isWorkflowFile(filename)) {
1660
+ workflowIRs.push(ir);
1661
+ } else if (isModelFile(filename)) {
1662
+ if (!ir.category || ir.category === "workflow") {
1663
+ ir.category = "data";
1664
+ }
1665
+ modelIRs.push(ir);
1666
+ } else if (isServerActionFile(filename)) {
1667
+ const meta = ir.metadata;
1668
+ const actions = meta?.serverActions;
1669
+ if (actions) {
1670
+ for (const action of actions) {
1671
+ serverActionEntries.push({
1672
+ name: action.name,
1673
+ sourceFile: filename,
1674
+ async: action.async,
1675
+ params: action.params,
1676
+ description: action.description
1677
+ });
1678
+ }
1679
+ }
1680
+ } else if (isComponentFile(filename)) {
1681
+ const views = ir.views;
1682
+ const experience = views?.default ?? ir.experience;
1683
+ if (experience) {
1684
+ const baseName = filename.split("/").pop()?.replace(/\.(tsx?|jsx?)$/, "") || "Component";
1685
+ const source = files[filename];
1686
+ const props = source ? extractComponentProps(source) : [];
1687
+ componentDefinitions[baseName] = { experience, props };
1688
+ }
1689
+ }
1690
+ }
1691
+ for (const [filename, source] of Object.entries(files)) {
1692
+ if (isActionFile(filename)) {
1693
+ const result = extractAction(source, filename);
1694
+ if (result) {
1695
+ actionDefinitionIRs.push(result.ir);
1696
+ for (const w of result.warnings) {
1697
+ warnings.push({ file: filename, message: w, severity: "warning" });
1698
+ }
1699
+ }
1700
+ }
1701
+ }
1702
+ if (usePhase2) {
1703
+ const modelFiles = {};
1704
+ for (const [filename, source] of Object.entries(files)) {
1705
+ if (isModelFile(filename)) {
1706
+ modelFiles[filename] = source;
1707
+ }
1708
+ }
1709
+ if (Object.keys(modelFiles).length > 0) {
1710
+ modelResults = compileModels(modelFiles, { mode: options.mode || "infer" });
1711
+ modelIRs.length = 0;
1712
+ for (const [, result] of modelResults) {
1713
+ modelIRs.push(result.ir);
1714
+ }
1715
+ }
1716
+ const actionFiles = {};
1717
+ for (const [filename, source] of Object.entries(files)) {
1718
+ if (isServerActionFile(filename)) {
1719
+ actionFiles[filename] = source;
1720
+ }
1721
+ }
1722
+ if (Object.keys(actionFiles).length > 0) {
1723
+ actionResult = compileActions(actionFiles, {
1724
+ mode: options.mode || "infer",
1725
+ blueprintSlug: config.slug || "app"
1726
+ });
1727
+ serverActionEntries.length = 0;
1728
+ for (const reg of actionResult.actions) {
1729
+ serverActionEntries.push({
1730
+ name: reg.name,
1731
+ sourceFile: reg.sourceFile,
1732
+ async: reg.async,
1733
+ params: reg.params,
1734
+ description: reg.description,
1735
+ body: reg.body,
1736
+ returnType: reg.returnType
1737
+ });
1738
+ }
1739
+ }
1740
+ const appFiles = {};
1741
+ for (const [filename, source] of Object.entries(files)) {
1742
+ if (isAppDirFile(filename) || isPageFile2(filename)) {
1743
+ appFiles[filename] = source;
1744
+ }
1745
+ }
1746
+ if (Object.keys(appFiles).length > 0) {
1747
+ routeResult = extractRoutes(appFiles, {
1748
+ slugPrefix: config.slug || "app"
1749
+ });
1750
+ }
1751
+ }
1752
+ const childDefinitions = [
1753
+ ...workflowIRs,
1754
+ ...modelIRs,
1755
+ ...actionDefinitionIRs
1756
+ ];
1757
+ let routeTable = [];
1758
+ if (routeResult) {
1759
+ routeTable = routeResult.routes.map((r) => ({
1760
+ path: r.path,
1761
+ stateName: r.stateName,
1762
+ sourceFile: r.sourceFile,
1763
+ params: r.params
1764
+ }));
1765
+ childDefinitions.push(routeResult.routerIR);
1766
+ } else {
1767
+ const pageFiles = [];
1768
+ for (const filename of Object.keys(files)) {
1769
+ if (isPageFile2(filename)) {
1770
+ pageFiles.push({
1771
+ relativePath: filename,
1772
+ absolutePath: filename
1773
+ });
1774
+ const routePath = filename.replace(/^pages\//, "").replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
1775
+ const stateName = pathToStateName(routePath);
1776
+ const urlPattern = pathToUrlPattern(filename, filename.split("/").pop() || "page.tsx");
1777
+ const params = extractParams(filename);
1778
+ routeTable.push({
1779
+ path: urlPattern,
1780
+ stateName,
1781
+ sourceFile: filename,
1782
+ params
1783
+ });
1784
+ }
1785
+ }
1786
+ if (pageFiles.length > 0) {
1787
+ const routerWorkflow = extractRouterWorkflow(pageFiles, {
1788
+ slug: config.slug ? config.slug + "-router" : "app-router",
1789
+ pageFileName: pageFiles[0]?.relativePath.split("/").pop() || "page.tsx"
1790
+ });
1791
+ childDefinitions.push(routerWorkflow);
1792
+ }
1793
+ }
1794
+ if (options.resolvedModules && options.resolvedModules.length > 0) {
1795
+ const depConfigs = parseDependencyRouteConfigs(files);
1796
+ for (const mod of options.resolvedModules) {
1797
+ const depConfig = depConfigs.get(mod.slug);
1798
+ const prefix = depConfig?.prefix ?? `/${mod.slug.replace(/^mod-/, "")}`;
1799
+ const routeOverrides = depConfig?.routes;
1800
+ for (const modRoute of mod.routeTable) {
1801
+ if (routeOverrides) {
1802
+ const override = routeOverrides[modRoute.path];
1803
+ if (override === false) continue;
1804
+ if (typeof override === "string") {
1805
+ const existingPaths2 = new Set(routeTable.map((r) => r.path));
1806
+ if (!existingPaths2.has(override)) {
1807
+ routeTable.push({
1808
+ path: override,
1809
+ stateName: `MOD_${mod.slug.replace(/-/g, "_").toUpperCase()}_${modRoute.stateName}`,
1810
+ sourceFile: modRoute.sourceFile,
1811
+ params: modRoute.params,
1812
+ moduleSlug: mod.slug
1813
+ });
1814
+ }
1815
+ continue;
1816
+ }
1817
+ }
1818
+ const prefixedPath = prefix + (modRoute.path === "/" ? "" : modRoute.path);
1819
+ const existingPaths = new Set(routeTable.map((r) => r.path));
1820
+ if (!existingPaths.has(prefixedPath)) {
1821
+ routeTable.push({
1822
+ path: prefixedPath,
1823
+ stateName: `MOD_${mod.slug.replace(/-/g, "_").toUpperCase()}_${modRoute.stateName}`,
1824
+ sourceFile: modRoute.sourceFile,
1825
+ params: modRoute.params,
1826
+ moduleSlug: mod.slug
1827
+ });
1828
+ }
1829
+ }
1830
+ }
1831
+ }
1832
+ const allIRs = Object.values(fileIRs);
1833
+ const parentIR = mergeIRs(allIRs, config);
1834
+ if (!parentIR.metadata) parentIR.metadata = {};
1835
+ const parentMeta = parentIR.metadata;
1836
+ parentMeta.childSlugs = childDefinitions.map((c) => c.slug);
1837
+ parentMeta.serverActions = serverActionEntries;
1838
+ if (routeTable.length > 0) {
1839
+ parentMeta.routeTable = routeTable;
1840
+ }
1841
+ parentMeta.composition = {
1842
+ workflowCount: workflowIRs.length,
1843
+ modelCount: modelIRs.length,
1844
+ actionCount: actionDefinitionIRs.length,
1845
+ serverActionCount: serverActionEntries.length,
1846
+ routeCount: routeTable.length,
1847
+ componentCount: Object.keys(componentDefinitions).length,
1848
+ totalFiles: Object.keys(files).length
1849
+ };
1850
+ if (Object.keys(componentDefinitions).length > 0) {
1851
+ parentMeta.componentDefinitions = componentDefinitions;
1852
+ }
1853
+ if (actionResult) {
1854
+ parentMeta.actionEndpoints = actionResult.actions.map((a) => ({
1855
+ actionId: a.actionId,
1856
+ endpoint: a.endpoint,
1857
+ group: a.group
1858
+ }));
1859
+ }
1860
+ for (const [filename, source] of Object.entries(files)) {
1861
+ if (isConfigFile(filename) || isModuleManifestFile(filename)) {
1862
+ const manifest2 = parseModuleManifest(source);
1863
+ if (manifest2) {
1864
+ parentMeta.module_manifest = manifest2;
1865
+ }
1866
+ break;
1867
+ }
1868
+ }
1869
+ const manifest = parentMeta.module_manifest;
1870
+ if (manifest?.dependencies) {
1871
+ parentMeta.module_dependencies = manifest.dependencies;
1872
+ }
1873
+ if (manifest?.contributions) {
1874
+ parentMeta.slot_contributions = manifest.contributions;
1875
+ }
1876
+ const compilableFiles = Object.keys(files).filter(isCompilableFile);
1877
+ const importLinks = resolveImportLinks(files, compilableFiles);
1878
+ const pageExperiences = {};
1879
+ for (const [filename, ir] of Object.entries(fileIRs)) {
1880
+ if (isPageFile2(filename)) {
1881
+ const views = ir.views;
1882
+ if (views?.default) {
1883
+ pageExperiences[filename] = views.default;
1884
+ }
1885
+ }
1886
+ }
1887
+ const parentViews = parentIR.views;
1888
+ if (Object.keys(pageExperiences).length > 0) {
1889
+ const pageEntries = Object.entries(pageExperiences);
1890
+ const routeNodes = pageEntries.map(([pagePath, pageTree], i) => {
1891
+ const routePath = "/" + pagePath.replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
1892
+ return {
1893
+ id: `route-${i}`,
1894
+ component: "Route",
1895
+ config: { path: routePath },
1896
+ children: [pageTree]
1897
+ };
1898
+ });
1899
+ const navLinks = pageEntries.map(([pagePath], i) => {
1900
+ const routePath = "/" + pagePath.replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
1901
+ const segments = routePath.split("/").filter(Boolean);
1902
+ const label = segments[segments.length - 1]?.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || "Home";
1903
+ return {
1904
+ id: `nav-link-${i}`,
1905
+ component: "NavLink",
1906
+ config: { to: routePath, label }
1907
+ };
1908
+ });
1909
+ routeNodes.unshift({
1910
+ id: "route-index",
1911
+ component: "Route",
1912
+ config: { path: "/", exact: true },
1913
+ children: [pageEntries[0][1]]
1914
+ });
1915
+ const moduleRoutes = routeTable.filter((r) => r.moduleSlug);
1916
+ for (let mi = 0; mi < moduleRoutes.length; mi++) {
1917
+ const mr = moduleRoutes[mi];
1918
+ routeNodes.push({
1919
+ id: `mod-route-${mi}`,
1920
+ component: "Route",
1921
+ config: { path: mr.path },
1922
+ children: [{
1923
+ id: `mod-route-${mi}-placeholder`,
1924
+ component: "ModuleView",
1925
+ config: { moduleSlug: mr.moduleSlug, sourceFile: mr.sourceFile, path: mr.path }
1926
+ }]
1927
+ });
1928
+ const segments = mr.path.split("/").filter(Boolean);
1929
+ const label = segments[segments.length - 1]?.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || mr.moduleSlug;
1930
+ navLinks.push({
1931
+ id: `mod-nav-${mi}`,
1932
+ component: "NavLink",
1933
+ config: { to: mr.path, label, moduleSlug: mr.moduleSlug }
1934
+ });
1935
+ }
1936
+ const composedExperience = {
1937
+ id: "blueprint-root",
1938
+ component: "Stack",
1939
+ className: "h-full flex flex-col",
1940
+ children: [
1941
+ // Nav bar (always visible, not inside Router so it always renders)
1942
+ {
1943
+ id: "nav-bar",
1944
+ component: "Row",
1945
+ config: { gap: 1 },
1946
+ className: "px-4 py-2 border-b border-border bg-background/95 backdrop-blur-sm sticky top-0 z-10 flex-wrap",
1947
+ children: navLinks
1948
+ },
1949
+ // Router with page routes
1950
+ {
1951
+ id: "page-router",
1952
+ component: "Router",
1953
+ className: "flex-1 min-h-0 overflow-y-auto",
1954
+ children: routeNodes.map((r) => ({
1955
+ ...r,
1956
+ children: r.children?.map((child) => ({
1957
+ ...child,
1958
+ id: child.id || r.id + "-content",
1959
+ component: child.component || "Stack",
1960
+ config: { ...child.config, gap: 4, padding: 4 }
1961
+ }))
1962
+ }))
1963
+ }
1964
+ ]
1965
+ };
1966
+ parentIR.experience = composedExperience;
1967
+ const slug = config.slug || parentIR.slug || "blueprint";
1968
+ parentMeta.blueprint_manifest = {
1969
+ routes: [
1970
+ { path: `${slug}/*`, node: slug, label: config.name || parentIR.name || slug }
1971
+ ],
1972
+ config: {
1973
+ full_bleed: true
1974
+ }
1975
+ };
1976
+ } else if (parentViews?.default) {
1977
+ parentIR.experience = parentViews.default;
1978
+ }
1979
+ for (const childDef of childDefinitions) {
1980
+ for (const [filename, ir] of Object.entries(fileIRs)) {
1981
+ if (ir.slug === childDef.slug || isWorkflowFile(filename) && ir.slug === childDef.slug) {
1982
+ const views = ir.views;
1983
+ if (views?.default) {
1984
+ childDef.experience = views.default;
1985
+ }
1986
+ break;
1987
+ }
1988
+ }
1989
+ }
1990
+ return {
1991
+ ir: parentIR,
1992
+ childDefinitions,
1993
+ fileIRs,
1994
+ routeTable,
1995
+ serverActions: serverActionEntries,
1996
+ errors,
1997
+ warnings,
1998
+ pageExperiences,
1999
+ componentDefinitions,
2000
+ importLinks,
2001
+ modelResults,
2002
+ actionResult,
2003
+ routeResult
2004
+ };
2005
+ }
2006
+ function compileProject(files, options = {}) {
2007
+ const allErrors = [];
2008
+ const allWarnings = [];
2009
+ const fileIRs = {};
2010
+ let config = {};
2011
+ for (const [filename, source] of Object.entries(files)) {
2012
+ if (isConfigFile(filename)) {
2013
+ config = parseConfig(source);
2014
+ break;
2015
+ }
2016
+ }
2017
+ const mode = config.mode || options.mode || "infer";
2018
+ const compilableFiles = Object.keys(files).filter(isCompilableFile);
2019
+ const orderedFiles = resolveCompilationOrder(files, compilableFiles);
2020
+ for (const filename of orderedFiles) {
2021
+ const source = files[filename];
2022
+ const { ir, errors } = compileFile(filename, source, mode);
2023
+ for (const e of errors) {
2024
+ if (e.severity === "error") allErrors.push(e);
2025
+ else allWarnings.push(e);
2026
+ }
2027
+ if (ir) {
2028
+ fileIRs[filename] = ir;
2029
+ }
2030
+ }
2031
+ return buildComposedResult(files, fileIRs, config, allErrors, allWarnings, {
2032
+ usePhase2Modules: options.usePhase2Modules,
2033
+ mode,
2034
+ resolvedModules: options.resolvedModules
2035
+ });
2036
+ }
2037
+ var IncrementalProjectCompiler = class {
2038
+ constructor() {
2039
+ this.lastConfig = {};
2040
+ this.cache = new IncrementalCache();
2041
+ }
2042
+ /**
2043
+ * Compile a project incrementally — only recompiles changed files.
2044
+ */
2045
+ compile(files, options = {}) {
2046
+ const startTime = Date.now();
2047
+ const { dependents } = buildDependencyGraph(files);
2048
+ const dirtySet = this.cache.detectDirtyFiles(files, dependents);
2049
+ const allDirty = /* @__PURE__ */ new Set([
2050
+ ...dirtySet.contentChanged,
2051
+ ...dirtySet.dependencyDirty,
2052
+ ...dirtySet.added
2053
+ ]);
2054
+ for (const removed of dirtySet.removed) {
2055
+ this.cache.delete(removed);
2056
+ }
2057
+ if (allDirty.size === 0 && this.cache.getCachedFiles().length > 0) {
2058
+ this.cache.updateStats(0, this.cache.getCachedFiles().length, Date.now() - startTime);
2059
+ return this.rebuildFromCache(files, options);
2060
+ }
2061
+ for (const [filename, source] of Object.entries(files)) {
2062
+ if (isConfigFile(filename)) {
2063
+ if (allDirty.has(filename)) {
2064
+ this.lastConfig = parseConfig(source);
2065
+ }
2066
+ break;
2067
+ }
2068
+ }
2069
+ const mode = this.lastConfig.mode || options.mode || "infer";
2070
+ const compilableFiles = Object.keys(files).filter(isCompilableFile);
2071
+ let recompiled = 0;
2072
+ let cacheHits = 0;
2073
+ for (const filename of compilableFiles) {
2074
+ if (allDirty.has(filename) || !this.cache.has(filename)) {
2075
+ const { ir, errors } = compileFile(filename, files[filename], mode);
2076
+ if (ir) {
2077
+ this.cache.set(filename, files[filename], { ir, errors });
2078
+ } else {
2079
+ this.cache.delete(filename);
2080
+ }
2081
+ recompiled++;
2082
+ } else {
2083
+ cacheHits++;
2084
+ }
2085
+ }
2086
+ for (const cached of this.cache.getCachedFiles()) {
2087
+ if (!compilableFiles.includes(cached)) {
2088
+ this.cache.delete(cached);
2089
+ }
2090
+ }
2091
+ const fileIRs = {};
2092
+ const allErrors = [];
2093
+ const allWarnings = [];
2094
+ for (const cached of this.cache.getCachedFiles()) {
2095
+ const entry = this.cache.get(cached);
2096
+ if (entry) {
2097
+ fileIRs[cached] = entry.ir;
2098
+ for (const e of entry.errors) {
2099
+ if (e.severity === "error") allErrors.push(e);
2100
+ else allWarnings.push(e);
2101
+ }
2102
+ }
2103
+ }
2104
+ this.cache.updateStats(recompiled, cacheHits, Date.now() - startTime);
2105
+ return buildComposedResult(files, fileIRs, this.lastConfig, allErrors, allWarnings, {
2106
+ usePhase2Modules: options.usePhase2Modules,
2107
+ mode,
2108
+ resolvedModules: options.resolvedModules
2109
+ });
2110
+ }
2111
+ /**
2112
+ * Rebuild result from cache without recompiling anything.
2113
+ */
2114
+ rebuildFromCache(files, options) {
2115
+ let config = this.lastConfig;
2116
+ if (!config.slug) {
2117
+ for (const [filename, source] of Object.entries(files)) {
2118
+ if (isConfigFile(filename)) {
2119
+ config = parseConfig(source);
2120
+ this.lastConfig = config;
2121
+ break;
2122
+ }
2123
+ }
2124
+ }
2125
+ const fileIRs = {};
2126
+ const allErrors = [];
2127
+ const allWarnings = [];
2128
+ for (const cached of this.cache.getCachedFiles()) {
2129
+ const entry = this.cache.get(cached);
2130
+ if (entry) {
2131
+ fileIRs[cached] = entry.ir;
2132
+ for (const e of entry.errors) {
2133
+ if (e.severity === "error") allErrors.push(e);
2134
+ else allWarnings.push(e);
2135
+ }
2136
+ }
2137
+ }
2138
+ const mode = this.lastConfig.mode || options.mode || "infer";
2139
+ return buildComposedResult(files, fileIRs, config, allErrors, allWarnings, {
2140
+ usePhase2Modules: options.usePhase2Modules,
2141
+ mode,
2142
+ resolvedModules: options.resolvedModules
2143
+ });
2144
+ }
2145
+ /** Invalidate a specific file's cache. */
2146
+ invalidate(filename) {
2147
+ this.cache.delete(filename);
2148
+ }
2149
+ /** Invalidate all caches. */
2150
+ invalidateAll() {
2151
+ this.cache.clear();
2152
+ this.lastConfig = {};
2153
+ }
2154
+ /** Check if a file would need recompilation given its current source. */
2155
+ isDirty(filename, source) {
2156
+ hashContent(source);
2157
+ const cached = this.cache.get(filename);
2158
+ return !cached;
2159
+ }
2160
+ /** Get list of files currently in cache. */
2161
+ getCachedFiles() {
2162
+ return this.cache.getCachedFiles();
2163
+ }
2164
+ /** Get compilation statistics. */
2165
+ getStats() {
2166
+ const stats = this.cache.getStats();
2167
+ return {
2168
+ cachedFiles: stats.totalCached,
2169
+ totalHashes: stats.totalCached,
2170
+ ...stats
2171
+ };
2172
+ }
2173
+ };
2174
+
2175
+ export {
2176
+ compileModel,
2177
+ compileModels,
2178
+ extractRoutes,
2179
+ compileActions,
2180
+ resolveActionReferences,
2181
+ hashContent,
2182
+ extractImports,
2183
+ resolveImport,
2184
+ buildDependencyGraph,
2185
+ computeTransitiveDirtySet,
2186
+ topologicalSort,
2187
+ IncrementalCache,
2188
+ compileProject,
2189
+ IncrementalProjectCompiler
2190
+ };