@mmapp/react-compiler 0.1.0-alpha.15 → 0.1.0-alpha.17

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 (185) hide show
  1. package/dist/{chunk-NUPJYPFU.mjs → chunk-6LAII7OP.mjs} +79 -1
  2. package/dist/{chunk-UBDNXVL2.mjs → chunk-FPAMQXKB.mjs} +89 -3
  3. package/dist/cli/index.js +240 -9
  4. package/dist/cli/index.mjs +57 -9
  5. package/dist/dev-server.js +79 -1
  6. package/dist/dev-server.mjs +1 -1
  7. package/dist/index.js +79 -1
  8. package/dist/index.mjs +1 -1
  9. package/dist/{init-2CRSUGV5.mjs → init-CJCDWI33.mjs} +119 -0
  10. package/package.json +2 -2
  11. package/dist/chunk-26U577GB.mjs +0 -3465
  12. package/dist/chunk-2FBDFAX6.mjs +0 -2362
  13. package/dist/chunk-2L4QSMXG.mjs +0 -175
  14. package/dist/chunk-2REDFOER.mjs +0 -931
  15. package/dist/chunk-3USIFFE4.mjs +0 -2190
  16. package/dist/chunk-45YMGEVT.mjs +0 -186
  17. package/dist/chunk-46YKSHQR.mjs +0 -175
  18. package/dist/chunk-4FN2AISW.mjs +0 -148
  19. package/dist/chunk-4OPI5L7G.mjs +0 -2593
  20. package/dist/chunk-4RYTKOOJ.mjs +0 -186
  21. package/dist/chunk-4XHK6FWL.mjs +0 -2058
  22. package/dist/chunk-52XHYD2V.mjs +0 -214
  23. package/dist/chunk-5FTDWKHH.mjs +0 -244
  24. package/dist/chunk-5GUFFFGL.mjs +0 -148
  25. package/dist/chunk-5RKTOVR5.mjs +0 -244
  26. package/dist/chunk-5VNJ7C6N.mjs +0 -154
  27. package/dist/chunk-5YDMOO4X.mjs +0 -214
  28. package/dist/chunk-64ZWEMLJ.mjs +0 -148
  29. package/dist/chunk-6CQOAAMV.mjs +0 -1803
  30. package/dist/chunk-6SEVAAVT.mjs +0 -3516
  31. package/dist/chunk-6XP4KSWQ.mjs +0 -2190
  32. package/dist/chunk-6YLR5ZDA.mjs +0 -2829
  33. package/dist/chunk-72QWL54I.mjs +0 -175
  34. package/dist/chunk-7B4TRI7C.mjs +0 -4835
  35. package/dist/chunk-7JRAEFRB.mjs +0 -7510
  36. package/dist/chunk-7ZKGHTNB.mjs +0 -4952
  37. package/dist/chunk-AOGY2GK6.mjs +0 -3292
  38. package/dist/chunk-AXXUXRNA.mjs +0 -1434
  39. package/dist/chunk-CHLVKMQW.mjs +0 -175
  40. package/dist/chunk-CKGOZAB7.mjs +0 -939
  41. package/dist/chunk-D34RAZUX.mjs +0 -2223
  42. package/dist/chunk-DE3ZGQAC.mjs +0 -148
  43. package/dist/chunk-DMCY3BBG.mjs +0 -1933
  44. package/dist/chunk-DPIK3PJS.mjs +0 -244
  45. package/dist/chunk-E5IVH4RE.mjs +0 -186
  46. package/dist/chunk-E6FZNUR5.mjs +0 -4953
  47. package/dist/chunk-EJRBDQDP.mjs +0 -2607
  48. package/dist/chunk-ELO4TXJL.mjs +0 -186
  49. package/dist/chunk-EO6SYNCG.mjs +0 -175
  50. package/dist/chunk-EQGA6A6D.mjs +0 -121
  51. package/dist/chunk-EY2CSXYA.mjs +0 -822
  52. package/dist/chunk-FIQ65CDR.mjs +0 -925
  53. package/dist/chunk-FKRO52XH.mjs +0 -3446
  54. package/dist/chunk-FL4YAKU6.mjs +0 -4941
  55. package/dist/chunk-FOZXJFAR.mjs +0 -186
  56. package/dist/chunk-FX6URXWN.mjs +0 -186
  57. package/dist/chunk-FYT47UBU.mjs +0 -5076
  58. package/dist/chunk-G7SMOWOL.mjs +0 -828
  59. package/dist/chunk-GCLGPOJZ.mjs +0 -148
  60. package/dist/chunk-GGB4G5YY.mjs +0 -175
  61. package/dist/chunk-GXB4JOP7.mjs +0 -5072
  62. package/dist/chunk-HFXOUMTD.mjs +0 -175
  63. package/dist/chunk-HLRGCCIL.mjs +0 -4839
  64. package/dist/chunk-HOIUP6IF.mjs +0 -690
  65. package/dist/chunk-HRYR54PT.mjs +0 -175
  66. package/dist/chunk-HWIZ47US.mjs +0 -214
  67. package/dist/chunk-I3AU7GRD.mjs +0 -120
  68. package/dist/chunk-IB7MNPQL.mjs +0 -4953
  69. package/dist/chunk-ICSIHQCG.mjs +0 -148
  70. package/dist/chunk-ILFGMUVD.mjs +0 -1933
  71. package/dist/chunk-IPTX5MJU.mjs +0 -3223
  72. package/dist/chunk-ITGUSH2Z.mjs +0 -2783
  73. package/dist/chunk-IXHBCAMF.mjs +0 -3306
  74. package/dist/chunk-J7JUAHS4.mjs +0 -186
  75. package/dist/chunk-J7TWJ3TM.mjs +0 -2784
  76. package/dist/chunk-JDPLDGVF.mjs +0 -4810
  77. package/dist/chunk-JLA5VNQ3.mjs +0 -186
  78. package/dist/chunk-JQLWFCTM.mjs +0 -214
  79. package/dist/chunk-K53XP2DL.mjs +0 -148
  80. package/dist/chunk-K5HX2SVL.mjs +0 -1902
  81. package/dist/chunk-KFGYOOVS.mjs +0 -214
  82. package/dist/chunk-KFJJCQAL.mjs +0 -148
  83. package/dist/chunk-KFVVOS5N.mjs +0 -925
  84. package/dist/chunk-KJUIIEQE.mjs +0 -186
  85. package/dist/chunk-KNWTHRVQ.mjs +0 -175
  86. package/dist/chunk-KSG4XSZF.mjs +0 -175
  87. package/dist/chunk-L2OZ4CDV.mjs +0 -113
  88. package/dist/chunk-LF5N6DOU.mjs +0 -175
  89. package/dist/chunk-LJQCM2IM.mjs +0 -214
  90. package/dist/chunk-MIZV3TAN.mjs +0 -3293
  91. package/dist/chunk-NKKLQE5V.mjs +0 -148
  92. package/dist/chunk-NOW23XFZ.mjs +0 -186
  93. package/dist/chunk-NRXQKQ74.mjs +0 -148
  94. package/dist/chunk-NTB7OEX2.mjs +0 -2918
  95. package/dist/chunk-NW6555WJ.mjs +0 -186
  96. package/dist/chunk-O4AUS7EU.mjs +0 -148
  97. package/dist/chunk-OMZE6VLQ.mjs +0 -214
  98. package/dist/chunk-OPJKP747.mjs +0 -7506
  99. package/dist/chunk-OWI6XWCD.mjs +0 -3375
  100. package/dist/chunk-P4BR7WVO.mjs +0 -2190
  101. package/dist/chunk-PRUMNNDI.mjs +0 -3192
  102. package/dist/chunk-QQHVYH2X.mjs +0 -244
  103. package/dist/chunk-QTBD5B3F.mjs +0 -148
  104. package/dist/chunk-R57T26RR.mjs +0 -734
  105. package/dist/chunk-S5QLWLLT.mjs +0 -186
  106. package/dist/chunk-SCWGT2FY.mjs +0 -2190
  107. package/dist/chunk-SKSDPPNT.mjs +0 -3788
  108. package/dist/chunk-SMKJUSB3.mjs +0 -2190
  109. package/dist/chunk-SP2YUS33.mjs +0 -186
  110. package/dist/chunk-SU4E6E7B.mjs +0 -3153
  111. package/dist/chunk-SYUUKW5A.mjs +0 -3379
  112. package/dist/chunk-THFYE5ZX.mjs +0 -244
  113. package/dist/chunk-UDDTWG5J.mjs +0 -734
  114. package/dist/chunk-UL2XZEMA.mjs +0 -3128
  115. package/dist/chunk-VCAY2KGM.mjs +0 -175
  116. package/dist/chunk-VLTKQDJ3.mjs +0 -244
  117. package/dist/chunk-WBYMW4NQ.mjs +0 -3450
  118. package/dist/chunk-WECAV6QB.mjs +0 -148
  119. package/dist/chunk-WMKBXUCE.mjs +0 -3228
  120. package/dist/chunk-XAJ5BKKL.mjs +0 -4947
  121. package/dist/chunk-XG2X7AEA.mjs +0 -175
  122. package/dist/chunk-XG7Z23NQ.mjs +0 -148
  123. package/dist/chunk-XMWUHQVV.mjs +0 -939
  124. package/dist/chunk-XWZAOCQ7.mjs +0 -2607
  125. package/dist/chunk-XZNEDRGN.mjs +0 -3876
  126. package/dist/chunk-Y6FXYEAI.mjs +0 -10
  127. package/dist/chunk-Y6MA7ULW.mjs +0 -148
  128. package/dist/chunk-YFS6JMYO.mjs +0 -3342
  129. package/dist/chunk-YMS7Q7LG.mjs +0 -214
  130. package/dist/chunk-Z2G5RZ4H.mjs +0 -186
  131. package/dist/chunk-Z6AIQ4KL.mjs +0 -113
  132. package/dist/chunk-ZA37XTGA.mjs +0 -175
  133. package/dist/chunk-ZE3KCHBM.mjs +0 -2918
  134. package/dist/config-PL24KEWL.mjs +0 -219
  135. package/dist/dev-server-Bs_sz2DG.d.mts +0 -111
  136. package/dist/dev-server-Bs_sz2DG.d.ts +0 -111
  137. package/dist/dev-server-CjoufJ-u.d.mts +0 -109
  138. package/dist/dev-server-CjoufJ-u.d.ts +0 -109
  139. package/dist/dev-server-RmGHIntF.d.mts +0 -113
  140. package/dist/dev-server-RmGHIntF.d.ts +0 -113
  141. package/dist/engine-binary-QQUDACBJ.mjs +0 -455
  142. package/dist/envelope-DD7v0v6E.d.mts +0 -265
  143. package/dist/envelope-DD7v0v6E.d.ts +0 -265
  144. package/dist/envelope-vCVjrHlo.d.mts +0 -265
  145. package/dist/envelope-vCVjrHlo.d.ts +0 -265
  146. package/dist/index-B5gSgvnd.d.mts +0 -44
  147. package/dist/index-B5gSgvnd.d.ts +0 -44
  148. package/dist/index-Bs0MnR54.d.mts +0 -103
  149. package/dist/index-Bs0MnR54.d.ts +0 -103
  150. package/dist/index-DR0nNc_f.d.mts +0 -101
  151. package/dist/index-DR0nNc_f.d.ts +0 -101
  152. package/dist/index-revho_gS.d.mts +0 -104
  153. package/dist/index-revho_gS.d.ts +0 -104
  154. package/dist/init-7FJENUDK.mjs +0 -407
  155. package/dist/init-7JQMAAXS.mjs +0 -363
  156. package/dist/init-DQDX3QK6.mjs +0 -369
  157. package/dist/init-EHO4VQ22.mjs +0 -369
  158. package/dist/init-IXEE2RCF.mjs +0 -340
  159. package/dist/init-UC3FWPIW.mjs +0 -367
  160. package/dist/init-UNSMVKIK.mjs +0 -366
  161. package/dist/init-UNV5XIDE.mjs +0 -367
  162. package/dist/project-compiler-2P4N4DR7.mjs +0 -10
  163. package/dist/project-compiler-D2LCC27O.mjs +0 -10
  164. package/dist/project-compiler-EGJUTAJU.mjs +0 -10
  165. package/dist/project-compiler-EJ3GANJE.mjs +0 -10
  166. package/dist/project-compiler-LOQKVRZJ.mjs +0 -10
  167. package/dist/project-compiler-NNK32MPG.mjs +0 -10
  168. package/dist/project-compiler-OP2VVGJQ.mjs +0 -10
  169. package/dist/project-compiler-RQ6OQKRM.mjs +0 -10
  170. package/dist/project-compiler-VFR6CSDX.mjs +0 -10
  171. package/dist/project-compiler-VWNNCHGO.mjs +0 -10
  172. package/dist/project-compiler-XVAAU4C5.mjs +0 -10
  173. package/dist/project-compiler-YES5FGMD.mjs +0 -10
  174. package/dist/project-compiler-ZKMQDLGU.mjs +0 -10
  175. package/dist/project-decompiler-5GY2KSG4.mjs +0 -7
  176. package/dist/project-decompiler-FLXCEJHS.mjs +0 -7
  177. package/dist/project-decompiler-US7GAVIC.mjs +0 -7
  178. package/dist/project-decompiler-VLPR22QF.mjs +0 -7
  179. package/dist/pull-A2QUHW4K.mjs +0 -109
  180. package/dist/pull-FUS5QYZS.mjs +0 -109
  181. package/dist/pull-JBEQWVPE.mjs +0 -109
  182. package/dist/pull-LD5ENLGY.mjs +0 -109
  183. package/dist/pull-P44LDRWB.mjs +0 -109
  184. package/dist/verify-BYHUKARQ.mjs +0 -1833
  185. package/dist/verify-SEIXUGN4.mjs +0 -1833
@@ -1,2918 +0,0 @@
1
- import {
2
- babelPlugin,
3
- init_ts_to_expression,
4
- transpileBlock,
5
- ts_to_expression_exports
6
- } from "./chunk-OPJKP747.mjs";
7
- import {
8
- __toCommonJS
9
- } from "./chunk-CIESM3BP.mjs";
10
-
11
- // src/project-compiler.ts
12
- import { transformSync as transformSync3 } from "@babel/core";
13
-
14
- // src/babel/extractors/router-extractor.ts
15
- function pathToStateName(routePath) {
16
- if (!routePath || routePath === "" || routePath === "/") return "HOME";
17
- return routePath.split("/").filter(Boolean).map((segment) => {
18
- if (segment.startsWith("[") && segment.endsWith("]")) {
19
- return segment.slice(1, -1);
20
- }
21
- return segment;
22
- }).join("_").replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase().replace(/-/g, "_");
23
- }
24
- function pathToUrlPattern(relativePath, pageFileName = "page.tsx") {
25
- let routePath = relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
26
- routePath = routePath.replace(/\[([^\]]+)\]/g, ":$1");
27
- return "/" + routePath;
28
- }
29
- function extractParams(relativePath) {
30
- const params = [];
31
- const paramRegex = /\[([^\]]+)\]/g;
32
- let match;
33
- while ((match = paramRegex.exec(relativePath)) !== null) {
34
- params.push(match[1]);
35
- }
36
- return params;
37
- }
38
- function extractRouterWorkflow(pages, options = {}) {
39
- const {
40
- slug = "app-router",
41
- pageFileName = "page.tsx",
42
- layoutFileName = "layout.tsx"
43
- } = options;
44
- const pageFiles = pages.filter((p) => !p.isLayout && p.relativePath.endsWith(pageFileName));
45
- const layoutFiles = pages.filter((p) => p.isLayout || p.relativePath.endsWith(layoutFileName));
46
- const routeStates = [];
47
- const stateMap = /* @__PURE__ */ new Map();
48
- for (const page of pageFiles) {
49
- const routePath = page.relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
50
- const stateName = pathToStateName(routePath);
51
- const urlPattern = pathToUrlPattern(page.relativePath, pageFileName);
52
- const params = extractParams(page.relativePath);
53
- const layoutBoundary = findLayoutBoundary(page.relativePath, layoutFiles);
54
- const routeState = {
55
- name: stateName,
56
- path: urlPattern,
57
- params,
58
- layoutBoundary,
59
- sourceFile: page.relativePath,
60
- type: stateName === "HOME" ? "START" : "REGULAR"
61
- };
62
- routeStates.push(routeState);
63
- stateMap.set(stateName, routeState);
64
- }
65
- if (!stateMap.has("HOME") && routeStates.length > 0) {
66
- routeStates[0].type = "START";
67
- }
68
- const states = routeStates.map((rs) => ({
69
- name: rs.name,
70
- type: rs.type,
71
- description: `Route: ${rs.path}`,
72
- on_enter: [],
73
- during: [],
74
- on_exit: []
75
- }));
76
- const transitions = [];
77
- let transitionId = 0;
78
- for (const page of pageFiles) {
79
- if (!page.navigationTargets) continue;
80
- const fromPath = page.relativePath.replace(new RegExp(`/?${pageFileName.replace(".", "\\.")}$`), "");
81
- const fromState = pathToStateName(fromPath);
82
- for (const target of page.navigationTargets) {
83
- const targetPath = target.replace(/^\//, "").replace(/\/page\.(tsx|ts|jsx|js)$/, "");
84
- const toState = pathToStateName(targetPath);
85
- if (stateMap.has(toState) || toState === fromState) continue;
86
- const transitionName = `navigate_to_${toState.toLowerCase()}`;
87
- transitions.push({
88
- name: transitionName,
89
- from: [fromState],
90
- to: toState,
91
- actions: [],
92
- conditions: []
93
- });
94
- transitionId++;
95
- }
96
- }
97
- if (routeStates.length > 1) {
98
- for (const rs of routeStates) {
99
- if (rs.type !== "START") {
100
- transitions.push({
101
- name: `navigate_home_from_${rs.name.toLowerCase()}`,
102
- from: [rs.name],
103
- to: "HOME",
104
- actions: [],
105
- conditions: []
106
- });
107
- }
108
- }
109
- }
110
- const fields = [];
111
- const seenParams = /* @__PURE__ */ new Set();
112
- for (const rs of routeStates) {
113
- for (const param of rs.params) {
114
- if (seenParams.has(param)) continue;
115
- seenParams.add(param);
116
- fields.push({
117
- name: param,
118
- type: "text",
119
- required: false,
120
- default_value: ""
121
- });
122
- }
123
- }
124
- const routeMetadata = routeStates.map((rs) => ({
125
- state: rs.name,
126
- path: rs.path,
127
- params: rs.params,
128
- layoutBoundary: rs.layoutBoundary,
129
- sourceFile: rs.sourceFile
130
- }));
131
- return {
132
- slug,
133
- name: "Router",
134
- version: "1.0.0",
135
- description: "Auto-derived router workflow from file-based routing",
136
- category: "router",
137
- fields,
138
- states,
139
- transitions,
140
- roles: [],
141
- tags: [{ tag_name: "auto-derived" }, { tag_name: "router" }],
142
- metadata: {
143
- runtime: "local",
144
- routes: routeMetadata,
145
- layoutBoundaries: layoutFiles.map((l) => l.relativePath)
146
- }
147
- };
148
- }
149
- function findLayoutBoundary(pageRelativePath, layoutFiles) {
150
- const layoutPaths = layoutFiles.map((l) => l.relativePath);
151
- const parts = pageRelativePath.split("/");
152
- for (let i = parts.length - 1; i >= 0; i--) {
153
- const dir = parts.slice(0, i).join("/");
154
- const layoutPath = dir ? `${dir}/layout.tsx` : "layout.tsx";
155
- if (layoutPaths.includes(layoutPath)) {
156
- return layoutPath;
157
- }
158
- }
159
- return void 0;
160
- }
161
-
162
- // src/model-compiler.ts
163
- import { transformSync } from "@babel/core";
164
- function extractInterfaceName(source) {
165
- const match = source.match(/export\s+interface\s+(\w+)/);
166
- return match ? match[1] : "Unknown";
167
- }
168
- function extractWorkflowAnnotation(source) {
169
- const result = {};
170
- const workflowMatch = source.match(/@workflow\s+(.+)/);
171
- if (workflowMatch) {
172
- const attrs = workflowMatch[1];
173
- const slugM = attrs.match(/slug="([^"]+)"/);
174
- if (slugM) result.slug = slugM[1];
175
- const versionM = attrs.match(/version="([^"]+)"/);
176
- if (versionM) result.version = versionM[1];
177
- const categoryM = attrs.match(/category="([^"]+)"/);
178
- if (categoryM) result.category = categoryM[1];
179
- }
180
- const descMatch = source.match(/@description\s+(.+)/);
181
- if (descMatch) result.description = descMatch[1].trim();
182
- return result;
183
- }
184
- function extractFieldOptionsRaw(source) {
185
- const result = {};
186
- const match = source.match(/export\s+const\s+fieldOptions\s*=\s*\{([\s\S]*?)\};/);
187
- if (!match) return result;
188
- const body = match[1];
189
- const fieldRegex = /(\w+)\s*:\s*\{([^}]*)\}/g;
190
- let fieldMatch;
191
- while ((fieldMatch = fieldRegex.exec(body)) !== null) {
192
- const fieldName = fieldMatch[1];
193
- const optionsBody = fieldMatch[2];
194
- const opts = {};
195
- const kvRegex = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|\[([^\]]*)\]|(\d+(?:\.\d+)?)|(\w+))/g;
196
- let kvMatch;
197
- while ((kvMatch = kvRegex.exec(optionsBody)) !== null) {
198
- const key = kvMatch[1];
199
- const strVal = kvMatch[2] ?? kvMatch[3];
200
- const arrVal = kvMatch[4];
201
- const numVal = kvMatch[5];
202
- const identVal = kvMatch[6];
203
- if (strVal !== void 0) {
204
- opts[key] = strVal;
205
- } else if (arrVal !== void 0) {
206
- opts[key] = arrVal.split(",").map((s) => s.trim().replace(/['"]/g, "")).filter(Boolean);
207
- } else if (numVal !== void 0) {
208
- opts[key] = Number(numVal);
209
- } else if (identVal !== void 0) {
210
- if (identVal === "true") opts[key] = true;
211
- else if (identVal === "false") opts[key] = false;
212
- else opts[key] = identVal;
213
- }
214
- }
215
- result[fieldName] = opts;
216
- }
217
- return result;
218
- }
219
- function compileModel(filename, source, options = {}) {
220
- const mode = options.mode || "infer";
221
- const interfaceName = extractInterfaceName(source);
222
- const annotation = extractWorkflowAnnotation(source);
223
- const rawFieldOptions = extractFieldOptionsRaw(source);
224
- const parserPlugins = filename.endsWith(".tsx") ? ["typescript", "jsx"] : ["typescript"];
225
- const babelResult = transformSync(source, {
226
- filename,
227
- plugins: [[babelPlugin, { mode }]],
228
- parserOpts: { plugins: parserPlugins, attachComment: true }
229
- });
230
- const ir = babelResult?.metadata?.mindmatrixIR ?? createEmptyModelIR(interfaceName);
231
- ir.category = options.categoryOverride || annotation.category || "data";
232
- if (options.slugOverride) {
233
- ir.slug = options.slugOverride;
234
- }
235
- if (ir.states.length === 0) {
236
- ir.states.push({
237
- name: "draft",
238
- type: "START",
239
- on_enter: [],
240
- during: [],
241
- on_exit: []
242
- });
243
- }
244
- const hasStart = ir.states.some((s) => s.type === "START");
245
- if (!hasStart && ir.states.length > 0) {
246
- ir.states[0].type = "START";
247
- }
248
- if (!ir.metadata) ir.metadata = {};
249
- const meta = ir.metadata;
250
- meta.sourceInterface = interfaceName;
251
- meta.fieldOptions = rawFieldOptions;
252
- meta.provenance = {
253
- frontend: "react-compiler",
254
- source: "model",
255
- compiler_version: "2.0.0"
256
- };
257
- return {
258
- ir,
259
- interfaceName,
260
- fieldNames: ir.fields.map((f) => f.name),
261
- transitionNames: ir.transitions.map((t2) => t2.name),
262
- stateNames: ir.states.map((s) => s.name),
263
- hasFieldOptions: Object.keys(rawFieldOptions).length > 0,
264
- fieldOptions: rawFieldOptions
265
- };
266
- }
267
- function compileModels(files, options = {}) {
268
- const results = /* @__PURE__ */ new Map();
269
- for (const [filename, source] of Object.entries(files)) {
270
- try {
271
- const compiled = compileModel(filename, source, options);
272
- results.set(filename, compiled);
273
- } catch (_err) {
274
- const iface = extractInterfaceName(source);
275
- results.set(filename, {
276
- ir: createEmptyModelIR(iface),
277
- interfaceName: iface,
278
- fieldNames: [],
279
- transitionNames: [],
280
- stateNames: ["draft"],
281
- hasFieldOptions: false,
282
- fieldOptions: {}
283
- });
284
- }
285
- }
286
- return results;
287
- }
288
- function createEmptyModelIR(interfaceName) {
289
- const slug = interfaceName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
290
- return {
291
- slug,
292
- name: interfaceName,
293
- version: "1.0.0",
294
- description: "Data model: " + interfaceName,
295
- category: "data",
296
- fields: [],
297
- states: [{
298
- name: "draft",
299
- type: "START",
300
- on_enter: [],
301
- during: [],
302
- on_exit: []
303
- }],
304
- transitions: [],
305
- roles: [],
306
- tags: [{ tag_name: "data" }, { tag_name: "model" }],
307
- metadata: {
308
- sourceInterface: interfaceName,
309
- provenance: {
310
- frontend: "react-compiler",
311
- source: "model",
312
- compiler_version: "2.0.0"
313
- }
314
- }
315
- };
316
- }
317
-
318
- // src/route-extractor.ts
319
- function filePathToUrlPattern(filePath, rootDir) {
320
- let route = filePath;
321
- const rootPrefix = rootDir + "/";
322
- if (route.startsWith(rootPrefix)) {
323
- route = route.slice(rootPrefix.length);
324
- }
325
- route = route.replace(/\.(tsx?|jsx?)$/, "");
326
- route = route.replace(/\/(page|index)$/, "");
327
- if (route === "page" || route === "index" || route === "") {
328
- return "/";
329
- }
330
- route = route.replace(/\[([^\]]+)\]/g, ":$1");
331
- return "/" + route;
332
- }
333
- function urlToStateName(urlPath) {
334
- if (urlPath === "/") return "HOME";
335
- return urlPath.split("/").filter(Boolean).map((segment) => segment.replace(/^:/, "")).join("_").replace(/([a-z])([A-Z])/g, "$1_$2").replace(/-/g, "_").toUpperCase();
336
- }
337
- function extractRouteParams(filePath) {
338
- const params = [];
339
- const regex = /\[([^\]]+)\]/g;
340
- let match;
341
- while ((match = regex.exec(filePath)) !== null) {
342
- params.push(match[1]);
343
- }
344
- return params;
345
- }
346
- function routeDepth(urlPath) {
347
- if (urlPath === "/") return 0;
348
- return urlPath.split("/").filter(Boolean).length;
349
- }
350
- function isPageFile(filename, pageFileName) {
351
- if (filename.endsWith("/" + pageFileName)) return true;
352
- return /\.(tsx|ts|jsx|js)$/.test(filename) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx") && !filename.includes("layout");
353
- }
354
- function isLayoutFile(filename, layoutFileName) {
355
- return filename.endsWith("/" + layoutFileName) || filename.endsWith(layoutFileName);
356
- }
357
- function extractRoutes(files, options = {}) {
358
- const rootDir = options.rootDir || "app";
359
- const pageFileName = options.pageFileName || "page.tsx";
360
- const layoutFileName = options.layoutFileName || "layout.tsx";
361
- const slugPrefix = options.slugPrefix || "app";
362
- const layouts = [];
363
- for (const filename of Object.keys(files)) {
364
- if (isLayoutFile(filename, layoutFileName)) {
365
- const dir = filename.replace(/\/[^/]+$/, "");
366
- layouts.push({
367
- sourceFile: filename,
368
- directory: dir,
369
- depth: dir.split("/").filter(Boolean).length
370
- });
371
- }
372
- }
373
- layouts.sort((a, b) => a.depth - b.depth);
374
- const routes = [];
375
- const allParams = /* @__PURE__ */ new Set();
376
- for (const filename of Object.keys(files)) {
377
- if (isLayoutFile(filename, layoutFileName)) continue;
378
- if (!filename.startsWith(rootDir + "/") && !filename.startsWith(rootDir)) continue;
379
- if (!isPageFile(filename, pageFileName)) continue;
380
- const urlPath = filePathToUrlPattern(filename, rootDir);
381
- const stateName = urlToStateName(urlPath);
382
- const params = extractRouteParams(filename);
383
- const depth = routeDepth(urlPath);
384
- const fileDir = filename.replace(/\/[^/]+$/, "");
385
- let layoutBoundary;
386
- for (let i = layouts.length - 1; i >= 0; i--) {
387
- if (fileDir.startsWith(layouts[i].directory)) {
388
- layoutBoundary = layouts[i].sourceFile;
389
- break;
390
- }
391
- }
392
- for (const p of params) allParams.add(p);
393
- routes.push({
394
- path: urlPath,
395
- stateName,
396
- sourceFile: filename,
397
- params,
398
- layoutBoundary,
399
- depth
400
- });
401
- }
402
- routes.sort((a, b) => a.path.localeCompare(b.path));
403
- const routerIR = buildRouterIR(routes, layouts, Array.from(allParams), slugPrefix);
404
- return {
405
- routes,
406
- layouts,
407
- routerIR,
408
- allParams: Array.from(allParams)
409
- };
410
- }
411
- function buildRouterIR(routes, layouts, allParams, slugPrefix) {
412
- const states = routes.map((route) => ({
413
- name: route.stateName,
414
- type: route.path === "/" ? "START" : "REGULAR",
415
- description: "Route: " + route.path,
416
- on_enter: [],
417
- during: [],
418
- on_exit: []
419
- }));
420
- if (states.length > 0 && !states.some((s) => s.type === "START")) {
421
- states[0].type = "START";
422
- }
423
- const transitions = [];
424
- for (const from of routes) {
425
- for (const to of routes) {
426
- if (from.stateName === to.stateName) continue;
427
- transitions.push({
428
- name: "nav_" + from.stateName.toLowerCase() + "_to_" + to.stateName.toLowerCase(),
429
- from: [from.stateName],
430
- to: to.stateName,
431
- actions: [],
432
- conditions: []
433
- });
434
- }
435
- }
436
- const fields = allParams.map((param) => ({
437
- name: param,
438
- type: "text",
439
- label: param.replace(/([A-Z])/g, " $1").replace(/^./, (c) => c.toUpperCase()),
440
- required: false,
441
- default_value: ""
442
- }));
443
- fields.push({
444
- name: "current_route",
445
- type: "text",
446
- label: "Current Route",
447
- required: false,
448
- default_value: "/"
449
- });
450
- return {
451
- slug: slugPrefix + "-router",
452
- name: "Router",
453
- version: "1.0.0",
454
- description: "Auto-generated router from app/ directory structure",
455
- category: "router",
456
- fields,
457
- states,
458
- transitions,
459
- roles: [],
460
- tags: [{ tag_name: "auto-derived" }, { tag_name: "router" }],
461
- metadata: {
462
- runtime: "local",
463
- routes: routes.map((r) => ({
464
- state: r.stateName,
465
- path: r.path,
466
- params: r.params,
467
- layoutBoundary: r.layoutBoundary,
468
- sourceFile: r.sourceFile
469
- })),
470
- layoutBoundaries: layouts.map((l) => l.sourceFile),
471
- provenance: {
472
- frontend: "react-compiler",
473
- source: "route-extractor",
474
- compiler_version: "2.0.0"
475
- }
476
- }
477
- };
478
- }
479
-
480
- // src/action-compiler.ts
481
- import { transformSync as transformSync2 } from "@babel/core";
482
- function extractActionGroup(filename) {
483
- const base = filename.split("/").pop() || filename;
484
- return base.replace(/\.server\.(ts|tsx|js|jsx)$/, "");
485
- }
486
- function generateEndpoint(blueprintSlug, group, actionName, apiBasePath) {
487
- return apiBasePath + "/" + blueprintSlug + "/" + group + "/" + actionName;
488
- }
489
- function generateActionId(group, actionName) {
490
- return group + ":" + actionName;
491
- }
492
- function extractFunctionBodies(source) {
493
- const result = /* @__PURE__ */ new Map();
494
- const funcHeaderRegex = /export\s+(async\s+)?function\s+(\w+)\s*\(/g;
495
- let headerMatch;
496
- while ((headerMatch = funcHeaderRegex.exec(source)) !== null) {
497
- const funcName = headerMatch[2];
498
- const startIdx = headerMatch.index;
499
- let braceStart = source.indexOf("{", headerMatch.index + headerMatch[0].length);
500
- if (braceStart === -1) continue;
501
- const signatureSlice = source.slice(headerMatch.index + headerMatch[0].length, braceStart);
502
- const returnTypeMatch = signatureSlice.match(/\)\s*:\s*([^{]+)/);
503
- const returnType = returnTypeMatch ? returnTypeMatch[1].trim() : void 0;
504
- let depth = 0;
505
- let endIdx = braceStart;
506
- for (let i = braceStart; i < source.length; i++) {
507
- if (source[i] === "{") depth++;
508
- else if (source[i] === "}") {
509
- depth--;
510
- if (depth === 0) {
511
- endIdx = i + 1;
512
- break;
513
- }
514
- }
515
- }
516
- const fullText = source.slice(startIdx, endIdx);
517
- result.set(funcName, { body: fullText, returnType });
518
- }
519
- return result;
520
- }
521
- function extractActionsViaRegex(source) {
522
- const actions = [];
523
- const funcRegex = /(?:\/\*\*([\s\S]*?)\*\/\s*)?export\s+(async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g;
524
- let match;
525
- while ((match = funcRegex.exec(source)) !== null) {
526
- const jsdoc = match[1];
527
- const isAsync = !!match[2];
528
- const name = match[3];
529
- const paramsStr = match[4];
530
- const params = paramsStr.split(",").map((p) => p.trim().split(":")[0].trim().split("?")[0].trim()).filter(Boolean);
531
- let description;
532
- if (jsdoc) {
533
- const lines = jsdoc.split("\n");
534
- for (const line of lines) {
535
- const trimmed = line.replace(/^\s*\*\s?/, "").trim();
536
- if (trimmed && !trimmed.startsWith("@")) {
537
- description = trimmed;
538
- break;
539
- }
540
- }
541
- }
542
- actions.push({ name, async: isAsync, params, description });
543
- }
544
- const arrowRegex = /export\s+const\s+(\w+)\s*=\s*(async\s+)?\([^)]*\)/g;
545
- while ((match = arrowRegex.exec(source)) !== null) {
546
- const name = match[1];
547
- const isAsync = !!match[2];
548
- const arrowParamMatch = source.slice(match.index).match(/\(([^)]*)\)/);
549
- const params = arrowParamMatch ? arrowParamMatch[1].split(",").map((p) => p.trim().split(":")[0].trim()).filter(Boolean) : [];
550
- actions.push({ name, async: isAsync, params });
551
- }
552
- return actions;
553
- }
554
- function compileActions(files, options = {}) {
555
- const mode = options.mode || "infer";
556
- const blueprintSlug = options.blueprintSlug || "app";
557
- const apiBasePath = options.apiBasePath || "/api/v1/actions";
558
- const allActions = [];
559
- const byFile = /* @__PURE__ */ new Map();
560
- const byGroup = /* @__PURE__ */ new Map();
561
- const errors = [];
562
- for (const [filename, source] of Object.entries(files)) {
563
- const group = extractActionGroup(filename);
564
- const fileActions = [];
565
- const bodies = extractFunctionBodies(source);
566
- try {
567
- const parserPlugins = filename.endsWith(".tsx") ? ["typescript", "jsx"] : ["typescript"];
568
- const babelResult = transformSync2(source, {
569
- filename,
570
- plugins: [[babelPlugin, { mode }]],
571
- parserOpts: { plugins: parserPlugins, attachComment: true }
572
- });
573
- const ir = babelResult?.metadata?.mindmatrixIR ?? null;
574
- const meta = ir?.metadata;
575
- const babelActions = meta?.serverActions;
576
- if (babelActions && babelActions.length > 0) {
577
- for (const action of babelActions) {
578
- const bodyInfo = bodies.get(action.name);
579
- const reg = {
580
- name: action.name,
581
- sourceFile: filename,
582
- async: action.async,
583
- params: action.params,
584
- contextType: action.contextType,
585
- description: action.description,
586
- body: bodyInfo?.body,
587
- returnType: bodyInfo?.returnType,
588
- endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
589
- group,
590
- actionId: generateActionId(group, action.name)
591
- };
592
- fileActions.push(reg);
593
- }
594
- } else {
595
- const regexActions = extractActionsViaRegex(source);
596
- for (const action of regexActions) {
597
- const bodyInfo = bodies.get(action.name);
598
- const reg = {
599
- name: action.name,
600
- sourceFile: filename,
601
- async: action.async,
602
- params: action.params,
603
- description: action.description,
604
- body: bodyInfo?.body,
605
- returnType: bodyInfo?.returnType,
606
- endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
607
- group,
608
- actionId: generateActionId(group, action.name)
609
- };
610
- fileActions.push(reg);
611
- }
612
- }
613
- } catch (err) {
614
- errors.push({ file: filename, message: err.message });
615
- const regexActions = extractActionsViaRegex(source);
616
- for (const action of regexActions) {
617
- const bodyInfo = bodies.get(action.name);
618
- const reg = {
619
- name: action.name,
620
- sourceFile: filename,
621
- async: action.async,
622
- params: action.params,
623
- description: action.description,
624
- body: bodyInfo?.body,
625
- returnType: bodyInfo?.returnType,
626
- endpoint: generateEndpoint(blueprintSlug, group, action.name, apiBasePath),
627
- group,
628
- actionId: generateActionId(group, action.name)
629
- };
630
- fileActions.push(reg);
631
- }
632
- }
633
- byFile.set(filename, fileActions);
634
- if (!byGroup.has(group)) byGroup.set(group, []);
635
- byGroup.get(group).push(...fileActions);
636
- allActions.push(...fileActions);
637
- }
638
- return { actions: allActions, byFile, byGroup, errors };
639
- }
640
- function resolveActionReferences(actionNames, registrations) {
641
- const result = /* @__PURE__ */ new Map();
642
- const byName = new Map(registrations.map((r) => [r.name, r]));
643
- for (const name of actionNames) {
644
- if (byName.has(name)) {
645
- result.set(name, byName.get(name));
646
- continue;
647
- }
648
- const colonIdx = name.indexOf(":");
649
- if (colonIdx > 0) {
650
- const actionName = name.slice(colonIdx + 1);
651
- if (byName.has(actionName)) {
652
- result.set(name, byName.get(actionName));
653
- continue;
654
- }
655
- }
656
- result.set(name, null);
657
- }
658
- return result;
659
- }
660
-
661
- // src/babel/extractors/action-extractor.ts
662
- init_ts_to_expression();
663
- import { parse } from "@babel/parser";
664
- import * as t from "@babel/types";
665
- function extractAction(source, filename) {
666
- let ast;
667
- try {
668
- ast = parse(source, {
669
- sourceType: "module",
670
- plugins: ["typescript"],
671
- strictMode: false
672
- });
673
- } catch {
674
- return null;
675
- }
676
- const result = findDefaultExportedFunction(ast);
677
- if (!result) return null;
678
- const { name, params, body, returnTypeAnnotation, isAsync } = result;
679
- const slug = toKebabCase(name);
680
- const warnings = [];
681
- const fields = params.map((p) => paramToField(p));
682
- const parameterMap = /* @__PURE__ */ new Map();
683
- for (const p of params) {
684
- const snakeName = toSnakeCase(p.name);
685
- if (snakeName !== p.name) {
686
- parameterMap.set(p.name, snakeName);
687
- }
688
- }
689
- const humanName = name.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
690
- const metadata = {
691
- source_file: filename,
692
- source_function: name,
693
- provenance: {
694
- frontend: "react-compiler",
695
- source: "action-extractor",
696
- compiler_version: "2.0.0"
697
- }
698
- };
699
- if (returnTypeAnnotation) {
700
- metadata.return_type = returnTypeAnnotation;
701
- }
702
- if (isAsync) {
703
- const segments = splitAtAwaits(body.body);
704
- const hasAwaits = segments.some((s) => s.kind !== "sync");
705
- if (hasAwaits) {
706
- const rewritten = rewriteAwaitReferences(segments);
707
- const { states: states2, transitions: transitions2, bodyIsPure } = generateMultiStateIR(rewritten, warnings, parameterMap);
708
- if (!bodyIsPure) {
709
- warnings.push(`Async action '${name}' body contains untranslatable JS \u2014 wrapped in $expr() markers`);
710
- }
711
- const ir2 = {
712
- slug,
713
- name: humanName,
714
- version: "0.1.0",
715
- category: "action",
716
- fields,
717
- states: states2,
718
- transitions: transitions2,
719
- roles: [],
720
- metadata: { ...metadata, async: true }
721
- };
722
- return { ir: ir2, bodyIsPure, warnings };
723
- }
724
- }
725
- const transpileResult = transpileBlock(body, { allMutable: true, parameterMap });
726
- const bodyExpr = transpileResult.expression;
727
- if (!transpileResult.pure) {
728
- warnings.push(`Action '${name}' body contains untranslatable JS \u2014 wrapped in $expr() markers`);
729
- }
730
- const states = [
731
- {
732
- name: "ready",
733
- type: "START",
734
- on_enter: [],
735
- during: [],
736
- on_exit: []
737
- },
738
- {
739
- name: "done",
740
- type: "END",
741
- on_enter: [],
742
- during: [],
743
- on_exit: []
744
- }
745
- ];
746
- const transitions = [
747
- {
748
- name: "execute",
749
- from: ["ready"],
750
- to: "done",
751
- actions: [
752
- {
753
- id: "body",
754
- type: "eval",
755
- mode: "auto",
756
- config: {
757
- expression: bodyExpr
758
- }
759
- }
760
- ]
761
- }
762
- ];
763
- const ir = {
764
- slug,
765
- name: humanName,
766
- version: "0.1.0",
767
- category: "action",
768
- fields,
769
- states,
770
- transitions,
771
- roles: [],
772
- metadata
773
- };
774
- return { ir, bodyIsPure: transpileResult.pure, warnings };
775
- }
776
- function findDefaultExportedFunction(ast) {
777
- const topLevelVars = /* @__PURE__ */ new Map();
778
- for (const node of ast.program.body) {
779
- if (t.isVariableDeclaration(node)) {
780
- for (const decl of node.declarations) {
781
- if (t.isIdentifier(decl.id) && decl.init) {
782
- if (t.isArrowFunctionExpression(decl.init) || t.isFunctionExpression(decl.init)) {
783
- topLevelVars.set(decl.id.name, decl.init);
784
- }
785
- }
786
- }
787
- }
788
- }
789
- for (const node of ast.program.body) {
790
- if (t.isExportDefaultDeclaration(node)) {
791
- const decl = node.declaration;
792
- if (t.isFunctionDeclaration(decl) && decl.body) {
793
- const name = decl.id?.name ?? inferNameFromFile(decl) ?? "action";
794
- return extractFromFunction(name, decl.params, decl.body, decl.returnType, decl.async);
795
- }
796
- if (t.isArrowFunctionExpression(decl) || t.isFunctionExpression(decl)) {
797
- const name = t.isFunctionExpression(decl) && decl.id ? decl.id.name : "action";
798
- const body = ensureBlock(decl.body);
799
- if (!body) return null;
800
- return extractFromFunction(name, decl.params, body, decl.returnType, decl.async);
801
- }
802
- if (t.isIdentifier(decl)) {
803
- const fn = topLevelVars.get(decl.name);
804
- if (fn) {
805
- const body = ensureBlock(fn.body);
806
- if (!body) return null;
807
- return extractFromFunction(decl.name, fn.params, body, fn.returnType, fn.async);
808
- }
809
- }
810
- }
811
- }
812
- return null;
813
- }
814
- function extractFromFunction(name, params, body, returnType, isAsync = false) {
815
- const extracted = params.map((p) => extractParam(p));
816
- const returnTypeAnnotation = returnType && t.isTSTypeAnnotation(returnType) ? serializeTSType(returnType.typeAnnotation) : void 0;
817
- return { name, params: extracted, body, returnTypeAnnotation, isAsync };
818
- }
819
- function ensureBlock(body) {
820
- if (t.isBlockStatement(body)) return body;
821
- const ret = t.returnStatement(body);
822
- return t.blockStatement([ret]);
823
- }
824
- function inferNameFromFile(_node) {
825
- return null;
826
- }
827
- function extractParam(param) {
828
- if (t.isIdentifier(param)) {
829
- return {
830
- name: param.name,
831
- typeAnnotation: param.typeAnnotation ? param.typeAnnotation.typeAnnotation : null,
832
- optional: param.optional ?? false
833
- };
834
- }
835
- if (t.isAssignmentPattern(param)) {
836
- const inner = param.left;
837
- if (t.isIdentifier(inner)) {
838
- return {
839
- name: inner.name,
840
- typeAnnotation: inner.typeAnnotation ? inner.typeAnnotation.typeAnnotation : null,
841
- optional: true,
842
- defaultValue: param.right
843
- };
844
- }
845
- }
846
- if (t.isRestElement(param)) {
847
- const arg = param.argument;
848
- return {
849
- name: t.isIdentifier(arg) ? arg.name : "rest",
850
- typeAnnotation: null,
851
- optional: true
852
- };
853
- }
854
- return { name: "params", typeAnnotation: null, optional: false };
855
- }
856
- function paramToField(param) {
857
- const fieldType = param.typeAnnotation ? tsTypeToFieldType(param.typeAnnotation) : "text";
858
- const field = {
859
- name: toSnakeCase(param.name),
860
- type: fieldType,
861
- required: !param.optional
862
- };
863
- if (param.defaultValue !== void 0) {
864
- field.default_value = extractLiteralDefault(param.defaultValue);
865
- }
866
- return field;
867
- }
868
- function extractLiteralDefault(expr) {
869
- if (!expr) return void 0;
870
- if (t.isStringLiteral(expr)) return expr.value;
871
- if (t.isNumericLiteral(expr)) return expr.value;
872
- if (t.isBooleanLiteral(expr)) return expr.value;
873
- if (t.isNullLiteral(expr)) return null;
874
- if (t.isArrayExpression(expr) && expr.elements.length === 0) return [];
875
- if (t.isObjectExpression(expr) && expr.properties.length === 0) return {};
876
- return void 0;
877
- }
878
- function tsTypeToFieldType(tsType) {
879
- if (t.isTSStringKeyword(tsType)) return "text";
880
- if (t.isTSNumberKeyword(tsType)) return "number";
881
- if (t.isTSBooleanKeyword(tsType)) return "boolean";
882
- if (t.isTSObjectKeyword(tsType)) return "json";
883
- if (t.isTSAnyKeyword(tsType) || t.isTSUnknownKeyword(tsType)) return "json";
884
- if (t.isTSArrayType(tsType)) return "json";
885
- if (t.isTSUnionType(tsType)) {
886
- const nonNullable = tsType.types.filter(
887
- (t2) => !t.isTSNullKeyword(t2) && !t.isTSUndefinedKeyword(t2)
888
- );
889
- if (nonNullable.length === 1) {
890
- return tsTypeToFieldType(nonNullable[0]);
891
- }
892
- if (nonNullable.every((t2) => t.isTSLiteralType(t2) && t.isStringLiteral(t2.literal))) {
893
- return "select";
894
- }
895
- return "text";
896
- }
897
- if (t.isTSTypeReference(tsType) && t.isIdentifier(tsType.typeName)) {
898
- const name = tsType.typeName.name;
899
- if (name === "Date") return "datetime";
900
- if (name === "string") return "text";
901
- if (name === "number") return "number";
902
- if (name === "boolean") return "boolean";
903
- }
904
- return "text";
905
- }
906
- function serializeTSType(tsType) {
907
- if (t.isTSStringKeyword(tsType)) return "string";
908
- if (t.isTSNumberKeyword(tsType)) return "number";
909
- if (t.isTSBooleanKeyword(tsType)) return "boolean";
910
- if (t.isTSVoidKeyword(tsType)) return "void";
911
- if (t.isTSAnyKeyword(tsType)) return "any";
912
- if (t.isTSTypeReference(tsType) && t.isIdentifier(tsType.typeName)) {
913
- return tsType.typeName.name;
914
- }
915
- if (t.isTSArrayType(tsType)) return `${serializeTSType(tsType.elementType)}[]`;
916
- if (t.isTSPromiseType(tsType)) {
917
- return `Promise<${tsType.typeParameter ? serializeTSType(tsType.typeParameter.params[0]) : "unknown"}>`;
918
- }
919
- return "unknown";
920
- }
921
- function splitAtAwaits(body) {
922
- const segments = [];
923
- let current = [];
924
- let index = 0;
925
- for (const stmt of body) {
926
- if (t.isTryStatement(stmt) && containsAwait(stmt.block)) {
927
- if (current.length > 0) {
928
- segments.push({ index: index++, kind: "sync", statements: current });
929
- current = [];
930
- }
931
- const trySegs = splitAtAwaits(stmt.block.body);
932
- const catchStmts = stmt.handler?.body.body ?? [];
933
- const catchParam = stmt.handler?.param && t.isIdentifier(stmt.handler.param) ? stmt.handler.param.name : "error";
934
- segments.push({
935
- index: index++,
936
- kind: "try_catch",
937
- statements: [stmt],
938
- trySegments: trySegs,
939
- catchStatements: catchStmts,
940
- catchParam
941
- });
942
- continue;
943
- }
944
- if (t.isForOfStatement(stmt) && containsAwait(stmt.body)) {
945
- if (current.length > 0) {
946
- segments.push({ index: index++, kind: "sync", statements: current });
947
- current = [];
948
- }
949
- const loopBody = t.isBlockStatement(stmt.body) ? stmt.body.body : [stmt.body];
950
- const loopBinding = t.isVariableDeclaration(stmt.left) && t.isIdentifier(stmt.left.declarations[0]?.id) ? stmt.left.declarations[0].id.name : "item";
951
- segments.push({
952
- index: index++,
953
- kind: "loop",
954
- statements: [stmt],
955
- loopBinding,
956
- loopIterable: stmt.right,
957
- loopBodySegments: splitAtAwaits(loopBody)
958
- });
959
- continue;
960
- }
961
- const promiseInfo = extractPromiseAllOrRace(stmt);
962
- if (promiseInfo) {
963
- if (current.length > 0) {
964
- segments.push({ index: index++, kind: "sync", statements: current });
965
- current = [];
966
- }
967
- segments.push({
968
- index: index++,
969
- kind: promiseInfo.type === "all" ? "parallel" : "race",
970
- statements: [stmt],
971
- parallelCallees: promiseInfo.callees,
972
- resultBinding: promiseInfo.binding
973
- });
974
- continue;
975
- }
976
- const awaitInfo = extractAwait(stmt);
977
- if (!awaitInfo) {
978
- current.push(stmt);
979
- continue;
980
- }
981
- if (current.length > 0) {
982
- segments.push({ index: index++, kind: "sync", statements: current });
983
- current = [];
984
- }
985
- segments.push({
986
- index: index++,
987
- kind: "await",
988
- statements: [stmt],
989
- awaitTarget: awaitInfo.callee,
990
- awaitArgs: awaitInfo.args,
991
- resultBinding: awaitInfo.binding
992
- });
993
- }
994
- if (current.length > 0) {
995
- segments.push({ index: index++, kind: "sync", statements: current });
996
- }
997
- return segments;
998
- }
999
- function extractAwait(stmt) {
1000
- if (t.isVariableDeclaration(stmt)) {
1001
- const decl = stmt.declarations[0];
1002
- if (decl?.init && t.isAwaitExpression(decl.init)) {
1003
- const arg = decl.init.argument;
1004
- if (t.isCallExpression(arg)) {
1005
- return {
1006
- callee: getCalleeName(arg.callee),
1007
- args: arg.arguments,
1008
- binding: t.isIdentifier(decl.id) ? decl.id.name : void 0
1009
- };
1010
- }
1011
- }
1012
- }
1013
- if (t.isExpressionStatement(stmt) && t.isAwaitExpression(stmt.expression)) {
1014
- const arg = stmt.expression.argument;
1015
- if (t.isCallExpression(arg)) {
1016
- return {
1017
- callee: getCalleeName(arg.callee),
1018
- args: arg.arguments,
1019
- binding: void 0
1020
- };
1021
- }
1022
- }
1023
- if (t.isReturnStatement(stmt) && stmt.argument && t.isAwaitExpression(stmt.argument)) {
1024
- const arg = stmt.argument.argument;
1025
- if (t.isCallExpression(arg)) {
1026
- return {
1027
- callee: getCalleeName(arg.callee),
1028
- args: arg.arguments,
1029
- binding: void 0
1030
- };
1031
- }
1032
- }
1033
- return null;
1034
- }
1035
- function containsAwait(node) {
1036
- if (t.isAwaitExpression(node)) return true;
1037
- if (t.isBlockStatement(node)) {
1038
- return node.body.some((s) => containsAwait(s));
1039
- }
1040
- if (t.isExpressionStatement(node)) {
1041
- return containsAwait(node.expression);
1042
- }
1043
- if (t.isVariableDeclaration(node)) {
1044
- return node.declarations.some((d) => d.init ? containsAwait(d.init) : false);
1045
- }
1046
- if (t.isReturnStatement(node) && node.argument) {
1047
- return containsAwait(node.argument);
1048
- }
1049
- if (t.isCallExpression(node)) {
1050
- return node.arguments.some((a) => t.isNode(a) && containsAwait(a));
1051
- }
1052
- if (t.isForOfStatement(node)) {
1053
- return containsAwait(node.body);
1054
- }
1055
- if (t.isForStatement(node)) {
1056
- return containsAwait(node.body);
1057
- }
1058
- if (t.isIfStatement(node)) {
1059
- return containsAwait(node.consequent) || (node.alternate ? containsAwait(node.alternate) : false);
1060
- }
1061
- if (t.isTryStatement(node)) {
1062
- return containsAwait(node.block) || (node.handler ? containsAwait(node.handler.body) : false);
1063
- }
1064
- if (t.isArrayExpression(node)) {
1065
- return node.elements.some((e) => e && !t.isSpreadElement(e) && containsAwait(e));
1066
- }
1067
- return false;
1068
- }
1069
- function extractPromiseAllOrRace(stmt) {
1070
- let awaitExpr = null;
1071
- let binding;
1072
- if (t.isVariableDeclaration(stmt)) {
1073
- const decl = stmt.declarations[0];
1074
- if (decl?.init && t.isAwaitExpression(decl.init)) {
1075
- awaitExpr = decl.init;
1076
- binding = t.isIdentifier(decl.id) ? decl.id.name : t.isArrayPattern(decl.id) ? "__destructured__" : void 0;
1077
- }
1078
- } else if (t.isExpressionStatement(stmt) && t.isAwaitExpression(stmt.expression)) {
1079
- awaitExpr = stmt.expression;
1080
- }
1081
- if (!awaitExpr) return null;
1082
- const arg = awaitExpr.argument;
1083
- if (!t.isCallExpression(arg)) return null;
1084
- if (!t.isMemberExpression(arg.callee)) return null;
1085
- if (!t.isIdentifier(arg.callee.object) || arg.callee.object.name !== "Promise") return null;
1086
- if (!t.isIdentifier(arg.callee.property)) return null;
1087
- const method = arg.callee.property.name;
1088
- if (method !== "all" && method !== "race") return null;
1089
- const firstArg = arg.arguments[0];
1090
- if (!t.isArrayExpression(firstArg)) return null;
1091
- const callees = [];
1092
- for (const elem of firstArg.elements) {
1093
- if (!elem || t.isSpreadElement(elem)) continue;
1094
- if (t.isCallExpression(elem)) {
1095
- callees.push({
1096
- callee: getCalleeName(elem.callee),
1097
- args: elem.arguments
1098
- });
1099
- }
1100
- }
1101
- if (callees.length === 0) return null;
1102
- if (binding === "__destructured__" && t.isVariableDeclaration(stmt)) {
1103
- const decl = stmt.declarations[0];
1104
- if (t.isArrayPattern(decl.id)) {
1105
- for (let i = 0; i < decl.id.elements.length && i < callees.length; i++) {
1106
- const elem = decl.id.elements[i];
1107
- if (t.isIdentifier(elem)) {
1108
- callees[i].binding = elem.name;
1109
- }
1110
- }
1111
- }
1112
- }
1113
- return { type: method, callees, binding };
1114
- }
1115
- function getCalleeName(callee) {
1116
- if (t.isIdentifier(callee)) return callee.name;
1117
- if (t.isMemberExpression(callee) && t.isIdentifier(callee.object) && t.isIdentifier(callee.property)) {
1118
- return `${callee.object.name}_${callee.property.name}`;
1119
- }
1120
- return "unknown_action";
1121
- }
1122
- function rewriteAwaitReferences(segments) {
1123
- const bindings = /* @__PURE__ */ new Map();
1124
- const slugCounts = /* @__PURE__ */ new Map();
1125
- return segments.map((seg) => {
1126
- if (seg.kind === "await" && seg.resultBinding) {
1127
- const slug = toSnakeCase(seg.awaitTarget ?? "unknown");
1128
- const count = (slugCounts.get(slug) ?? 0) + 1;
1129
- slugCounts.set(slug, count);
1130
- const key = count > 1 ? `${slug}_${count}` : slug;
1131
- bindings.set(seg.resultBinding, `action_results.${key}`);
1132
- }
1133
- if (seg.kind === "sync" && bindings.size > 0) {
1134
- return {
1135
- ...seg,
1136
- statements: seg.statements.map(
1137
- (stmt) => rewriteIdentifiersInStatement(stmt, bindings)
1138
- )
1139
- };
1140
- }
1141
- return seg;
1142
- });
1143
- }
1144
- function rewriteIdentifiersInStatement(stmt, _bindings) {
1145
- return stmt;
1146
- }
1147
- function generateMultiStateIR(segments, _warnings, parameterMap) {
1148
- const states = [];
1149
- const transitions = [];
1150
- let bodyIsPure = true;
1151
- const awaitBindings = /* @__PURE__ */ new Map();
1152
- const slugCounts = /* @__PURE__ */ new Map();
1153
- for (const seg of segments) {
1154
- if (seg.kind === "await" && seg.resultBinding) {
1155
- const slug = toSnakeCase(seg.awaitTarget ?? "unknown");
1156
- const count = (slugCounts.get(slug) ?? 0) + 1;
1157
- slugCounts.set(slug, count);
1158
- const key = count > 1 ? `${slug}_${count}` : slug;
1159
- awaitBindings.set(seg.resultBinding, `action_results.${key}`);
1160
- }
1161
- }
1162
- states.push({ name: "ready", type: "START", on_enter: [], during: [], on_exit: [] });
1163
- const usedStateNames = /* @__PURE__ */ new Set();
1164
- let prevState = "ready";
1165
- let transitionCount = 0;
1166
- let pendingSyncStmts = [];
1167
- for (const seg of segments) {
1168
- if (seg.kind === "sync") {
1169
- pendingSyncStmts.push(...seg.statements);
1170
- continue;
1171
- }
1172
- if (seg.kind === "try_catch" && seg.trySegments) {
1173
- if (pendingSyncStmts.length > 0) {
1174
- const block = t.blockStatement(pendingSyncStmts);
1175
- const transpiled = transpileBlock(block, { allMutable: true, derivedVarMap: buildDerivedVarMap(awaitBindings), parameterMap });
1176
- if (!transpiled.pure) bodyIsPure = false;
1177
- const preState = uniqueState("pre_try", usedStateNames);
1178
- states.push({ name: preState, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1179
- transitions.push({
1180
- name: transitionCount === 0 ? "execute" : `on_${getStateSuffix(prevState)}_complete`,
1181
- from: [prevState],
1182
- to: preState,
1183
- actions: [{ id: `pre_try_${transitionCount}`, type: "eval", mode: "auto", config: { expression: transpiled.expression } }]
1184
- });
1185
- prevState = preState;
1186
- transitionCount++;
1187
- pendingSyncStmts = [];
1188
- }
1189
- const tryResult = generateMultiStateIR(seg.trySegments, _warnings, parameterMap);
1190
- if (!tryResult.bodyIsPure) bodyIsPure = false;
1191
- const successState = uniqueState("try_success", usedStateNames);
1192
- const errorState = uniqueState("try_error", usedStateNames);
1193
- states.push({ name: errorState, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1194
- states.push({ name: successState, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1195
- for (const s of tryResult.states) {
1196
- if (s.name === "ready" || s.name === "done") continue;
1197
- states.push(s);
1198
- }
1199
- for (const tr of tryResult.transitions) {
1200
- const remappedFrom = tr.from.map((f) => f === "ready" ? prevState : f);
1201
- const remappedTo = tr.to === "done" ? successState : tr.to;
1202
- transitions.push({ ...tr, from: remappedFrom, to: remappedTo });
1203
- if (tr.actions.some((a) => a.type === "call_workflow")) {
1204
- const awaitState = tr.to === successState ? successState : tr.to;
1205
- if (awaitState !== successState) {
1206
- transitions.push({
1207
- name: `on_${getStateSuffix(awaitState)}_error`,
1208
- from: [awaitState],
1209
- to: errorState,
1210
- actions: [],
1211
- conditions: [{ expression: "action_error != null", type: "expression" }]
1212
- });
1213
- }
1214
- }
1215
- }
1216
- if (tryResult.transitions.length > 0 && transitionCount === 0) {
1217
- transitions[transitions.length - tryResult.transitions.length].name = "execute";
1218
- }
1219
- if (seg.catchStatements && seg.catchStatements.length > 0) {
1220
- const catchBlock = t.blockStatement(seg.catchStatements);
1221
- const catchBindings = new Map(awaitBindings);
1222
- if (seg.catchParam) {
1223
- catchBindings.set(seg.catchParam, "action_error");
1224
- }
1225
- const catchTranspiled = transpileBlock(catchBlock, {
1226
- allMutable: true,
1227
- derivedVarMap: buildDerivedVarMap(catchBindings),
1228
- parameterMap
1229
- });
1230
- if (!catchTranspiled.pure) bodyIsPure = false;
1231
- const postCatch = uniqueState("post_catch", usedStateNames);
1232
- states.push({ name: postCatch, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1233
- transitions.push({
1234
- name: `catch_${transitionCount}`,
1235
- from: [errorState],
1236
- to: postCatch,
1237
- actions: [{ id: `catch_eval_${transitionCount}`, type: "eval", mode: "auto", config: { expression: catchTranspiled.expression } }]
1238
- });
1239
- prevState = postCatch;
1240
- transitions.push({
1241
- name: `try_success_${transitionCount}`,
1242
- from: [successState],
1243
- to: postCatch,
1244
- actions: []
1245
- });
1246
- } else {
1247
- prevState = successState;
1248
- }
1249
- transitionCount++;
1250
- continue;
1251
- }
1252
- if ((seg.kind === "parallel" || seg.kind === "race") && seg.parallelCallees) {
1253
- if (pendingSyncStmts.length > 0) {
1254
- const block = t.blockStatement(pendingSyncStmts);
1255
- const transpiled = transpileBlock(block, { allMutable: true, derivedVarMap: buildDerivedVarMap(awaitBindings), parameterMap });
1256
- if (!transpiled.pure) bodyIsPure = false;
1257
- pendingSyncStmts = [];
1258
- }
1259
- const groupId = `p${transitionCount}`;
1260
- const dispatchKind = seg.kind === "parallel" ? "parallel" : "race";
1261
- const stateName2 = uniqueState(`awaiting_${dispatchKind}_${transitionCount}`, usedStateNames);
1262
- states.push({ name: stateName2, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1263
- const actions2 = [];
1264
- for (let ci = 0; ci < seg.parallelCallees.length; ci++) {
1265
- const callee = seg.parallelCallees[ci];
1266
- const slug = toSnakeCase(callee.callee);
1267
- actions2.push({
1268
- id: `call_${slug}_${ci}`,
1269
- type: "call_workflow",
1270
- mode: "auto",
1271
- config: {
1272
- definition_slug: callee.callee,
1273
- result_key: slug,
1274
- [`${dispatchKind}_group`]: groupId
1275
- }
1276
- });
1277
- if (callee.binding) {
1278
- awaitBindings.set(callee.binding, `action_results.${slug}`);
1279
- }
1280
- }
1281
- const transName2 = transitionCount === 0 ? "execute" : `on_${getStateSuffix(prevState)}_complete`;
1282
- transitions.push({ name: transName2, from: [prevState], to: stateName2, actions: actions2 });
1283
- prevState = stateName2;
1284
- transitionCount++;
1285
- continue;
1286
- }
1287
- if (seg.kind === "loop" && seg.loopBodySegments) {
1288
- if (pendingSyncStmts.length > 0) {
1289
- const block = t.blockStatement(pendingSyncStmts);
1290
- const transpiled = transpileBlock(block, { allMutable: true, derivedVarMap: buildDerivedVarMap(awaitBindings), parameterMap });
1291
- if (!transpiled.pure) bodyIsPure = false;
1292
- pendingSyncStmts = [];
1293
- }
1294
- const loopState = uniqueState(`loop_${toSnakeCase(seg.loopBinding ?? "item")}`, usedStateNames);
1295
- const postLoop = uniqueState("post_loop", usedStateNames);
1296
- states.push({ name: loopState, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1297
- states.push({ name: postLoop, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1298
- const initTransName = transitionCount === 0 ? "execute" : `on_${getStateSuffix(prevState)}_complete`;
1299
- transitions.push({
1300
- name: initTransName,
1301
- from: [prevState],
1302
- to: loopState,
1303
- actions: [{
1304
- id: `loop_init_${transitionCount}`,
1305
- type: "eval",
1306
- mode: "auto",
1307
- config: { expression: `let __loop_idx = 0; let __loop_items = ${seg.loopIterable ? toSnakeCase(seg.loopBinding ?? "items") : "[]"}` }
1308
- }]
1309
- });
1310
- const awaitSegs = seg.loopBodySegments.filter((s) => s.kind === "await");
1311
- if (awaitSegs.length > 0) {
1312
- const firstAwait = awaitSegs[0];
1313
- const slug = toSnakeCase(firstAwait.awaitTarget ?? "action");
1314
- transitions.push({
1315
- name: `loop_body_${transitionCount}`,
1316
- from: [loopState],
1317
- to: loopState,
1318
- actions: [{
1319
- id: `loop_call_${slug}`,
1320
- type: "call_workflow",
1321
- mode: "auto",
1322
- config: {
1323
- definition_slug: firstAwait.awaitTarget,
1324
- result_key: slug,
1325
- loop_iteration: true
1326
- }
1327
- }],
1328
- conditions: [{ expression: "__loop_idx < length(__loop_items)", type: "expression" }]
1329
- });
1330
- }
1331
- transitions.push({
1332
- name: `loop_done_${transitionCount}`,
1333
- from: [loopState],
1334
- to: postLoop,
1335
- actions: [],
1336
- conditions: [{ expression: "__loop_idx >= length(__loop_items)", type: "expression" }]
1337
- });
1338
- prevState = postLoop;
1339
- transitionCount++;
1340
- continue;
1341
- }
1342
- const targetSlug = toSnakeCase(seg.awaitTarget ?? "unknown");
1343
- let stateName = `awaiting_${targetSlug}`;
1344
- if (usedStateNames.has(stateName)) {
1345
- let suffix = 2;
1346
- while (usedStateNames.has(`${stateName}_${suffix}`)) suffix++;
1347
- stateName = `${stateName}_${suffix}`;
1348
- }
1349
- usedStateNames.add(stateName);
1350
- states.push({ name: stateName, type: "REGULAR", on_enter: [], during: [], on_exit: [] });
1351
- const actions = [];
1352
- if (pendingSyncStmts.length > 0) {
1353
- const block = t.blockStatement(pendingSyncStmts);
1354
- const transpiled = transpileBlock(block, {
1355
- allMutable: true,
1356
- derivedVarMap: buildDerivedVarMap(awaitBindings),
1357
- parameterMap
1358
- });
1359
- if (!transpiled.pure) bodyIsPure = false;
1360
- actions.push({
1361
- id: `pre_${transitionCount}`,
1362
- type: "eval",
1363
- mode: "auto",
1364
- config: { expression: transpiled.expression }
1365
- });
1366
- pendingSyncStmts = [];
1367
- }
1368
- const callConfig = {
1369
- definition_slug: seg.awaitTarget,
1370
- result_key: targetSlug
1371
- };
1372
- if (seg.awaitArgs && seg.awaitArgs.length > 0) {
1373
- const params = {};
1374
- for (let i = 0; i < seg.awaitArgs.length; i++) {
1375
- const arg = seg.awaitArgs[i];
1376
- if (t.isObjectExpression(arg)) {
1377
- for (const prop of arg.properties) {
1378
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
1379
- const { expression: valExpr } = transpileArgExpression(prop.value, awaitBindings);
1380
- params[prop.key.name] = valExpr;
1381
- }
1382
- }
1383
- } else {
1384
- const { expression: valExpr } = transpileArgExpression(arg, awaitBindings);
1385
- params[`arg${i}`] = valExpr;
1386
- }
1387
- }
1388
- if (Object.keys(params).length > 0) {
1389
- callConfig.params = params;
1390
- }
1391
- }
1392
- actions.push({
1393
- id: `call_${targetSlug}`,
1394
- type: "call_workflow",
1395
- mode: "auto",
1396
- config: callConfig
1397
- });
1398
- const transName = transitionCount === 0 ? "execute" : `on_${getStateSuffix(prevState)}_complete`;
1399
- transitions.push({
1400
- name: transName,
1401
- from: [prevState],
1402
- to: stateName,
1403
- actions
1404
- });
1405
- prevState = stateName;
1406
- transitionCount++;
1407
- }
1408
- states.push({ name: "done", type: "END", on_enter: [], during: [], on_exit: [] });
1409
- const finalActions = [];
1410
- if (pendingSyncStmts.length > 0) {
1411
- const block = t.blockStatement(pendingSyncStmts);
1412
- const transpiled = transpileBlock(block, {
1413
- allMutable: true,
1414
- derivedVarMap: buildDerivedVarMap(awaitBindings),
1415
- parameterMap
1416
- });
1417
- if (!transpiled.pure) bodyIsPure = false;
1418
- finalActions.push({
1419
- id: "post_final",
1420
- type: "eval",
1421
- mode: "auto",
1422
- config: { expression: transpiled.expression }
1423
- });
1424
- }
1425
- const finalTransName = transitionCount === 0 ? "execute" : `on_${getStateSuffix(prevState)}_complete`;
1426
- transitions.push({
1427
- name: finalTransName,
1428
- from: [prevState],
1429
- to: "done",
1430
- actions: finalActions
1431
- });
1432
- return { states, transitions, bodyIsPure };
1433
- }
1434
- function uniqueState(base, used) {
1435
- if (!used.has(base)) {
1436
- used.add(base);
1437
- return base;
1438
- }
1439
- let suffix = 2;
1440
- while (used.has(`${base}_${suffix}`)) suffix++;
1441
- const name = `${base}_${suffix}`;
1442
- used.add(name);
1443
- return name;
1444
- }
1445
- function getStateSuffix(stateName) {
1446
- return stateName.replace(/^awaiting_/, "");
1447
- }
1448
- function transpileArgExpression(expr, bindings) {
1449
- if (t.isIdentifier(expr)) {
1450
- const replacement = bindings.get(expr.name);
1451
- if (replacement) return { expression: replacement };
1452
- return { expression: `state_data.${toSnakeCase(expr.name)}` };
1453
- }
1454
- if (t.isMemberExpression(expr) && t.isIdentifier(expr.object)) {
1455
- const replacement = bindings.get(expr.object.name);
1456
- if (replacement && t.isIdentifier(expr.property)) {
1457
- return { expression: `${replacement}.${expr.property.name}` };
1458
- }
1459
- }
1460
- if (t.isStringLiteral(expr)) return { expression: `'${expr.value}'` };
1461
- if (t.isNumericLiteral(expr)) return { expression: String(expr.value) };
1462
- if (t.isBooleanLiteral(expr)) return { expression: String(expr.value) };
1463
- const { transpile } = (init_ts_to_expression(), __toCommonJS(ts_to_expression_exports));
1464
- return { expression: transpile(expr, { derivedVarMap: buildDerivedVarMap(bindings) }) };
1465
- }
1466
- function buildDerivedVarMap(bindings) {
1467
- const map = /* @__PURE__ */ new Map();
1468
- for (const [varName, path] of bindings) {
1469
- map.set(varName, t.identifier(path));
1470
- }
1471
- return map;
1472
- }
1473
- function toKebabCase(name) {
1474
- return name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
1475
- }
1476
- function toSnakeCase(name) {
1477
- return name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
1478
- }
1479
-
1480
- // src/incremental-compiler.ts
1481
- function hashContent(content) {
1482
- let hash = 5381;
1483
- for (let i = 0; i < content.length; i++) {
1484
- hash = (hash << 5) + hash + content.charCodeAt(i);
1485
- hash |= 0;
1486
- }
1487
- return (hash >>> 0).toString(36);
1488
- }
1489
- function extractImports(source) {
1490
- const imports = [];
1491
- const regex = /import\s+.*?from\s+['"](\.[^'"]+)['"]/g;
1492
- let match;
1493
- while ((match = regex.exec(source)) !== null) {
1494
- imports.push(match[1]);
1495
- }
1496
- return imports;
1497
- }
1498
- function resolveImport(fromFile, importPath, availableFiles) {
1499
- const parts = fromFile.split("/");
1500
- parts.pop();
1501
- const dir = parts.join("/");
1502
- const segments = (dir ? dir + "/" + importPath : importPath).split("/");
1503
- const resolved = [];
1504
- for (const seg of segments) {
1505
- if (seg === ".") continue;
1506
- if (seg === "..") {
1507
- resolved.pop();
1508
- continue;
1509
- }
1510
- resolved.push(seg);
1511
- }
1512
- const basePath = resolved.join("/");
1513
- const extensions = ["", ".ts", ".tsx", ".js", ".jsx"];
1514
- for (const ext of extensions) {
1515
- const candidate = basePath + ext;
1516
- if (availableFiles.includes(candidate)) {
1517
- return candidate;
1518
- }
1519
- }
1520
- for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
1521
- const candidate = basePath + "/index" + ext;
1522
- if (availableFiles.includes(candidate)) {
1523
- return candidate;
1524
- }
1525
- }
1526
- return null;
1527
- }
1528
- function buildDependencyGraph(files) {
1529
- const availableFiles = Object.keys(files);
1530
- const edges = [];
1531
- const dependents = /* @__PURE__ */ new Map();
1532
- const dependencies = /* @__PURE__ */ new Map();
1533
- for (const filename of availableFiles) {
1534
- dependents.set(filename, /* @__PURE__ */ new Set());
1535
- dependencies.set(filename, /* @__PURE__ */ new Set());
1536
- }
1537
- for (const [filename, source] of Object.entries(files)) {
1538
- const imports = extractImports(source);
1539
- for (const imp of imports) {
1540
- const resolved = resolveImport(filename, imp, availableFiles);
1541
- if (resolved && resolved !== filename) {
1542
- edges.push({ from: filename, to: resolved });
1543
- dependencies.get(filename).add(resolved);
1544
- if (!dependents.has(resolved)) dependents.set(resolved, /* @__PURE__ */ new Set());
1545
- dependents.get(resolved).add(filename);
1546
- }
1547
- }
1548
- }
1549
- return { edges, dependents, dependencies };
1550
- }
1551
- function computeTransitiveDirtySet(directlyChanged, dependents) {
1552
- const dirty = new Set(directlyChanged);
1553
- const queue = [...directlyChanged];
1554
- while (queue.length > 0) {
1555
- const file = queue.shift();
1556
- const deps = dependents.get(file);
1557
- if (!deps) continue;
1558
- for (const dep of deps) {
1559
- if (!dirty.has(dep)) {
1560
- dirty.add(dep);
1561
- queue.push(dep);
1562
- }
1563
- }
1564
- }
1565
- return dirty;
1566
- }
1567
- function topologicalSort(files, dependencies) {
1568
- const inDegree = /* @__PURE__ */ new Map();
1569
- const dependentMap = /* @__PURE__ */ new Map();
1570
- for (const f of files) {
1571
- inDegree.set(f, 0);
1572
- dependentMap.set(f, /* @__PURE__ */ new Set());
1573
- }
1574
- for (const f of files) {
1575
- const deps = dependencies.get(f);
1576
- if (!deps) continue;
1577
- for (const dep of deps) {
1578
- if (files.includes(dep)) {
1579
- inDegree.set(f, (inDegree.get(f) || 0) + 1);
1580
- if (!dependentMap.has(dep)) dependentMap.set(dep, /* @__PURE__ */ new Set());
1581
- dependentMap.get(dep).add(f);
1582
- }
1583
- }
1584
- }
1585
- const queue = [];
1586
- for (const [f, degree] of inDegree) {
1587
- if (degree === 0) queue.push(f);
1588
- }
1589
- const order = [];
1590
- while (queue.length > 0) {
1591
- const f = queue.shift();
1592
- order.push(f);
1593
- for (const dependent of dependentMap.get(f) || []) {
1594
- const newDegree = (inDegree.get(dependent) || 1) - 1;
1595
- inDegree.set(dependent, newDegree);
1596
- if (newDegree === 0) queue.push(dependent);
1597
- }
1598
- }
1599
- for (const f of files) {
1600
- if (!order.includes(f)) {
1601
- order.push(f);
1602
- }
1603
- }
1604
- return order;
1605
- }
1606
- var IncrementalCache = class {
1607
- constructor() {
1608
- this.hashes = /* @__PURE__ */ new Map();
1609
- this.results = /* @__PURE__ */ new Map();
1610
- this.lastStats = {
1611
- totalCached: 0,
1612
- lastRecompiled: 0,
1613
- lastCacheHits: 0,
1614
- lastCompileMs: 0,
1615
- avgPerFileMs: 0,
1616
- cacheHitRate: 0
1617
- };
1618
- }
1619
- /**
1620
- * Determines which files are dirty given current file contents.
1621
- */
1622
- detectDirtyFiles(files, dependents) {
1623
- const currentFiles = new Set(Object.keys(files));
1624
- const contentChanged = [];
1625
- const removed = [];
1626
- const added = [];
1627
- for (const [filename, source] of Object.entries(files)) {
1628
- const hash = hashContent(source);
1629
- const cached = this.hashes.get(filename);
1630
- if (!cached) {
1631
- added.push(filename);
1632
- } else if (cached.hash !== hash) {
1633
- contentChanged.push(filename);
1634
- }
1635
- }
1636
- for (const cached of this.hashes.keys()) {
1637
- if (!currentFiles.has(cached)) {
1638
- removed.push(cached);
1639
- }
1640
- }
1641
- const directlyDirty = /* @__PURE__ */ new Set([...contentChanged, ...added]);
1642
- let dependencyDirty = [];
1643
- if (dependents && directlyDirty.size > 0) {
1644
- const allDirty = computeTransitiveDirtySet(directlyDirty, dependents);
1645
- dependencyDirty = [...allDirty].filter((f) => !directlyDirty.has(f));
1646
- }
1647
- return { contentChanged, dependencyDirty, removed, added };
1648
- }
1649
- /**
1650
- * Updates the cache with a compilation result.
1651
- */
1652
- set(filename, source, result) {
1653
- this.hashes.set(filename, {
1654
- hash: hashContent(source),
1655
- compiledAt: Date.now(),
1656
- size: source.length
1657
- });
1658
- this.results.set(filename, result);
1659
- }
1660
- /**
1661
- * Gets a cached result.
1662
- */
1663
- get(filename) {
1664
- return this.results.get(filename);
1665
- }
1666
- /**
1667
- * Checks if a file is in cache and not dirty.
1668
- */
1669
- has(filename) {
1670
- return this.results.has(filename);
1671
- }
1672
- /**
1673
- * Removes a file from cache.
1674
- */
1675
- delete(filename) {
1676
- this.hashes.delete(filename);
1677
- this.results.delete(filename);
1678
- }
1679
- /**
1680
- * Clears all cached data.
1681
- */
1682
- clear() {
1683
- this.hashes.clear();
1684
- this.results.clear();
1685
- }
1686
- /**
1687
- * Updates stats after a compilation run.
1688
- */
1689
- updateStats(recompiled, cacheHits, compileMs) {
1690
- const total = recompiled + cacheHits;
1691
- this.lastStats = {
1692
- totalCached: this.results.size,
1693
- lastRecompiled: recompiled,
1694
- lastCacheHits: cacheHits,
1695
- lastCompileMs: compileMs,
1696
- avgPerFileMs: recompiled > 0 ? compileMs / recompiled : 0,
1697
- cacheHitRate: total > 0 ? cacheHits / total : 0
1698
- };
1699
- }
1700
- /**
1701
- * Gets compilation statistics.
1702
- */
1703
- getStats() {
1704
- return { ...this.lastStats };
1705
- }
1706
- /**
1707
- * Gets all cached filenames.
1708
- */
1709
- getCachedFiles() {
1710
- return [...this.results.keys()];
1711
- }
1712
- /**
1713
- * Gets all cached results.
1714
- */
1715
- getAllResults() {
1716
- return new Map(this.results);
1717
- }
1718
- };
1719
-
1720
- // src/project-compiler.ts
1721
- function parseConfig(source) {
1722
- const config = {};
1723
- const slugMatch = source.match(/slug:\s*['"]([^'"]+)['"]/);
1724
- if (slugMatch) config.slug = slugMatch[1];
1725
- const nameMatch = source.match(/name:\s*['"]([^'"]+)['"]/);
1726
- if (nameMatch) config.name = nameMatch[1];
1727
- const versionMatch = source.match(/version:\s*['"]([^'"]+)['"]/);
1728
- if (versionMatch) config.version = versionMatch[1];
1729
- const descMatch = source.match(/description:\s*['"]([^'"]+)['"]/);
1730
- if (descMatch) config.description = descMatch[1];
1731
- const categoryArrayMatch = source.match(/category:\s*\[([^\]]+)\]/);
1732
- if (categoryArrayMatch) {
1733
- const items = categoryArrayMatch[1].match(/['"]([^'"]+)['"]/g);
1734
- if (items) {
1735
- config.category = items.map((s) => s.replace(/['"]/g, ""));
1736
- }
1737
- } else {
1738
- const categoryMatch = source.match(/category:\s*['"]([^'"]+)['"]/);
1739
- if (categoryMatch) config.category = categoryMatch[1];
1740
- }
1741
- const modeMatch = source.match(/mode:\s*['"]([^'"]+)['"]/);
1742
- if (modeMatch && (modeMatch[1] === "strict" || modeMatch[1] === "infer")) {
1743
- config.mode = modeMatch[1];
1744
- }
1745
- return config;
1746
- }
1747
- function parseModuleManifest(source) {
1748
- if (!source.includes("defineBlueprint") && !source.includes("defineModule")) return null;
1749
- const manifest = {};
1750
- const stringFields = ["slug", "name", "version", "description", "author", "license", "icon"];
1751
- for (const field of stringFields) {
1752
- const match = source.match(new RegExp(`${field}:\\s*['"]([^'"]+)['"]`));
1753
- if (match) manifest[field] = match[1];
1754
- }
1755
- const catArrayMatch = source.match(/category:\s*\[([^\]]+)\]/);
1756
- if (catArrayMatch) {
1757
- const items = catArrayMatch[1].match(/['"]([^'"]+)['"]/g);
1758
- if (items) {
1759
- manifest.category = items.map((s) => s.replace(/['"]/g, ""));
1760
- }
1761
- } else {
1762
- const catMatch = source.match(/category:\s*['"]([^'"]+)['"]/);
1763
- if (catMatch) manifest.category = catMatch[1];
1764
- }
1765
- const tagsMatch = source.match(/tags:\s*\[([^\]]*)\]/);
1766
- if (tagsMatch) {
1767
- manifest.tags = tagsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1768
- }
1769
- const modelsMatch = source.match(/models:\s*\[([^\]]*)\]/);
1770
- if (modelsMatch) {
1771
- manifest.models = modelsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1772
- }
1773
- const capsMatch = source.match(/capabilities:\s*\[([^\]]*)\]/);
1774
- if (capsMatch) {
1775
- manifest.capabilities = capsMatch[1].split(",").map((s) => s.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1776
- }
1777
- const routesBlock = extractArrayBlock(source, "routes");
1778
- if (routesBlock) {
1779
- const routes = [];
1780
- const routeRegex = /\{\s*path:\s*['"]([^'"]+)['"][^}]*\}/g;
1781
- let rm;
1782
- while ((rm = routeRegex.exec(routesBlock)) !== null) {
1783
- const entry = { path: rm[1] };
1784
- const labelMatch = rm[0].match(/label:\s*['"]([^'"]+)['"]/);
1785
- if (labelMatch) entry.label = labelMatch[1];
1786
- const groupMatch = rm[0].match(/group:\s*['"]([^'"]+)['"]/);
1787
- if (groupMatch) entry.group = groupMatch[1];
1788
- const iconMatch = rm[0].match(/icon:\s*['"]([^'"]+)['"]/);
1789
- if (iconMatch) entry.icon = iconMatch[1];
1790
- routes.push(entry);
1791
- }
1792
- if (routes.length > 0) manifest.routes = routes;
1793
- }
1794
- const actionsBlock = extractArrayBlock(source, "actions");
1795
- if (actionsBlock) {
1796
- const actions = [];
1797
- const actionRegex = /\{\s*id:\s*['"]([^'"]+)['"][^}]*\}/g;
1798
- let am;
1799
- while ((am = actionRegex.exec(actionsBlock)) !== null) {
1800
- const entry = { id: am[1] };
1801
- const descMatch = am[0].match(/description:\s*['"]([^'"]+)['"]/);
1802
- if (descMatch) entry.description = descMatch[1];
1803
- actions.push(entry);
1804
- }
1805
- if (actions.length > 0) manifest.actions = actions;
1806
- }
1807
- const contribsBlock = extractArrayBlock(source, "contributions");
1808
- if (contribsBlock) {
1809
- const contributions = [];
1810
- const contribRegex = /\{\s*slot:\s*['"]([^'"]+)['"][^}]*\}/g;
1811
- let cm;
1812
- while ((cm = contribRegex.exec(contribsBlock)) !== null) {
1813
- const entry = { slot: cm[1], view: "" };
1814
- const viewMatch = cm[0].match(/view:\s*['"]([^'"]+)['"]/);
1815
- if (viewMatch) entry.view = viewMatch[1];
1816
- const prioMatch = cm[0].match(/priority:\s*(\d+)/);
1817
- if (prioMatch) entry.priority = parseInt(prioMatch[1], 10);
1818
- contributions.push(entry);
1819
- }
1820
- if (contributions.length > 0) manifest.contributions = contributions;
1821
- }
1822
- const configSchemaBlock = extractObjectBlock(source, "configSchema");
1823
- if (configSchemaBlock) {
1824
- const configSchema = {};
1825
- const modelSlugMatch = configSchemaBlock.match(/modelSlug:\s*['"]([^'"]+)['"]/);
1826
- if (modelSlugMatch) configSchema.modelSlug = modelSlugMatch[1];
1827
- const defaultsBlock = extractObjectBlock(configSchemaBlock, "defaults");
1828
- if (defaultsBlock) {
1829
- const defaults = {};
1830
- const kvRegex = /(\w+):\s*(?:'([^']*)'|"([^"]*)"|(\d+(?:\.\d+)?)|(\btrue\b|\bfalse\b))/g;
1831
- let kv;
1832
- while ((kv = kvRegex.exec(defaultsBlock)) !== null) {
1833
- const key = kv[1];
1834
- const val = kv[2] ?? kv[3] ?? (kv[4] !== void 0 ? Number(kv[4]) : kv[5] === "true");
1835
- defaults[key] = val;
1836
- }
1837
- configSchema.defaults = defaults;
1838
- }
1839
- manifest.configSchema = configSchema;
1840
- }
1841
- const depsBlock = extractArrayBlock(source, "dependencies");
1842
- if (depsBlock) {
1843
- const dependencies = [];
1844
- const depBlocks = extractNestedObjects(depsBlock);
1845
- for (const block of depBlocks) {
1846
- const slugMatch = block.match(/slug:\s*['"]([^'"]+)['"]/);
1847
- if (!slugMatch) continue;
1848
- const entry = { slug: slugMatch[1] };
1849
- const verMatch = block.match(/version:\s*['"]([^'"]+)['"]/);
1850
- if (verMatch) entry.version = verMatch[1];
1851
- const reqMatch = block.match(/required:\s*(true|false)/);
1852
- if (reqMatch) entry.required = reqMatch[1] === "true";
1853
- const prefixMatch = block.match(/prefix:\s*['"]([^'"]+)['"]/);
1854
- if (prefixMatch) entry.routeConfig = { prefix: prefixMatch[1] };
1855
- const configBlock = extractObjectBlock(block, "config");
1856
- if (configBlock) {
1857
- const config = {};
1858
- const kvRegex = /(\w+):\s*(?:'([^']*)'|"([^"]*)"|(\d+(?:\.\d+)?)|(\btrue\b|\bfalse\b))/g;
1859
- let kv;
1860
- while ((kv = kvRegex.exec(configBlock)) !== null) {
1861
- const key = kv[1];
1862
- const val = kv[2] ?? kv[3] ?? (kv[4] !== void 0 ? Number(kv[4]) : kv[5] === "true");
1863
- config[key] = val;
1864
- }
1865
- if (Object.keys(config).length > 0) entry.config = config;
1866
- }
1867
- dependencies.push(entry);
1868
- }
1869
- if (dependencies.length > 0) manifest.dependencies = dependencies;
1870
- }
1871
- const hasRichFields = manifest.routes || manifest.actions || manifest.contributions || manifest.capabilities || manifest.dependencies || manifest.configSchema;
1872
- return hasRichFields ? manifest : null;
1873
- }
1874
- function parseDependencyRouteConfigs(files) {
1875
- const result = /* @__PURE__ */ new Map();
1876
- let configSource;
1877
- for (const [filename, source] of Object.entries(files)) {
1878
- if (isConfigFile(filename)) {
1879
- configSource = source;
1880
- break;
1881
- }
1882
- }
1883
- if (!configSource) return result;
1884
- const depsBlock = extractArrayBlock(configSource, "dependencies");
1885
- if (!depsBlock) return result;
1886
- const depObjects = extractNestedObjects(depsBlock);
1887
- for (const depSrc of depObjects) {
1888
- const slugMatch = depSrc.match(/slug:\s*['"]([^'"]+)['"]/);
1889
- if (!slugMatch) continue;
1890
- const slug = slugMatch[1];
1891
- const rcBlock = extractObjectBlock(depSrc, "routeConfig");
1892
- if (!rcBlock) continue;
1893
- const entry = {};
1894
- const prefixMatch = rcBlock.match(/prefix:\s*['"]([^'"]+)['"]/);
1895
- if (prefixMatch) entry.prefix = prefixMatch[1];
1896
- const routesBlock = extractObjectBlock(rcBlock, "routes");
1897
- if (routesBlock) {
1898
- const overrides = {};
1899
- const overrideRegex = /['"]([^'"]+)['"]\s*:\s*(?:['"]([^'"]+)['"]|(false))/g;
1900
- let om;
1901
- while ((om = overrideRegex.exec(routesBlock)) !== null) {
1902
- overrides[om[1]] = om[3] === "false" ? false : om[2];
1903
- }
1904
- if (Object.keys(overrides).length > 0) entry.routes = overrides;
1905
- }
1906
- result.set(slug, entry);
1907
- }
1908
- return result;
1909
- }
1910
- function extractNestedObjects(source) {
1911
- const objects = [];
1912
- let depth = 0;
1913
- let start = -1;
1914
- for (let i = 0; i < source.length; i++) {
1915
- if (source[i] === "{") {
1916
- if (depth === 0) start = i;
1917
- depth++;
1918
- } else if (source[i] === "}") {
1919
- depth--;
1920
- if (depth === 0 && start >= 0) {
1921
- objects.push(source.slice(start, i + 1));
1922
- start = -1;
1923
- }
1924
- }
1925
- }
1926
- return objects;
1927
- }
1928
- function extractObjectBlock(source, fieldName) {
1929
- const pattern = new RegExp(`${fieldName}:\\s*\\{`);
1930
- const match = pattern.exec(source);
1931
- if (!match) return null;
1932
- let depth = 1;
1933
- const startIdx = match.index + match[0].length;
1934
- for (let i = startIdx; i < source.length; i++) {
1935
- if (source[i] === "{") depth++;
1936
- else if (source[i] === "}") {
1937
- depth--;
1938
- if (depth === 0) return source.slice(startIdx, i);
1939
- }
1940
- }
1941
- return null;
1942
- }
1943
- function extractArrayBlock(source, fieldName) {
1944
- const startPattern = new RegExp(`${fieldName}:\\s*\\[`);
1945
- const match = startPattern.exec(source);
1946
- if (!match) return null;
1947
- let depth = 1;
1948
- const startIdx = match.index + match[0].length;
1949
- for (let i = startIdx; i < source.length; i++) {
1950
- if (source[i] === "[") depth++;
1951
- else if (source[i] === "]") {
1952
- depth--;
1953
- if (depth === 0) return source.slice(startIdx, i);
1954
- }
1955
- }
1956
- return null;
1957
- }
1958
- function isWorkflowFile(filename) {
1959
- return /\.workflow\.(tsx?|jsx?)$/.test(filename);
1960
- }
1961
- function isModelFile(filename) {
1962
- return /models\/.*\.(ts|tsx)$/.test(filename) && !filename.endsWith(".test.ts");
1963
- }
1964
- function isServerActionFile(filename) {
1965
- return /\.server\.(ts|tsx)$/.test(filename);
1966
- }
1967
- function isActionFile(filename) {
1968
- return /^actions\/.*\.(ts|tsx)$/.test(filename) && !filename.endsWith(".server.ts") && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx");
1969
- }
1970
- function isComponentFile(filename) {
1971
- return /components\/.*\.(tsx?|jsx?)$/.test(filename) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx");
1972
- }
1973
- function isPageFile2(filename) {
1974
- return (/pages\/.*\.(tsx?|jsx?)$/.test(filename) || /app\/.*\.(tsx?|jsx?)$/.test(filename)) && !filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx") && !filename.includes("layout");
1975
- }
1976
- function isAppDirFile(filename) {
1977
- return /^app\//.test(filename);
1978
- }
1979
- function isConfigFile(filename) {
1980
- return /mm\.config\.(ts|tsx|js)$/.test(filename);
1981
- }
1982
- function isModuleManifestFile(filename) {
1983
- return /mm\.module\.(ts|tsx|js)$/.test(filename);
1984
- }
1985
- function isCompilableFile(filename) {
1986
- return isWorkflowFile(filename) || isModelFile(filename) || isServerActionFile(filename) || isActionFile(filename) || isPageFile2(filename) || isComponentFile(filename);
1987
- }
1988
- function compileFile(filename, source, mode) {
1989
- const errors = [];
1990
- try {
1991
- const parserPlugins = filename.endsWith(".tsx") || filename.endsWith(".jsx") ? ["typescript", "jsx"] : ["typescript"];
1992
- const result = transformSync3(source, {
1993
- filename,
1994
- plugins: [[babelPlugin, { mode }]],
1995
- parserOpts: { plugins: parserPlugins, attachComment: true }
1996
- });
1997
- const ir = result?.metadata?.mindmatrixIR ?? null;
1998
- if (ir?.metadata) {
1999
- const meta = ir.metadata;
2000
- const fileErrors = meta.errors;
2001
- const fileWarnings = meta.warnings;
2002
- if (fileErrors) {
2003
- for (const e of fileErrors) {
2004
- errors.push({
2005
- file: filename,
2006
- message: e.message,
2007
- line: e.line,
2008
- column: e.column,
2009
- endLine: e.line,
2010
- endColumn: e.column !== void 0 ? e.column + 10 : void 0,
2011
- severity: "error"
2012
- });
2013
- }
2014
- }
2015
- if (fileWarnings) {
2016
- for (const w of fileWarnings) {
2017
- errors.push({
2018
- file: filename,
2019
- message: w.message,
2020
- line: w.line,
2021
- column: w.column,
2022
- endLine: w.line,
2023
- endColumn: w.column !== void 0 ? w.column + 10 : void 0,
2024
- severity: "warning"
2025
- });
2026
- }
2027
- }
2028
- }
2029
- return { ir, errors };
2030
- } catch (err) {
2031
- const errMsg = err.message;
2032
- const locMatch = errMsg.match(/\((\d+):(\d+)\)/);
2033
- const line = locMatch ? parseInt(locMatch[1], 10) : void 0;
2034
- const column = locMatch ? parseInt(locMatch[2], 10) : void 0;
2035
- errors.push({
2036
- file: filename,
2037
- message: "Compilation failed: " + errMsg,
2038
- line,
2039
- column,
2040
- endLine: line,
2041
- endColumn: column !== void 0 ? column + 1 : void 0,
2042
- severity: "error"
2043
- });
2044
- return { ir: null, errors };
2045
- }
2046
- }
2047
- function deduplicateActions(actions) {
2048
- const seen = /* @__PURE__ */ new Set();
2049
- const result = [];
2050
- for (const action of actions) {
2051
- const key = JSON.stringify(action);
2052
- if (!seen.has(key)) {
2053
- seen.add(key);
2054
- result.push(action);
2055
- }
2056
- }
2057
- return result;
2058
- }
2059
- function mergeIRs(irs, config) {
2060
- if (irs.length === 0) {
2061
- return createEmptyIR(config);
2062
- }
2063
- if (irs.length === 1) {
2064
- return applyConfig(irs[0], config);
2065
- }
2066
- const fieldMap = /* @__PURE__ */ new Map();
2067
- for (const ir of irs) {
2068
- for (const field of ir.fields) {
2069
- if (!fieldMap.has(field.name)) {
2070
- fieldMap.set(field.name, field);
2071
- } else {
2072
- const existing = fieldMap.get(field.name);
2073
- if (field.required && !existing.required) {
2074
- existing.required = true;
2075
- }
2076
- if (field.default_value != null && field.default_value !== "" && field.default_value !== 0 && field.default_value !== false) {
2077
- if (existing.default_value == null || existing.default_value === "" || existing.default_value === 0 || existing.default_value === false) {
2078
- existing.default_value = field.default_value;
2079
- }
2080
- }
2081
- }
2082
- }
2083
- }
2084
- const stateMap = /* @__PURE__ */ new Map();
2085
- for (const ir of irs) {
2086
- for (const state of ir.states) {
2087
- if (stateMap.has(state.name)) {
2088
- const existing = stateMap.get(state.name);
2089
- existing.on_enter = deduplicateActions([...existing.on_enter, ...state.on_enter]);
2090
- existing.on_exit = deduplicateActions([...existing.on_exit, ...state.on_exit]);
2091
- existing.during = deduplicateActions([...existing.during, ...state.during]);
2092
- if (state.on_event) {
2093
- existing.on_event = [...existing.on_event || [], ...state.on_event];
2094
- }
2095
- } else {
2096
- stateMap.set(state.name, { ...state });
2097
- }
2098
- }
2099
- }
2100
- const transitionMap = /* @__PURE__ */ new Map();
2101
- for (const ir of irs) {
2102
- for (const transition of ir.transitions) {
2103
- if (!transitionMap.has(transition.name)) {
2104
- transitionMap.set(transition.name, transition);
2105
- }
2106
- }
2107
- }
2108
- const events = [];
2109
- for (const ir of irs) {
2110
- if (ir.on_event) {
2111
- events.push(...ir.on_event);
2112
- }
2113
- }
2114
- const viewTrees = [];
2115
- for (const ir of irs) {
2116
- const views = ir.views;
2117
- if (views?.default) {
2118
- viewTrees.push(views.default);
2119
- }
2120
- }
2121
- const extensions = {};
2122
- for (const ir of irs) {
2123
- if (ir.extensions) {
2124
- for (const [key, islands] of Object.entries(ir.extensions)) {
2125
- if (!extensions[key]) extensions[key] = [];
2126
- extensions[key].push(...islands);
2127
- }
2128
- }
2129
- }
2130
- const metadata = {};
2131
- for (const ir of irs) {
2132
- if (ir.metadata) {
2133
- for (const [key, value] of Object.entries(ir.metadata)) {
2134
- if (key === "errors" || key === "warnings") continue;
2135
- metadata[key] = value;
2136
- }
2137
- }
2138
- }
2139
- const roleMap = /* @__PURE__ */ new Map();
2140
- for (const ir of irs) {
2141
- for (const role of ir.roles) {
2142
- if (!roleMap.has(role.name)) {
2143
- roleMap.set(role.name, role);
2144
- }
2145
- }
2146
- }
2147
- const base = irs[0];
2148
- const merged = {
2149
- slug: base.slug,
2150
- name: base.name,
2151
- version: base.version,
2152
- description: base.description,
2153
- category: base.category,
2154
- fields: Array.from(fieldMap.values()),
2155
- states: Array.from(stateMap.values()),
2156
- transitions: Array.from(transitionMap.values()),
2157
- roles: Array.from(roleMap.values()),
2158
- tags: base.tags,
2159
- metadata
2160
- };
2161
- if (events.length > 0) {
2162
- merged.on_event = events;
2163
- }
2164
- if (Object.keys(extensions).length > 0) {
2165
- merged.extensions = extensions;
2166
- }
2167
- if (viewTrees.length === 1) {
2168
- merged.views = { default: viewTrees[0] };
2169
- } else if (viewTrees.length > 1) {
2170
- const rootView = {
2171
- id: "project-root",
2172
- component: "Stack",
2173
- children: viewTrees
2174
- };
2175
- merged.views = { default: rootView };
2176
- }
2177
- return applyConfig(merged, config);
2178
- }
2179
- function applyConfig(ir, config) {
2180
- if (config.slug) ir.slug = config.slug;
2181
- if (config.name) ir.name = config.name;
2182
- if (config.version) ir.version = config.version;
2183
- if (config.description !== void 0) ir.description = config.description;
2184
- if (config.category) ir.category = config.category;
2185
- if (!ir.metadata) ir.metadata = {};
2186
- ir.metadata.stable_id = "def-" + ir.slug;
2187
- ir.metadata.provenance = {
2188
- frontend: "react-compiler",
2189
- source: "project",
2190
- compiler_version: "2.0.0"
2191
- };
2192
- return ir;
2193
- }
2194
- function createEmptyIR(config) {
2195
- return {
2196
- slug: config.slug || "project",
2197
- name: config.name || "Project",
2198
- version: config.version || "0.1.0",
2199
- description: config.description,
2200
- category: config.category || "workflow",
2201
- fields: [],
2202
- states: [{
2203
- name: "draft",
2204
- type: "START",
2205
- on_enter: [],
2206
- during: [],
2207
- on_exit: []
2208
- }],
2209
- transitions: [],
2210
- roles: [],
2211
- tags: [],
2212
- metadata: {
2213
- stable_id: "def-" + (config.slug || "project"),
2214
- provenance: {
2215
- frontend: "react-compiler",
2216
- source: "project",
2217
- compiler_version: "2.0.0"
2218
- }
2219
- }
2220
- };
2221
- }
2222
- function resolveImportLinks(files, compilableFiles) {
2223
- const links = [];
2224
- for (const filename of compilableFiles) {
2225
- const source = files[filename];
2226
- const importRegex = /import\s+(?:type\s+)?(?:\{([^}]+)\}|(\w+))\s+from\s+['"](\.[^'"]+)['"]/g;
2227
- let match;
2228
- while ((match = importRegex.exec(source)) !== null) {
2229
- const namedImports = match[1];
2230
- const defaultImport = match[2];
2231
- const importPath = match[3];
2232
- const resolved = resolveImport(filename, importPath, Object.keys(files));
2233
- if (!resolved || resolved === filename) continue;
2234
- let linkType = "unknown";
2235
- if (isModelFile(resolved)) {
2236
- linkType = "data-source";
2237
- } else if (isServerActionFile(resolved)) {
2238
- linkType = "action";
2239
- } else if (/components\//.test(resolved)) {
2240
- linkType = "component";
2241
- }
2242
- const symbols = [];
2243
- if (namedImports) {
2244
- symbols.push(...namedImports.split(",").map((s) => s.trim().split(" as ")[0].trim()).filter(Boolean));
2245
- }
2246
- if (defaultImport) {
2247
- symbols.push(defaultImport);
2248
- }
2249
- links.push({
2250
- fromFile: filename,
2251
- toFile: resolved,
2252
- linkType,
2253
- symbols
2254
- });
2255
- }
2256
- }
2257
- return links;
2258
- }
2259
- function resolveCompilationOrder(files, compilableFiles) {
2260
- const { dependencies } = buildDependencyGraph(files);
2261
- return topologicalSort(compilableFiles, dependencies);
2262
- }
2263
- function extractComponentProps(source) {
2264
- const match = source.match(/function\s+\w+\s*\(\s*\{([^}]+)\}/);
2265
- if (!match) return [];
2266
- return match[1].split(",").map((p) => p.trim().split(/[\s=:]/)[0].replace(/^\.{3}/, "").trim()).filter(Boolean);
2267
- }
2268
- function generateModuleTypeStubs(childDefinitions, _modelResults) {
2269
- const stubs = {};
2270
- for (const child of childDefinitions) {
2271
- if (!child.slug) continue;
2272
- const category = child.category;
2273
- const isModel = category === "data" || Array.isArray(category) && category.includes("model") || Array.isArray(category) && category.includes("data");
2274
- if (!isModel || !child.fields || child.fields.length === 0) continue;
2275
- const interfaceName = slugToInterfaceName(child.slug) + "Fields";
2276
- const lines = [
2277
- `// Auto-generated type stub for model: ${child.slug}`,
2278
- `// Do not edit \u2014 regenerated on each build.`,
2279
- ``,
2280
- `export interface ${interfaceName} {`
2281
- ];
2282
- for (const field of child.fields) {
2283
- const tsType = irFieldTypeToTs(field.type || "string");
2284
- const optional = field.required ? "" : "?";
2285
- const desc = field.description;
2286
- if (desc) {
2287
- lines.push(` /** ${desc} */`);
2288
- }
2289
- lines.push(` ${field.name}${optional}: ${tsType};`);
2290
- }
2291
- lines.push(`}`);
2292
- lines.push(``);
2293
- lines.push(`export declare const ${slugToCamelCase(child.slug)}Slug: '${child.slug}';`);
2294
- lines.push(``);
2295
- stubs[`types/modules/${child.slug}.d.ts`] = lines.join("\n");
2296
- }
2297
- return stubs;
2298
- }
2299
- function slugToInterfaceName(slug) {
2300
- return slug.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
2301
- }
2302
- function slugToCamelCase(slug) {
2303
- const parts = slug.split("-");
2304
- return parts[0] + parts.slice(1).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
2305
- }
2306
- function irFieldTypeToTs(type) {
2307
- switch (type) {
2308
- case "string":
2309
- case "text":
2310
- case "email":
2311
- case "url":
2312
- case "phone":
2313
- case "slug":
2314
- case "color":
2315
- case "password":
2316
- return "string";
2317
- case "number":
2318
- case "integer":
2319
- case "float":
2320
- case "currency":
2321
- case "percent":
2322
- return "number";
2323
- case "boolean":
2324
- case "toggle":
2325
- return "boolean";
2326
- case "date":
2327
- case "datetime":
2328
- case "timestamp":
2329
- return "string";
2330
- case "json":
2331
- case "object":
2332
- return "Record<string, unknown>";
2333
- case "array":
2334
- return "unknown[]";
2335
- case "enum":
2336
- return "string";
2337
- case "file":
2338
- case "image":
2339
- return "string";
2340
- default:
2341
- return "unknown";
2342
- }
2343
- }
2344
- function buildComposedResult(files, fileIRs, config, errors, warnings, options = {}) {
2345
- const usePhase2 = options.usePhase2Modules !== false;
2346
- const workflowIRs = [];
2347
- const modelIRs = [];
2348
- const actionDefinitionIRs = [];
2349
- const serverActionEntries = [];
2350
- let modelResults;
2351
- let actionResult;
2352
- let routeResult;
2353
- const componentDefinitions = {};
2354
- for (const [filename, ir] of Object.entries(fileIRs)) {
2355
- if (isWorkflowFile(filename)) {
2356
- workflowIRs.push(ir);
2357
- } else if (isModelFile(filename)) {
2358
- if (!ir.category || ir.category === "workflow") {
2359
- ir.category = "data";
2360
- }
2361
- modelIRs.push(ir);
2362
- } else if (isServerActionFile(filename)) {
2363
- const meta = ir.metadata;
2364
- const actions = meta?.serverActions;
2365
- if (actions) {
2366
- for (const action of actions) {
2367
- serverActionEntries.push({
2368
- name: action.name,
2369
- sourceFile: filename,
2370
- async: action.async,
2371
- params: action.params,
2372
- description: action.description
2373
- });
2374
- }
2375
- }
2376
- } else if (isComponentFile(filename)) {
2377
- const views = ir.views;
2378
- const experience = views?.default ?? ir.experience;
2379
- if (experience) {
2380
- const baseName = filename.split("/").pop()?.replace(/\.(tsx?|jsx?)$/, "") || "Component";
2381
- const source = files[filename];
2382
- const props = source ? extractComponentProps(source) : [];
2383
- componentDefinitions[baseName] = { experience, props };
2384
- }
2385
- }
2386
- }
2387
- for (const [filename, source] of Object.entries(files)) {
2388
- if (isActionFile(filename)) {
2389
- const result = extractAction(source, filename);
2390
- if (result) {
2391
- actionDefinitionIRs.push(result.ir);
2392
- for (const w of result.warnings) {
2393
- warnings.push({ file: filename, message: w, severity: "warning" });
2394
- }
2395
- }
2396
- }
2397
- }
2398
- if (usePhase2) {
2399
- const modelFiles = {};
2400
- for (const [filename, source] of Object.entries(files)) {
2401
- if (isModelFile(filename)) {
2402
- modelFiles[filename] = source;
2403
- }
2404
- }
2405
- if (Object.keys(modelFiles).length > 0) {
2406
- modelResults = compileModels(modelFiles, { mode: options.mode || "infer" });
2407
- modelIRs.length = 0;
2408
- for (const [, result] of modelResults) {
2409
- modelIRs.push(result.ir);
2410
- }
2411
- }
2412
- const actionFiles = {};
2413
- for (const [filename, source] of Object.entries(files)) {
2414
- if (isServerActionFile(filename)) {
2415
- actionFiles[filename] = source;
2416
- }
2417
- }
2418
- if (Object.keys(actionFiles).length > 0) {
2419
- actionResult = compileActions(actionFiles, {
2420
- mode: options.mode || "infer",
2421
- blueprintSlug: config.slug || "app"
2422
- });
2423
- serverActionEntries.length = 0;
2424
- for (const reg of actionResult.actions) {
2425
- serverActionEntries.push({
2426
- name: reg.name,
2427
- sourceFile: reg.sourceFile,
2428
- async: reg.async,
2429
- params: reg.params,
2430
- description: reg.description,
2431
- body: reg.body,
2432
- returnType: reg.returnType
2433
- });
2434
- }
2435
- }
2436
- const appFiles = {};
2437
- for (const [filename, source] of Object.entries(files)) {
2438
- if (isAppDirFile(filename) || isPageFile2(filename)) {
2439
- appFiles[filename] = source;
2440
- }
2441
- }
2442
- if (Object.keys(appFiles).length > 0) {
2443
- routeResult = extractRoutes(appFiles, {
2444
- slugPrefix: config.slug || "app"
2445
- });
2446
- }
2447
- }
2448
- const childDefinitions = [
2449
- ...workflowIRs,
2450
- ...modelIRs,
2451
- ...actionDefinitionIRs
2452
- ];
2453
- let routeTable = [];
2454
- if (routeResult) {
2455
- routeTable = routeResult.routes.map((r) => ({
2456
- path: r.path,
2457
- stateName: r.stateName,
2458
- sourceFile: r.sourceFile,
2459
- params: r.params
2460
- }));
2461
- childDefinitions.push(routeResult.routerIR);
2462
- } else {
2463
- const pageFiles = [];
2464
- for (const filename of Object.keys(files)) {
2465
- if (isPageFile2(filename)) {
2466
- pageFiles.push({
2467
- relativePath: filename,
2468
- absolutePath: filename
2469
- });
2470
- const routePath = filename.replace(/^pages\//, "").replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
2471
- const stateName = pathToStateName(routePath);
2472
- const urlPattern = pathToUrlPattern(filename, filename.split("/").pop() || "page.tsx");
2473
- const params = extractParams(filename);
2474
- routeTable.push({
2475
- path: urlPattern,
2476
- stateName,
2477
- sourceFile: filename,
2478
- params
2479
- });
2480
- }
2481
- }
2482
- if (pageFiles.length > 0) {
2483
- const routerWorkflow = extractRouterWorkflow(pageFiles, {
2484
- slug: config.slug ? config.slug + "-router" : "app-router",
2485
- pageFileName: pageFiles[0]?.relativePath.split("/").pop() || "page.tsx"
2486
- });
2487
- childDefinitions.push(routerWorkflow);
2488
- }
2489
- }
2490
- if (options.resolvedModules && options.resolvedModules.length > 0) {
2491
- const depConfigs = parseDependencyRouteConfigs(files);
2492
- for (const mod of options.resolvedModules) {
2493
- const depConfig = depConfigs.get(mod.slug);
2494
- const prefix = depConfig?.prefix ?? `/${mod.slug.replace(/^mod-/, "")}`;
2495
- const routeOverrides = depConfig?.routes;
2496
- for (const modRoute of mod.routeTable) {
2497
- if (routeOverrides) {
2498
- const override = routeOverrides[modRoute.path];
2499
- if (override === false) continue;
2500
- if (typeof override === "string") {
2501
- const existingPaths2 = new Set(routeTable.map((r) => r.path));
2502
- if (!existingPaths2.has(override)) {
2503
- routeTable.push({
2504
- path: override,
2505
- stateName: `MOD_${mod.slug.replace(/-/g, "_").toUpperCase()}_${modRoute.stateName}`,
2506
- sourceFile: modRoute.sourceFile,
2507
- params: modRoute.params,
2508
- moduleSlug: mod.slug
2509
- });
2510
- }
2511
- continue;
2512
- }
2513
- }
2514
- const prefixedPath = prefix + (modRoute.path === "/" ? "" : modRoute.path);
2515
- const existingPaths = new Set(routeTable.map((r) => r.path));
2516
- if (!existingPaths.has(prefixedPath)) {
2517
- routeTable.push({
2518
- path: prefixedPath,
2519
- stateName: `MOD_${mod.slug.replace(/-/g, "_").toUpperCase()}_${modRoute.stateName}`,
2520
- sourceFile: modRoute.sourceFile,
2521
- params: modRoute.params,
2522
- moduleSlug: mod.slug
2523
- });
2524
- }
2525
- }
2526
- }
2527
- }
2528
- if (options.resolvedModules && options.resolvedModules.length > 0) {
2529
- for (const mod of options.resolvedModules) {
2530
- if (mod.serverActions && mod.serverActions.length > 0) {
2531
- for (const action of mod.serverActions) {
2532
- const namespacedName = `${mod.slug}:${action.name}`;
2533
- if (!serverActionEntries.some((a) => a.name === namespacedName)) {
2534
- serverActionEntries.push({
2535
- ...action,
2536
- name: namespacedName,
2537
- sourceFile: `${mod.slug}/${action.sourceFile}`
2538
- });
2539
- }
2540
- }
2541
- }
2542
- }
2543
- }
2544
- const allIRs = Object.values(fileIRs);
2545
- const parentIR = mergeIRs(allIRs, config);
2546
- if (!parentIR.metadata) parentIR.metadata = {};
2547
- const parentMeta = parentIR.metadata;
2548
- parentMeta.childSlugs = childDefinitions.map((c) => c.slug);
2549
- parentMeta.serverActions = serverActionEntries;
2550
- if (routeTable.length > 0) {
2551
- parentMeta.routeTable = routeTable;
2552
- }
2553
- parentMeta.composition = {
2554
- workflowCount: workflowIRs.length,
2555
- modelCount: modelIRs.length,
2556
- actionCount: actionDefinitionIRs.length,
2557
- serverActionCount: serverActionEntries.length,
2558
- routeCount: routeTable.length,
2559
- componentCount: Object.keys(componentDefinitions).length,
2560
- totalFiles: Object.keys(files).length
2561
- };
2562
- if (Object.keys(componentDefinitions).length > 0) {
2563
- parentMeta.componentDefinitions = componentDefinitions;
2564
- }
2565
- if (actionResult) {
2566
- parentMeta.actionEndpoints = actionResult.actions.map((a) => ({
2567
- actionId: a.actionId,
2568
- endpoint: a.endpoint,
2569
- group: a.group
2570
- }));
2571
- }
2572
- for (const [filename, source] of Object.entries(files)) {
2573
- if (isConfigFile(filename) || isModuleManifestFile(filename)) {
2574
- const manifest2 = parseModuleManifest(source);
2575
- if (manifest2) {
2576
- parentMeta.module_manifest = manifest2;
2577
- }
2578
- break;
2579
- }
2580
- }
2581
- const manifest = parentMeta.module_manifest;
2582
- if (manifest?.dependencies) {
2583
- parentMeta.module_dependencies = manifest.dependencies;
2584
- }
2585
- if (manifest?.contributions) {
2586
- parentMeta.slot_contributions = manifest.contributions;
2587
- }
2588
- if (manifest?.configSchema) {
2589
- parentMeta.configSchema = manifest.configSchema;
2590
- }
2591
- if (manifest?.dependencies) {
2592
- const depConfigs = {};
2593
- for (const dep of manifest.dependencies) {
2594
- if (dep.config) {
2595
- depConfigs[dep.slug] = dep.config;
2596
- }
2597
- }
2598
- if (Object.keys(depConfigs).length > 0) {
2599
- parentMeta.module_configs = depConfigs;
2600
- }
2601
- }
2602
- const compilableFiles = Object.keys(files).filter(isCompilableFile);
2603
- const importLinks = resolveImportLinks(files, compilableFiles);
2604
- const pageExperiences = {};
2605
- for (const [filename, ir] of Object.entries(fileIRs)) {
2606
- if (isPageFile2(filename)) {
2607
- const views = ir.views;
2608
- if (views?.default) {
2609
- pageExperiences[filename] = views.default;
2610
- }
2611
- }
2612
- }
2613
- const parentViews = parentIR.views;
2614
- if (Object.keys(pageExperiences).length > 0) {
2615
- const pageEntries = Object.entries(pageExperiences);
2616
- const routeNodes = pageEntries.map(([pagePath, pageTree], i) => {
2617
- const routePath = "/" + pagePath.replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
2618
- return {
2619
- id: `route-${i}`,
2620
- component: "Route",
2621
- config: { path: routePath },
2622
- children: [pageTree]
2623
- };
2624
- });
2625
- const navLinks = pageEntries.map(([pagePath], i) => {
2626
- const routePath = "/" + pagePath.replace(/^app\//, "").replace(/\.(tsx?|jsx?)$/, "").replace(/\/index$/, "").replace(/\/page$/, "");
2627
- const segments = routePath.split("/").filter(Boolean);
2628
- const label = segments[segments.length - 1]?.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || "Home";
2629
- return {
2630
- id: `nav-link-${i}`,
2631
- component: "NavLink",
2632
- config: { to: routePath, label }
2633
- };
2634
- });
2635
- routeNodes.unshift({
2636
- id: "route-index",
2637
- component: "Route",
2638
- config: { path: "/", exact: true },
2639
- children: [pageEntries[0][1]]
2640
- });
2641
- const moduleRoutes = routeTable.filter((r) => r.moduleSlug);
2642
- for (let mi = 0; mi < moduleRoutes.length; mi++) {
2643
- const mr = moduleRoutes[mi];
2644
- routeNodes.push({
2645
- id: `mod-route-${mi}`,
2646
- component: "Route",
2647
- config: { path: mr.path },
2648
- children: [{
2649
- id: `mod-route-${mi}-placeholder`,
2650
- component: "ModuleView",
2651
- config: { moduleSlug: mr.moduleSlug, sourceFile: mr.sourceFile, path: mr.path }
2652
- }]
2653
- });
2654
- const segments = mr.path.split("/").filter(Boolean);
2655
- const label = segments[segments.length - 1]?.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) || mr.moduleSlug;
2656
- navLinks.push({
2657
- id: `mod-nav-${mi}`,
2658
- component: "NavLink",
2659
- config: { to: mr.path, label, moduleSlug: mr.moduleSlug }
2660
- });
2661
- }
2662
- const composedExperience = {
2663
- id: "blueprint-root",
2664
- component: "Stack",
2665
- className: "h-full flex flex-col",
2666
- children: [
2667
- // Nav bar (always visible, not inside Router so it always renders)
2668
- {
2669
- id: "nav-bar",
2670
- component: "Row",
2671
- config: { gap: 1 },
2672
- className: "px-4 py-2 border-b border-border bg-background/95 backdrop-blur-sm sticky top-0 z-10 flex-wrap",
2673
- children: navLinks
2674
- },
2675
- // Router with page routes
2676
- {
2677
- id: "page-router",
2678
- component: "Router",
2679
- className: "flex-1 min-h-0 overflow-y-auto",
2680
- children: routeNodes.map((r) => ({
2681
- ...r,
2682
- children: r.children?.map((child) => ({
2683
- ...child,
2684
- id: child.id || r.id + "-content",
2685
- component: child.component || "Stack",
2686
- config: { ...child.config, gap: 4, padding: 4 }
2687
- }))
2688
- }))
2689
- }
2690
- ]
2691
- };
2692
- parentIR.experience = composedExperience;
2693
- const slug = config.slug || parentIR.slug || "blueprint";
2694
- parentMeta.blueprint_manifest = {
2695
- routes: [
2696
- { path: `${slug}/*`, node: slug, label: config.name || parentIR.name || slug }
2697
- ],
2698
- config: {
2699
- full_bleed: true
2700
- }
2701
- };
2702
- } else if (parentViews?.default) {
2703
- parentIR.experience = parentViews.default;
2704
- }
2705
- for (const childDef of childDefinitions) {
2706
- for (const [filename, ir] of Object.entries(fileIRs)) {
2707
- if (ir.slug === childDef.slug || isWorkflowFile(filename) && ir.slug === childDef.slug) {
2708
- const views = ir.views;
2709
- if (views?.default) {
2710
- childDef.experience = views.default;
2711
- }
2712
- break;
2713
- }
2714
- }
2715
- }
2716
- const typeStubs = generateModuleTypeStubs(childDefinitions, modelResults);
2717
- return {
2718
- ir: parentIR,
2719
- childDefinitions,
2720
- fileIRs,
2721
- routeTable,
2722
- serverActions: serverActionEntries,
2723
- errors,
2724
- warnings,
2725
- pageExperiences,
2726
- componentDefinitions,
2727
- importLinks,
2728
- modelResults,
2729
- actionResult,
2730
- routeResult,
2731
- typeStubs
2732
- };
2733
- }
2734
- function compileProject(files, options = {}) {
2735
- const allErrors = [];
2736
- const allWarnings = [];
2737
- const fileIRs = {};
2738
- let config = {};
2739
- for (const [filename, source] of Object.entries(files)) {
2740
- if (isConfigFile(filename)) {
2741
- config = parseConfig(source);
2742
- break;
2743
- }
2744
- }
2745
- const mode = config.mode || options.mode || "infer";
2746
- const compilableFiles = Object.keys(files).filter(isCompilableFile);
2747
- const orderedFiles = resolveCompilationOrder(files, compilableFiles);
2748
- for (const filename of orderedFiles) {
2749
- const source = files[filename];
2750
- const { ir, errors } = compileFile(filename, source, mode);
2751
- for (const e of errors) {
2752
- if (e.severity === "error") allErrors.push(e);
2753
- else allWarnings.push(e);
2754
- }
2755
- if (ir) {
2756
- fileIRs[filename] = ir;
2757
- }
2758
- }
2759
- return buildComposedResult(files, fileIRs, config, allErrors, allWarnings, {
2760
- usePhase2Modules: options.usePhase2Modules,
2761
- mode,
2762
- resolvedModules: options.resolvedModules
2763
- });
2764
- }
2765
- var IncrementalProjectCompiler = class {
2766
- constructor() {
2767
- this.lastConfig = {};
2768
- this.cache = new IncrementalCache();
2769
- }
2770
- /**
2771
- * Compile a project incrementally — only recompiles changed files.
2772
- */
2773
- compile(files, options = {}) {
2774
- const startTime = Date.now();
2775
- const { dependents } = buildDependencyGraph(files);
2776
- const dirtySet = this.cache.detectDirtyFiles(files, dependents);
2777
- const allDirty = /* @__PURE__ */ new Set([
2778
- ...dirtySet.contentChanged,
2779
- ...dirtySet.dependencyDirty,
2780
- ...dirtySet.added
2781
- ]);
2782
- for (const removed of dirtySet.removed) {
2783
- this.cache.delete(removed);
2784
- }
2785
- if (allDirty.size === 0 && this.cache.getCachedFiles().length > 0) {
2786
- this.cache.updateStats(0, this.cache.getCachedFiles().length, Date.now() - startTime);
2787
- return this.rebuildFromCache(files, options);
2788
- }
2789
- for (const [filename, source] of Object.entries(files)) {
2790
- if (isConfigFile(filename)) {
2791
- if (allDirty.has(filename)) {
2792
- this.lastConfig = parseConfig(source);
2793
- }
2794
- break;
2795
- }
2796
- }
2797
- const mode = this.lastConfig.mode || options.mode || "infer";
2798
- const compilableFiles = Object.keys(files).filter(isCompilableFile);
2799
- let recompiled = 0;
2800
- let cacheHits = 0;
2801
- for (const filename of compilableFiles) {
2802
- if (allDirty.has(filename) || !this.cache.has(filename)) {
2803
- const { ir, errors } = compileFile(filename, files[filename], mode);
2804
- if (ir) {
2805
- this.cache.set(filename, files[filename], { ir, errors });
2806
- } else {
2807
- this.cache.delete(filename);
2808
- }
2809
- recompiled++;
2810
- } else {
2811
- cacheHits++;
2812
- }
2813
- }
2814
- for (const cached of this.cache.getCachedFiles()) {
2815
- if (!compilableFiles.includes(cached)) {
2816
- this.cache.delete(cached);
2817
- }
2818
- }
2819
- const fileIRs = {};
2820
- const allErrors = [];
2821
- const allWarnings = [];
2822
- for (const cached of this.cache.getCachedFiles()) {
2823
- const entry = this.cache.get(cached);
2824
- if (entry) {
2825
- fileIRs[cached] = entry.ir;
2826
- for (const e of entry.errors) {
2827
- if (e.severity === "error") allErrors.push(e);
2828
- else allWarnings.push(e);
2829
- }
2830
- }
2831
- }
2832
- this.cache.updateStats(recompiled, cacheHits, Date.now() - startTime);
2833
- return buildComposedResult(files, fileIRs, this.lastConfig, allErrors, allWarnings, {
2834
- usePhase2Modules: options.usePhase2Modules,
2835
- mode,
2836
- resolvedModules: options.resolvedModules
2837
- });
2838
- }
2839
- /**
2840
- * Rebuild result from cache without recompiling anything.
2841
- */
2842
- rebuildFromCache(files, options) {
2843
- let config = this.lastConfig;
2844
- if (!config.slug) {
2845
- for (const [filename, source] of Object.entries(files)) {
2846
- if (isConfigFile(filename)) {
2847
- config = parseConfig(source);
2848
- this.lastConfig = config;
2849
- break;
2850
- }
2851
- }
2852
- }
2853
- const fileIRs = {};
2854
- const allErrors = [];
2855
- const allWarnings = [];
2856
- for (const cached of this.cache.getCachedFiles()) {
2857
- const entry = this.cache.get(cached);
2858
- if (entry) {
2859
- fileIRs[cached] = entry.ir;
2860
- for (const e of entry.errors) {
2861
- if (e.severity === "error") allErrors.push(e);
2862
- else allWarnings.push(e);
2863
- }
2864
- }
2865
- }
2866
- const mode = this.lastConfig.mode || options.mode || "infer";
2867
- return buildComposedResult(files, fileIRs, config, allErrors, allWarnings, {
2868
- usePhase2Modules: options.usePhase2Modules,
2869
- mode,
2870
- resolvedModules: options.resolvedModules
2871
- });
2872
- }
2873
- /** Invalidate a specific file's cache. */
2874
- invalidate(filename) {
2875
- this.cache.delete(filename);
2876
- }
2877
- /** Invalidate all caches. */
2878
- invalidateAll() {
2879
- this.cache.clear();
2880
- this.lastConfig = {};
2881
- }
2882
- /** Check if a file would need recompilation given its current source. */
2883
- isDirty(filename, source) {
2884
- hashContent(source);
2885
- const cached = this.cache.get(filename);
2886
- return !cached;
2887
- }
2888
- /** Get list of files currently in cache. */
2889
- getCachedFiles() {
2890
- return this.cache.getCachedFiles();
2891
- }
2892
- /** Get compilation statistics. */
2893
- getStats() {
2894
- const stats = this.cache.getStats();
2895
- return {
2896
- cachedFiles: stats.totalCached,
2897
- totalHashes: stats.totalCached,
2898
- ...stats
2899
- };
2900
- }
2901
- };
2902
-
2903
- export {
2904
- compileModel,
2905
- compileModels,
2906
- extractRoutes,
2907
- compileActions,
2908
- resolveActionReferences,
2909
- hashContent,
2910
- extractImports,
2911
- resolveImport,
2912
- buildDependencyGraph,
2913
- computeTransitiveDirtySet,
2914
- topologicalSort,
2915
- IncrementalCache,
2916
- compileProject,
2917
- IncrementalProjectCompiler
2918
- };