@mmapp/react-compiler 0.1.0-alpha.18 → 0.1.0-alpha.19

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