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

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 (199) 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-4FP5DXY4.mjs +3456 -0
  17. package/dist/chunk-4HX4PI4R.mjs +734 -0
  18. package/dist/chunk-4VU56NTZ.mjs +544 -0
  19. package/dist/chunk-4XHK6FWL.mjs +2058 -0
  20. package/dist/chunk-52C2JKH2.mjs +186 -0
  21. package/dist/chunk-52XHYD2V.mjs +214 -0
  22. package/dist/chunk-5GUFFFGL.mjs +148 -0
  23. package/dist/chunk-5N2FDDS6.mjs +214 -0
  24. package/dist/chunk-5ZSJXWZT.mjs +1646 -0
  25. package/dist/chunk-6CQOAAMV.mjs +1803 -0
  26. package/dist/chunk-6SEVAAVT.mjs +3516 -0
  27. package/dist/chunk-6YLR5ZDA.mjs +2829 -0
  28. package/dist/chunk-77UJB356.mjs +244 -0
  29. package/dist/chunk-7QOAJPQF.mjs +774 -0
  30. package/dist/chunk-A5RCMIBP.mjs +1795 -0
  31. package/dist/chunk-A63R3GKH.mjs +1803 -0
  32. package/dist/chunk-ABNTZXF5.mjs +951 -0
  33. package/dist/chunk-ADHWW56I.mjs +214 -0
  34. package/dist/chunk-AOGY2GK6.mjs +3292 -0
  35. package/dist/chunk-AXXUXRNA.mjs +1434 -0
  36. package/dist/chunk-BZLU5YK5.mjs +1025 -0
  37. package/dist/chunk-C7XCDDBH.mjs +1802 -0
  38. package/dist/chunk-CHLVKMQW.mjs +175 -0
  39. package/dist/chunk-CKGOZAB7.mjs +939 -0
  40. package/dist/chunk-CSXYDIVC.mjs +214 -0
  41. package/dist/chunk-CXTV4VGG.mjs +148 -0
  42. package/dist/chunk-D34RAZUX.mjs +2223 -0
  43. package/dist/chunk-DDIC7WM6.mjs +3127 -0
  44. package/dist/chunk-DPUQOBU6.mjs +4810 -0
  45. package/dist/chunk-E6MDVTGT.mjs +148 -0
  46. package/dist/chunk-EGKMUEM6.mjs +544 -0
  47. package/dist/chunk-EO6SYNCG.mjs +175 -0
  48. package/dist/chunk-EQGA6A6D.mjs +121 -0
  49. package/dist/chunk-EY2CSXYA.mjs +822 -0
  50. package/dist/chunk-EYLOSECJ.mjs +544 -0
  51. package/dist/chunk-FF5BQVII.mjs +148 -0
  52. package/dist/chunk-FIQ65CDR.mjs +925 -0
  53. package/dist/chunk-FOZXJFAR.mjs +186 -0
  54. package/dist/chunk-G2IAZ5Q6.mjs +148 -0
  55. package/dist/chunk-G7SMOWOL.mjs +828 -0
  56. package/dist/chunk-GK7NU6DO.mjs +214 -0
  57. package/dist/chunk-HDSCPEHY.mjs +4061 -0
  58. package/dist/chunk-HJELFNEA.mjs +186 -0
  59. package/dist/chunk-HOIUP6IF.mjs +690 -0
  60. package/dist/chunk-HRJGDPNE.mjs +148 -0
  61. package/dist/chunk-I3AU7GRD.mjs +120 -0
  62. package/dist/chunk-I3VQQJZ6.mjs +2843 -0
  63. package/dist/chunk-I6SSPILI.mjs +550 -0
  64. package/dist/chunk-IPTX5MJU.mjs +3223 -0
  65. package/dist/chunk-ITGUSH2Z.mjs +2783 -0
  66. package/dist/chunk-IXHBCAMF.mjs +3306 -0
  67. package/dist/chunk-J7JUAHS4.mjs +186 -0
  68. package/dist/chunk-J7TWJ3TM.mjs +2784 -0
  69. package/dist/chunk-JDPLDGVF.mjs +4810 -0
  70. package/dist/chunk-JK72MQ4N.mjs +877 -0
  71. package/dist/chunk-K2HHCAS2.mjs +148 -0
  72. package/dist/chunk-K5HX2SVL.mjs +1902 -0
  73. package/dist/chunk-KAUEQ2F3.mjs +148 -0
  74. package/dist/chunk-KFVVOS5N.mjs +925 -0
  75. package/dist/chunk-KIH4AN3Y.mjs +154 -0
  76. package/dist/chunk-KPDMN5IX.mjs +175 -0
  77. package/dist/chunk-LZL2IRCH.mjs +186 -0
  78. package/dist/chunk-MIZV3TAN.mjs +3293 -0
  79. package/dist/chunk-MRH4J7IX.mjs +2846 -0
  80. package/dist/chunk-NKBL5GUC.mjs +186 -0
  81. package/dist/chunk-NQCNSCF6.mjs +148 -0
  82. package/dist/chunk-NRP5J3BR.mjs +4811 -0
  83. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  84. package/dist/chunk-NUPJYPFU.mjs +801 -0
  85. package/dist/chunk-NVQUTSQX.mjs +3128 -0
  86. package/dist/chunk-OGMG64EY.mjs +148 -0
  87. package/dist/chunk-OL5B2HTH.mjs +175 -0
  88. package/dist/chunk-OPJKP747.mjs +7506 -0
  89. package/dist/chunk-OQLGGBNE.mjs +2918 -0
  90. package/dist/chunk-OW4AQUDL.mjs +544 -0
  91. package/dist/chunk-OWI6XWCD.mjs +3375 -0
  92. package/dist/chunk-OZT2EAF2.mjs +2776 -0
  93. package/dist/chunk-PBRBRKIQ.mjs +175 -0
  94. package/dist/chunk-PRUMNNDI.mjs +3192 -0
  95. package/dist/chunk-QPNHDTSM.mjs +4839 -0
  96. package/dist/chunk-RNEIAJDR.mjs +897 -0
  97. package/dist/chunk-RY7POBNT.mjs +3127 -0
  98. package/dist/chunk-S6FJ4DXL.mjs +1813 -0
  99. package/dist/chunk-SU4E6E7B.mjs +3153 -0
  100. package/dist/chunk-SYUUKW5A.mjs +3379 -0
  101. package/dist/chunk-THB5SX2S.mjs +113 -0
  102. package/dist/chunk-THFYE5ZX.mjs +244 -0
  103. package/dist/chunk-TK3QMXIP.mjs +2921 -0
  104. package/dist/chunk-TRR2NPAV.mjs +248 -0
  105. package/dist/chunk-TTTTOT7P.mjs +1803 -0
  106. package/dist/chunk-TXONBY6A.mjs +7509 -0
  107. package/dist/chunk-U2PX3JSY.mjs +1933 -0
  108. package/dist/chunk-U6F7CTHK.mjs +550 -0
  109. package/dist/chunk-UASOWKDI.mjs +186 -0
  110. package/dist/chunk-UDDTWG5J.mjs +734 -0
  111. package/dist/chunk-UIWLAQCL.mjs +175 -0
  112. package/dist/chunk-UL2XZEMA.mjs +3128 -0
  113. package/dist/chunk-US3AVDAI.mjs +3456 -0
  114. package/dist/chunk-V5DIDOTT.mjs +148 -0
  115. package/dist/chunk-VLTKQDJ3.mjs +244 -0
  116. package/dist/chunk-VVC42PTS.mjs +175 -0
  117. package/dist/chunk-VX3T3NIR.mjs +897 -0
  118. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  119. package/dist/chunk-XMWUHQVV.mjs +939 -0
  120. package/dist/chunk-XUQ5R6F3.mjs +213 -0
  121. package/dist/chunk-Y6FXYEAI.mjs +10 -0
  122. package/dist/chunk-YCWC2SCO.mjs +148 -0
  123. package/dist/chunk-YFS6JMYO.mjs +3342 -0
  124. package/dist/chunk-ZE67HOSN.mjs +148 -0
  125. package/dist/chunk-ZSK5TPIV.mjs +148 -0
  126. package/dist/cli/index.js +329 -653
  127. package/dist/cli/index.mjs +9 -9
  128. package/dist/config-PL24KEWL.mjs +219 -0
  129. package/dist/deploy-VAHWALWB.mjs +9 -0
  130. package/dist/dev-server-Bs_sz2DG.d.mts +111 -0
  131. package/dist/dev-server-Bs_sz2DG.d.ts +111 -0
  132. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  133. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  134. package/dist/dev-server.js +205 -522
  135. package/dist/dev-server.mjs +4 -4
  136. package/dist/engine-binary-KQB23JDR.mjs +97 -0
  137. package/dist/envelope-DD7v0v6E.d.mts +265 -0
  138. package/dist/envelope-DD7v0v6E.d.ts +265 -0
  139. package/dist/envelope.js +9 -6
  140. package/dist/envelope.mjs +2 -2
  141. package/dist/index-B5gSgvnd.d.mts +44 -0
  142. package/dist/index-B5gSgvnd.d.ts +44 -0
  143. package/dist/index-Bs0MnR54.d.mts +103 -0
  144. package/dist/index-Bs0MnR54.d.ts +103 -0
  145. package/dist/index-DR0nNc_f.d.mts +101 -0
  146. package/dist/index-DR0nNc_f.d.ts +101 -0
  147. package/dist/index-revho_gS.d.mts +104 -0
  148. package/dist/index-revho_gS.d.ts +104 -0
  149. package/dist/index-vQjwYekL.d.mts +104 -0
  150. package/dist/index-vQjwYekL.d.ts +104 -0
  151. package/dist/index.d.mts +2 -2
  152. package/dist/index.d.ts +2 -2
  153. package/dist/index.js +210 -525
  154. package/dist/index.mjs +10 -9
  155. package/dist/init-2XLTUF7O.mjs +407 -0
  156. package/dist/init-7FJENUDK.mjs +407 -0
  157. package/dist/init-AVZJHZYY.mjs +538 -0
  158. package/dist/init-DQDX3QK6.mjs +369 -0
  159. package/dist/init-K3GVM4JS.mjs +538 -0
  160. package/dist/init-NXS5BJN3.mjs +454 -0
  161. package/dist/init-UC3FWPIW.mjs +367 -0
  162. package/dist/init-UNV5XIDE.mjs +367 -0
  163. package/dist/project-compiler-2HOPO7GC.mjs +10 -0
  164. package/dist/project-compiler-D245L5QR.mjs +10 -0
  165. package/dist/project-compiler-FUQRMB4X.mjs +10 -0
  166. package/dist/project-compiler-LA5OBI5P.mjs +10 -0
  167. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  168. package/dist/project-compiler-PZNFE6AH.mjs +10 -0
  169. package/dist/project-compiler-QBVF6MFI.mjs +10 -0
  170. package/dist/project-compiler-VFR6CSDX.mjs +10 -0
  171. package/dist/project-compiler-WMJZA4UH.mjs +10 -0
  172. package/dist/project-compiler-XXS27TZT.mjs +10 -0
  173. package/dist/project-compiler-YYGDSHY5.mjs +10 -0
  174. package/dist/project-decompiler-5GY2KSG4.mjs +7 -0
  175. package/dist/project-decompiler-7I2OMUVY.mjs +7 -0
  176. package/dist/project-decompiler-QCZYY4TW.mjs +7 -0
  177. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  178. package/dist/pull-2Q53HF3H.mjs +107 -0
  179. package/dist/pull-5AFHD3QG.mjs +109 -0
  180. package/dist/pull-5WJ4LW4U.mjs +109 -0
  181. package/dist/pull-65GUSX6F.mjs +109 -0
  182. package/dist/pull-6LVI4LMM.mjs +109 -0
  183. package/dist/pull-A2QUHW4K.mjs +109 -0
  184. package/dist/pull-B6T5BUKV.mjs +109 -0
  185. package/dist/pull-CKHWZT33.mjs +109 -0
  186. package/dist/pull-GM74ERRT.mjs +109 -0
  187. package/dist/pull-JBEQWVPE.mjs +109 -0
  188. package/dist/pull-P44LDRWB.mjs +109 -0
  189. package/dist/pull-W2US3T3E.mjs +109 -0
  190. package/dist/testing/index.js +9 -6
  191. package/dist/testing/index.mjs +1 -1
  192. package/dist/verify-3PPS4XAM.mjs +1833 -0
  193. package/dist/verify-GKEH5FZQ.mjs +1833 -0
  194. package/dist/verify-HDKUNHZ4.mjs +1833 -0
  195. package/dist/verify-SEIXUGN4.mjs +1833 -0
  196. package/dist/vite/index.js +10 -7
  197. package/dist/vite/index.mjs +2 -2
  198. package/mm-dev.db +0 -0
  199. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2115,7 +2115,8 @@ function convertOnEvent(sub) {
2115
2115
  };
2116
2116
  }
2117
2117
  function emitIR(extracted) {
2118
- const { slug, name, version, description, category, fields, states, transitions } = extracted;
2118
+ const { slug, name, version, description, category: rawCategory, fields, states, transitions } = extracted;
2119
+ const category = Array.isArray(rawCategory) ? rawCategory : [rawCategory];
2119
2120
  const stateArray = Array.from(states.values());
2120
2121
  if (stateArray.length === 0) {
2121
2122
  stateArray.push({
@@ -2503,13 +2504,15 @@ function emitCanonical(extracted, sourceFilename) {
2503
2504
  }
2504
2505
  ]
2505
2506
  });
2506
- const category = extracted.category;
2507
+ const rawCat = extracted.category;
2507
2508
  let categoryArray;
2508
- if (category.includes("/")) {
2509
- const [primary, ...tags] = category.split("/");
2509
+ if (Array.isArray(rawCat)) {
2510
+ categoryArray = rawCat;
2511
+ } else if (rawCat.includes("/")) {
2512
+ const [primary, ...tags] = rawCat.split("/");
2510
2513
  categoryArray = (0, import_player_core.normalizeCategory)(primary, ...tags);
2511
2514
  } else {
2512
- categoryArray = [category];
2515
+ categoryArray = [rawCat];
2513
2516
  }
2514
2517
  return {
2515
2518
  slug: ir.slug,
@@ -2662,7 +2665,7 @@ function compilerStateToWorkflow(state, metadata) {
2662
2665
  name: metadata.name || "Workflow",
2663
2666
  version: metadata.version || "0.1.0",
2664
2667
  description: metadata.description,
2665
- category: metadata.category || "workflow",
2668
+ category: metadata.categoryArray ? metadata.categoryArray : [metadata.category || "workflow"],
2666
2669
  fields: state.fields || [],
2667
2670
  states: state.states || /* @__PURE__ */ new Map(),
2668
2671
  transitions: state.transitions || [],
@@ -7794,7 +7797,7 @@ function extractRouterWorkflow(pages, options = {}) {
7794
7797
  name: "Router",
7795
7798
  version: "1.0.0",
7796
7799
  description: "Auto-derived router workflow from file-based routing",
7797
- category: "router",
7800
+ category: ["router"],
7798
7801
  fields,
7799
7802
  states,
7800
7803
  transitions,
@@ -7893,7 +7896,8 @@ function compileModel(filename, source, options = {}) {
7893
7896
  parserOpts: { plugins: parserPlugins, attachComment: true }
7894
7897
  });
7895
7898
  const ir = babelResult?.metadata?.mindmatrixIR ?? createEmptyModelIR(interfaceName);
7896
- ir.category = options.categoryOverride || annotation.category || "data";
7899
+ const catOverride = options.categoryOverride || annotation.category || "data";
7900
+ ir.category = Array.isArray(catOverride) ? catOverride : [catOverride];
7897
7901
  if (options.slugOverride) {
7898
7902
  ir.slug = options.slugOverride;
7899
7903
  }
@@ -7957,7 +7961,7 @@ function createEmptyModelIR(interfaceName) {
7957
7961
  name: interfaceName,
7958
7962
  version: "1.0.0",
7959
7963
  description: "Data model: " + interfaceName,
7960
- category: "data",
7964
+ category: ["data"],
7961
7965
  fields: [],
7962
7966
  states: [{
7963
7967
  name: "draft",
@@ -8125,7 +8129,7 @@ function buildRouterIR(routes, layouts, allParams, slugPrefix) {
8125
8129
  name: "Router",
8126
8130
  version: "1.0.0",
8127
8131
  description: "Auto-generated router from app/ directory structure",
8128
- category: "router",
8132
+ category: ["router"],
8129
8133
  fields,
8130
8134
  states,
8131
8135
  transitions,
@@ -8394,7 +8398,7 @@ function extractAction(source, filename) {
8394
8398
  slug,
8395
8399
  name: humanName,
8396
8400
  version: "0.1.0",
8397
- category: "action",
8401
+ category: ["action"],
8398
8402
  fields,
8399
8403
  states: states2,
8400
8404
  transitions: transitions2,
@@ -8446,7 +8450,7 @@ function extractAction(source, filename) {
8446
8450
  slug,
8447
8451
  name: humanName,
8448
8452
  version: "0.1.0",
8449
- category: "action",
8453
+ category: ["action"],
8450
8454
  fields,
8451
8455
  states,
8452
8456
  transitions,
@@ -9883,7 +9887,7 @@ function applyConfig(ir, config) {
9883
9887
  if (config.name) ir.name = config.name;
9884
9888
  if (config.version) ir.version = config.version;
9885
9889
  if (config.description !== void 0) ir.description = config.description;
9886
- if (config.category) ir.category = config.category;
9890
+ if (config.category) ir.category = Array.isArray(config.category) ? config.category : [config.category];
9887
9891
  if (!ir.metadata) ir.metadata = {};
9888
9892
  ir.metadata.stable_id = "def-" + ir.slug;
9889
9893
  ir.metadata.provenance = {
@@ -9899,7 +9903,7 @@ function createEmptyIR(config) {
9899
9903
  name: config.name || "Project",
9900
9904
  version: config.version || "0.1.0",
9901
9905
  description: config.description,
9902
- category: config.category || "workflow",
9906
+ category: Array.isArray(config.category) ? config.category : [config.category || "workflow"],
9903
9907
  fields: [],
9904
9908
  states: [{
9905
9909
  name: "draft",
@@ -10057,8 +10061,10 @@ function buildComposedResult(files, fileIRs, config, errors, warnings, options =
10057
10061
  if (isWorkflowFile(filename)) {
10058
10062
  workflowIRs.push(ir);
10059
10063
  } else if (isModelFile2(filename)) {
10060
- if (!ir.category || ir.category === "workflow") {
10061
- ir.category = "data";
10064
+ const cat = ir.category;
10065
+ const isDefault = !cat || cat === "workflow" || Array.isArray(cat) && cat.length === 1 && cat[0] === "workflow";
10066
+ if (isDefault) {
10067
+ ir.category = ["data"];
10062
10068
  }
10063
10069
  modelIRs.push(ir);
10064
10070
  } else if (isServerActionFile2(filename)) {
@@ -10870,14 +10876,12 @@ async function deploy(options) {
10870
10876
  }
10871
10877
  async function fetchExistingDefinition(apiUrl, token, slug) {
10872
10878
  try {
10873
- const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
10879
+ const res = await fetch(`${apiUrl}/workflow/definitions/${encodeURIComponent(slug)}`, {
10874
10880
  headers: { Authorization: `Bearer ${token}` }
10875
10881
  });
10876
10882
  if (!res.ok) return null;
10877
- const data = await res.json();
10878
- const definitions = Array.isArray(data) ? data : data.items ?? data.data;
10879
- if (!definitions || definitions.length === 0) return null;
10880
- const def = definitions[0];
10883
+ const def = await res.json();
10884
+ if (!def || !def.id) return null;
10881
10885
  return {
10882
10886
  id: def.id,
10883
10887
  slug: def.slug,
@@ -10895,7 +10899,7 @@ async function createDefinition(apiUrl, token, ir) {
10895
10899
  "Content-Type": "application/json",
10896
10900
  Authorization: `Bearer ${token}`
10897
10901
  },
10898
- body: JSON.stringify(ir)
10902
+ body: JSON.stringify({ ...ir, visibility: "PUBLIC" })
10899
10903
  });
10900
10904
  if (!res.ok) {
10901
10905
  const errorText = await res.text();
@@ -12665,7 +12669,7 @@ function generatePageFile(page, _slug) {
12665
12669
  slug: page.route || "page",
12666
12670
  name: page.componentName,
12667
12671
  version: "1.0.0",
12668
- category: "page",
12672
+ category: "view",
12669
12673
  states: [],
12670
12674
  transitions: [],
12671
12675
  fields: [],
@@ -12856,7 +12860,8 @@ function decompileProject(definition, options) {
12856
12860
  ...child,
12857
12861
  experience: child.views?.default
12858
12862
  };
12859
- if (child.category === "data" && child.fields.length > 0 && !options?.skipModels) {
12863
+ const childCatArr = Array.isArray(child.category) ? child.category : [child.category];
12864
+ if (childCatArr.includes("data") && child.fields.length > 0 && !options?.skipModels) {
12860
12865
  let childModelContent = generateModelFile(childSlug, child.fields);
12861
12866
  if (child.states.length > 0) {
12862
12867
  childModelContent += generateStateEnum(childSlug, child.states);
@@ -13117,7 +13122,8 @@ function compareIR(original, roundTripped) {
13117
13122
  const diffs = [];
13118
13123
  comparePrimitive(diffs, "slug", original.slug, roundTripped.slug);
13119
13124
  comparePrimitive(diffs, "version", original.version, roundTripped.version);
13120
- comparePrimitive(diffs, "category", original.category, roundTripped.category);
13125
+ const normCat = (c) => JSON.stringify(Array.isArray(c) ? c : c ? [c] : []);
13126
+ comparePrimitive(diffs, "category", normCat(original.category), normCat(roundTripped.category));
13121
13127
  compareFields(diffs, original.fields, roundTripped.fields);
13122
13128
  compareStates(diffs, original.states, roundTripped.states);
13123
13129
  compareTransitions(diffs, original.transitions, roundTripped.transitions);
@@ -13334,7 +13340,7 @@ function mindmatrixReact(options) {
13334
13340
  slug: ir.slug,
13335
13341
  name: ir.name,
13336
13342
  version: ir.version,
13337
- category: ir.category || "workflow",
13343
+ category: ir.category || ["workflow"],
13338
13344
  fields: ir.fields || [],
13339
13345
  states: ir.states || [],
13340
13346
  transitions: ir.transitions || [],
@@ -13919,425 +13925,6 @@ async function startWatchMode(options) {
13919
13925
  });
13920
13926
  }
13921
13927
 
13922
- // src/cli/local-server.ts
13923
- var http = __toESM(require("http"));
13924
- var import_node_crypto = require("crypto");
13925
- var MemoryStore = class {
13926
- constructor() {
13927
- this.definitions = /* @__PURE__ */ new Map();
13928
- this.instances = /* @__PURE__ */ new Map();
13929
- this.slugIndex = /* @__PURE__ */ new Map();
13930
- }
13931
- // slug → id
13932
- // ── Definitions ──────────────────────────────────────────────────────
13933
- createDefinition(input) {
13934
- if (this.slugIndex.has(input.slug)) {
13935
- const existing = this.definitions.get(this.slugIndex.get(input.slug));
13936
- if (existing) return existing;
13937
- }
13938
- const now = (/* @__PURE__ */ new Date()).toISOString();
13939
- const def = {
13940
- id: input.id ?? (0, import_node_crypto.randomUUID)(),
13941
- slug: input.slug,
13942
- name: input.name,
13943
- version: input.version ?? "1.0.0",
13944
- description: input.description ?? null,
13945
- category: input.category ?? "workflow",
13946
- fields: input.fields ?? [],
13947
- states: input.states ?? [],
13948
- transitions: input.transitions ?? [],
13949
- roles: input.roles ?? [],
13950
- experience: input.experience ?? null,
13951
- metadata: input.metadata ?? {},
13952
- child_definitions: input.child_definitions ?? [],
13953
- is_immutable: input.is_immutable ?? false,
13954
- tags: input.tags ?? [],
13955
- inline_tags: input.inline_tags ?? [],
13956
- created_at: now,
13957
- updated_at: now
13958
- };
13959
- this.definitions.set(def.id, def);
13960
- this.slugIndex.set(def.slug, def.id);
13961
- return def;
13962
- }
13963
- getDefinition(idOrSlug) {
13964
- const byId = this.definitions.get(idOrSlug);
13965
- if (byId) return byId;
13966
- const id = this.slugIndex.get(idOrSlug);
13967
- if (id) return this.definitions.get(id);
13968
- return void 0;
13969
- }
13970
- listDefinitions(opts) {
13971
- let items = Array.from(this.definitions.values());
13972
- if (opts?.category) {
13973
- items = items.filter((d) => d.category === opts.category);
13974
- }
13975
- const total = items.length;
13976
- const offset = opts?.offset ?? 0;
13977
- const limit = opts?.limit ?? 50;
13978
- items = items.slice(offset, offset + limit);
13979
- return { items, total };
13980
- }
13981
- patchDefinition(id, patch) {
13982
- const def = this.definitions.get(id);
13983
- if (!def) return void 0;
13984
- Object.assign(def, patch, { updated_at: (/* @__PURE__ */ new Date()).toISOString() });
13985
- return def;
13986
- }
13987
- deleteDefinition(id) {
13988
- const def = this.definitions.get(id);
13989
- if (!def) return false;
13990
- this.slugIndex.delete(def.slug);
13991
- this.definitions.delete(id);
13992
- return true;
13993
- }
13994
- // ── Instances ────────────────────────────────────────────────────────
13995
- createInstance(input) {
13996
- const def = this.getDefinition(input.definition_id) ?? this.getDefinition(input.definition_slug);
13997
- if (!def) return null;
13998
- const initialState = def.states.find((s) => s.type === "START" || s.type === "initial");
13999
- const stateName = initialState?.name ?? "initial";
14000
- const stateData = {};
14001
- for (const field of def.fields) {
14002
- if (field.default_value !== void 0) {
14003
- stateData[field.name] = field.default_value;
14004
- }
14005
- }
14006
- Object.assign(stateData, input.state_data ?? {});
14007
- const now = (/* @__PURE__ */ new Date()).toISOString();
14008
- const inst = {
14009
- id: (0, import_node_crypto.randomUUID)(),
14010
- definition_id: def.id,
14011
- definition_slug: def.slug,
14012
- current_state: stateName,
14013
- state_data: stateData,
14014
- execution_lock_version: 0,
14015
- event_log: [{
14016
- event_type: "transition",
14017
- message: `Instance created in state '${stateName}'`,
14018
- timestamp: now
14019
- }],
14020
- created_at: now,
14021
- updated_at: now
14022
- };
14023
- this.instances.set(inst.id, inst);
14024
- return inst;
14025
- }
14026
- getInstance(id) {
14027
- return this.instances.get(id);
14028
- }
14029
- listInstances(opts) {
14030
- let items = Array.from(this.instances.values());
14031
- if (opts?.definition_id) {
14032
- items = items.filter((i) => i.definition_id === opts.definition_id);
14033
- }
14034
- const total = items.length;
14035
- const offset = opts?.offset ?? 0;
14036
- const limit = opts?.limit ?? 50;
14037
- items = items.slice(offset, offset + limit);
14038
- return { items, total };
14039
- }
14040
- // ── Execute Action (Transition) ──────────────────────────────────────
14041
- executeAction(input) {
14042
- const def = this.getDefinition(input.definition_id);
14043
- if (!def) return { success: false, error: "Definition not found" };
14044
- let inst;
14045
- if (input.instance_id) {
14046
- const existing = this.instances.get(input.instance_id);
14047
- if (!existing) return { success: false, error: "Instance not found" };
14048
- inst = existing;
14049
- } else {
14050
- const created = this.createInstance({
14051
- definition_id: def.id,
14052
- definition_slug: def.slug,
14053
- state_data: input.payload
14054
- });
14055
- if (!created) return { success: false, error: "Failed to create instance" };
14056
- inst = created;
14057
- }
14058
- if (input.payload && input.instance_id) {
14059
- Object.assign(inst.state_data, input.payload);
14060
- }
14061
- const transition = def.transitions.find((t27) => t27.name === input.action_name && t27.from.includes(inst.current_state));
14062
- if (!transition) {
14063
- return {
14064
- success: false,
14065
- instance_id: inst.id,
14066
- from_state: inst.current_state,
14067
- to_state: null,
14068
- state_data: inst.state_data,
14069
- error: `No transition '${input.action_name}' from state '${inst.current_state}'`
14070
- };
14071
- }
14072
- const fromState = inst.current_state;
14073
- const now = (/* @__PURE__ */ new Date()).toISOString();
14074
- const events = [];
14075
- let lastEvalResult = null;
14076
- events.push({ event_type: "transition", message: `Transition '${transition.name}' started: ${fromState} \u2192 ${transition.to}`, timestamp: now });
14077
- for (const action of transition.actions ?? []) {
14078
- try {
14079
- if (action.type === "set_field") {
14080
- const field = action.config?.field;
14081
- if (action.config?.expression) {
14082
- const expr = action.config.expression;
14083
- const result = this.evaluateSimpleExpression(expr, inst.state_data);
14084
- inst.state_data[field] = result;
14085
- } else if (action.config?.value !== void 0) {
14086
- inst.state_data[field] = action.config.value;
14087
- }
14088
- events.push({ event_type: "action_executed", message: `transition action 'set_field' succeeded`, timestamp: now });
14089
- } else if (action.type === "eval") {
14090
- const expr = action.config?.expression;
14091
- lastEvalResult = this.evaluateSimpleExpression(expr, inst.state_data);
14092
- events.push({ event_type: "action_executed", message: `transition action 'eval' succeeded`, timestamp: now });
14093
- } else {
14094
- events.push({ event_type: "action_executed", message: `transition action '${action.type}' succeeded (no-op in local mode)`, timestamp: now });
14095
- }
14096
- } catch (err) {
14097
- const msg = err instanceof Error ? err.message : String(err);
14098
- events.push({ event_type: "action_failed", message: `transition action '${action.type}' failed: ${msg}`, timestamp: now });
14099
- return {
14100
- success: false,
14101
- instance_id: inst.id,
14102
- from_state: fromState,
14103
- to_state: null,
14104
- state_data: inst.state_data,
14105
- event_log: events,
14106
- error: `transition action failed: ${msg}`
14107
- };
14108
- }
14109
- }
14110
- inst.current_state = transition.to;
14111
- inst.execution_lock_version++;
14112
- inst.updated_at = now;
14113
- events.push({ event_type: "transition", message: `State changed: ${fromState} \u2192 ${transition.to}`, timestamp: now });
14114
- inst.event_log.push(...events);
14115
- return {
14116
- success: true,
14117
- instance_id: inst.id,
14118
- from_state: fromState,
14119
- to_state: transition.to,
14120
- state_data: inst.state_data,
14121
- result: lastEvalResult,
14122
- event_log: events
14123
- };
14124
- }
14125
- /**
14126
- * Minimal expression evaluator for local dev mode.
14127
- * Handles: field references, arithmetic, string literals, simple comparisons.
14128
- * Does NOT handle: while loops, if/else, function calls, multi-statement blocks.
14129
- * For full evaluation, use mm-napi when available.
14130
- */
14131
- evaluateSimpleExpression(expr, context) {
14132
- if (context[expr] !== void 0) return context[expr];
14133
- const arithMatch = expr.match(/^(\w+)\s*([+\-*/])\s*(\d+(?:\.\d+)?)$/);
14134
- if (arithMatch) {
14135
- const [, field, op, numStr] = arithMatch;
14136
- const left = Number(context[field] ?? 0);
14137
- const right = Number(numStr);
14138
- switch (op) {
14139
- case "+":
14140
- return left + right;
14141
- case "-":
14142
- return left - right;
14143
- case "*":
14144
- return left * right;
14145
- case "/":
14146
- return right !== 0 ? left / right : 0;
14147
- }
14148
- }
14149
- if (/^\d+(\.\d+)?$/.test(expr.trim())) {
14150
- return Number(expr.trim());
14151
- }
14152
- const strMatch = expr.match(/^["'](.*)["']$/);
14153
- if (strMatch) return strMatch[1];
14154
- try {
14155
- const keys = Object.keys(context);
14156
- const values = Object.values(context);
14157
- const fn = new Function(...keys, `"use strict"; return (${expr});`);
14158
- return fn(...values);
14159
- } catch {
14160
- return null;
14161
- }
14162
- }
14163
- };
14164
- async function startLocalServer(options = {}) {
14165
- const { port = 4200, noAuth = true } = options;
14166
- const store = new MemoryStore();
14167
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
14168
- function json(res, status, body) {
14169
- const data = JSON.stringify(body);
14170
- res.writeHead(status, {
14171
- "Content-Type": "application/json",
14172
- "Access-Control-Allow-Origin": "*",
14173
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
14174
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
14175
- "Content-Length": Buffer.byteLength(data)
14176
- });
14177
- res.end(data);
14178
- }
14179
- function readBody(req) {
14180
- return new Promise((resolve3, reject) => {
14181
- const chunks = [];
14182
- req.on("data", (chunk) => chunks.push(chunk));
14183
- req.on("end", () => resolve3(Buffer.concat(chunks).toString()));
14184
- req.on("error", reject);
14185
- });
14186
- }
14187
- function parseQuery(url) {
14188
- const idx = url.indexOf("?");
14189
- if (idx === -1) return {};
14190
- const params = {};
14191
- const qs = url.slice(idx + 1);
14192
- for (const pair of qs.split("&")) {
14193
- const [k, v] = pair.split("=");
14194
- if (k) params[decodeURIComponent(k)] = decodeURIComponent(v ?? "");
14195
- }
14196
- return params;
14197
- }
14198
- const server = http.createServer(async (req, res) => {
14199
- const method = req.method?.toUpperCase() ?? "GET";
14200
- const rawUrl = req.url ?? "/";
14201
- const queryStart = rawUrl.indexOf("?");
14202
- const path = queryStart >= 0 ? rawUrl.slice(0, queryStart) : rawUrl;
14203
- const query = parseQuery(rawUrl);
14204
- if (method === "OPTIONS") {
14205
- res.writeHead(204, {
14206
- "Access-Control-Allow-Origin": "*",
14207
- "Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
14208
- "Access-Control-Allow-Headers": "Content-Type, Authorization",
14209
- "Access-Control-Max-Age": "86400"
14210
- });
14211
- res.end();
14212
- return;
14213
- }
14214
- try {
14215
- if (path === "/health" && method === "GET") {
14216
- return json(res, 200, {
14217
- status: "ok",
14218
- service: "mm-local-dev",
14219
- mode: "in-memory",
14220
- started_at: startedAt,
14221
- definitions: store.definitions.size,
14222
- instances: store.instances.size
14223
- });
14224
- }
14225
- if (path === "/api/v1/auth/login" && (method === "POST" || method === "GET")) {
14226
- return json(res, 200, {
14227
- token: "dev-token-local",
14228
- user: {
14229
- id: "dev-user-001",
14230
- email: "dev@localhost",
14231
- role: "admin",
14232
- name: "Local Developer"
14233
- }
14234
- });
14235
- }
14236
- if (path === "/api/v1/workflow/definitions" && method === "GET") {
14237
- const result = store.listDefinitions({
14238
- category: query.category,
14239
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
14240
- offset: query.offset ? parseInt(query.offset, 10) : void 0
14241
- });
14242
- if (query.slug) {
14243
- const def = store.getDefinition(query.slug);
14244
- return json(res, 200, { items: def ? [def] : [], total: def ? 1 : 0 });
14245
- }
14246
- return json(res, 200, result);
14247
- }
14248
- const defMatch = path.match(/^\/api\/v1\/workflow\/definitions\/([^/]+)$/);
14249
- if (defMatch && method === "GET") {
14250
- const def = store.getDefinition(defMatch[1]);
14251
- if (!def) return json(res, 404, { error: "Not found" });
14252
- return json(res, 200, def);
14253
- }
14254
- if (path === "/api/v1/workflow/definitions" && method === "POST") {
14255
- const body = JSON.parse(await readBody(req));
14256
- const def = store.createDefinition(body);
14257
- return json(res, 201, def);
14258
- }
14259
- if (defMatch && method === "PATCH") {
14260
- const body = JSON.parse(await readBody(req));
14261
- const updated = store.patchDefinition(defMatch[1], body);
14262
- if (!updated) return json(res, 404, { error: "Not found" });
14263
- return json(res, 200, updated);
14264
- }
14265
- if (defMatch && method === "DELETE") {
14266
- const deleted = store.deleteDefinition(defMatch[1]);
14267
- if (!deleted) return json(res, 404, { error: "Not found" });
14268
- return json(res, 204, null);
14269
- }
14270
- if (path === "/api/v1/workflow/instances" && method === "GET") {
14271
- const result = store.listInstances({
14272
- definition_id: query.definition_id,
14273
- limit: query.limit ? parseInt(query.limit, 10) : void 0,
14274
- offset: query.offset ? parseInt(query.offset, 10) : void 0
14275
- });
14276
- return json(res, 200, result);
14277
- }
14278
- if (path === "/api/v1/workflow/instances" && method === "POST") {
14279
- const body = JSON.parse(await readBody(req));
14280
- const inst = store.createInstance(body);
14281
- if (!inst) return json(res, 404, { error: "Definition not found" });
14282
- return json(res, 201, inst);
14283
- }
14284
- const instMatch = path.match(/^\/api\/v1\/workflow\/instances\/([^/]+)$/);
14285
- if (instMatch && method === "GET") {
14286
- const inst = store.getInstance(instMatch[1]);
14287
- if (!inst) return json(res, 404, { error: "Not found" });
14288
- return json(res, 200, inst);
14289
- }
14290
- if (path === "/api/v1/workflow/execute-action" && method === "POST") {
14291
- const body = JSON.parse(await readBody(req));
14292
- const result = store.executeAction(body);
14293
- return json(res, 200, result);
14294
- }
14295
- const dataMatch = path.match(/^\/api\/v1\/data\/([^/]+)$/);
14296
- if (dataMatch && method === "GET") {
14297
- const def = store.getDefinition(dataMatch[1]);
14298
- if (!def) return json(res, 404, { error: "Not found" });
14299
- const instances = store.listInstances({ definition_id: def.id });
14300
- return json(res, 200, instances);
14301
- }
14302
- if (path.startsWith("/api/v1/")) {
14303
- return json(res, 501, { error: "Not implemented in local dev mode", path, method });
14304
- }
14305
- return json(res, 404, { error: "Not found", path });
14306
- } catch (err) {
14307
- const message = err instanceof Error ? err.message : String(err);
14308
- console.error(`[mm-local] ${method} ${path} \u2014 Error: ${message}`);
14309
- return json(res, 500, { error: message });
14310
- }
14311
- });
14312
- return new Promise((resolve3, reject) => {
14313
- server.on("error", (err) => {
14314
- if (err.code === "EADDRINUSE") {
14315
- reject(new Error(`Port ${port} is already in use. Is another server running?`));
14316
- } else {
14317
- reject(err);
14318
- }
14319
- });
14320
- server.listen(port, () => {
14321
- console.log(`[mm-local] Local API server running at http://localhost:${port}`);
14322
- console.log(`[mm-local] Mode: in-memory (data lost on restart)`);
14323
- console.log(`[mm-local] Auth: disabled (all requests accepted)`);
14324
- resolve3({
14325
- server,
14326
- port,
14327
- store,
14328
- async close() {
14329
- return new Promise((res) => {
14330
- server.close(() => {
14331
- console.log("[mm-local] Local API server stopped");
14332
- res();
14333
- });
14334
- });
14335
- }
14336
- });
14337
- });
14338
- });
14339
- }
14340
-
14341
13928
  // src/dev-server.ts
14342
13929
  var currentErrors = null;
14343
13930
  function escapeHtml(s) {
@@ -14514,34 +14101,33 @@ async function createDevServer(options = {}) {
14514
14101
  open = false
14515
14102
  } = options;
14516
14103
  const clients = /* @__PURE__ */ new Set();
14517
- let localServer = null;
14518
14104
  let apiUrl;
14519
- let isLocalMode = false;
14520
- if (rawApiUrl === "local") {
14521
- localServer = await startLocalServer({ port: 4200 });
14522
- apiUrl = "http://localhost:4200/api/v1";
14523
- isLocalMode = true;
14524
- } else if (rawApiUrl === "auto") {
14525
- const defaultRemote = "https://dev.mindmatrix.club/api/v1";
14526
- const remoteHealth = await checkBackendHealth(defaultRemote);
14527
- if (remoteHealth.ok) {
14528
- apiUrl = defaultRemote;
14529
- } else {
14530
- const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
14531
- if (localHealth.ok) {
14532
- apiUrl = "http://localhost:4200/api/v1";
14105
+ if (rawApiUrl === "local" || rawApiUrl === "auto") {
14106
+ const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
14107
+ if (localHealth.ok) {
14108
+ apiUrl = "http://localhost:4200/api/v1";
14109
+ } else if (rawApiUrl === "auto") {
14110
+ const defaultRemote = "https://dev.mindmatrix.club/api/v1";
14111
+ const remoteHealth = await checkBackendHealth(defaultRemote);
14112
+ if (remoteHealth.ok) {
14113
+ apiUrl = defaultRemote;
14533
14114
  } else {
14534
- console.log("[mm-dev] No backend detected \u2014 starting local in-memory API server...");
14535
- localServer = await startLocalServer({ port: 4200 });
14115
+ console.error("[mm-dev] No backend detected. Start the engine first:");
14116
+ console.error(" mmrc dev (auto-starts engine)");
14117
+ console.error(" mmrc engine start (manual start)");
14118
+ console.error("[mm-dev] Or specify a remote API: mmrc dev --api-url https://...");
14536
14119
  apiUrl = "http://localhost:4200/api/v1";
14537
- isLocalMode = true;
14538
14120
  }
14121
+ } else {
14122
+ console.error("[mm-dev] Local engine not running on port 4200.");
14123
+ console.error(" Start it with: mmrc dev (or mmrc engine start)");
14124
+ apiUrl = "http://localhost:4200/api/v1";
14539
14125
  }
14540
14126
  } else {
14541
14127
  apiUrl = rawApiUrl;
14542
14128
  }
14543
- const token = isLocalMode ? "dev-token-local" : await resolveDevToken(apiUrl, explicitToken);
14544
- const health = isLocalMode ? { ok: true, version: "local", db: "in-memory" } : await checkBackendHealth(apiUrl);
14129
+ const token = await resolveDevToken(apiUrl, explicitToken);
14130
+ const health = await checkBackendHealth(apiUrl);
14545
14131
  let initialSlug, initialCompiled = 0, initialDeployed = false, initialErrors = 0;
14546
14132
  if (token && health.ok) {
14547
14133
  const r = await initialBuildDeploy(src, outDir, mode, apiUrl, token);
@@ -14601,63 +14187,169 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14601
14187
  `;
14602
14188
  }
14603
14189
  };
14604
- const devHtml = `<!DOCTYPE html>
14190
+ const { existsSync: fsExists } = await import("fs");
14191
+ const projectIndexPath = require("path").join(process.cwd(), "index.html");
14192
+ const hasProjectIndex = fsExists(projectIndexPath);
14193
+ if (!hasProjectIndex) {
14194
+ const { writeFileSync: fsWrite } = await import("fs");
14195
+ fsWrite(projectIndexPath, `<!DOCTYPE html>
14605
14196
  <html lang="en">
14606
14197
  <head>
14607
14198
  <meta charset="UTF-8">
14608
14199
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
14609
14200
  <title>MindMatrix Dev</title>
14610
- <style>
14611
- * { margin: 0; padding: 0; box-sizing: border-box; }
14612
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #fafafa; color: #111; }
14613
- #root { min-height: 100vh; }
14614
- .mm-loading { display: flex; align-items: center; justify-content: center; min-height: 100vh; flex-direction: column; gap: 12px; }
14615
- .mm-loading h1 { font-size: 20px; font-weight: 600; color: #333; }
14616
- .mm-loading p { color: #888; font-size: 14px; }
14617
- </style>
14618
14201
  </head>
14619
14202
  <body>
14620
- <div id="root">
14621
- <div class="mm-loading">
14622
- <h1>MindMatrix Dev</h1>
14623
- <p>Loading...</p>
14624
- </div>
14625
- </div>
14626
- <script>
14627
- async function render() {
14628
- const root = document.getElementById('root');
14203
+ <div id="root"></div>
14204
+ <script type="module" src="/__mm_dev_entry.tsx"></script>
14205
+ </body>
14206
+ </html>`, "utf-8");
14207
+ }
14208
+ const devEntryPlugin = {
14209
+ name: "mindmatrix-dev-entry",
14210
+ resolveId(id) {
14211
+ if (id === "/__mm_dev_entry.tsx") return id;
14212
+ return null;
14213
+ },
14214
+ load(id) {
14215
+ if (id !== "/__mm_dev_entry.tsx") return null;
14216
+ return `
14217
+ import React, { useState, useEffect } from 'react';
14218
+ import { createRoot } from 'react-dom/client';
14219
+ import { DevPlayer } from '@mmapp/react/player';
14220
+ import { PlayerProvider, ExperienceRenderer, createApiResolver } from '@mmapp/react/player';
14221
+
14222
+ // Detect API base URL \u2014 proxy through dev server or use direct URL
14223
+ const API_BASE = window.location.origin + '/api/v1';
14224
+ const resolver = createApiResolver({ baseUrl: API_BASE, token: () => localStorage.getItem('auth_token') });
14225
+
14226
+ /**
14227
+ * Prepare experience tree for rendering.
14228
+ * Attaches metadata.dataSources to the root node and remaps
14229
+ * dataSource slugs to use definition-relative naming.
14230
+ *
14231
+ * @param definition - The main blueprint definition
14232
+ * @param allDefinitions - ALL definitions from the API (used for slug resolution)
14233
+ */
14234
+ function prepareTree(definition, allDefinitions) {
14235
+ const exp = definition.experience;
14236
+ if (!exp || typeof exp !== 'object') return null;
14237
+
14238
+ const meta = definition.metadata || {};
14239
+ const dataSources = meta.dataSources || [];
14240
+ const mutationTargets = meta.mutationTargets || [];
14241
+
14242
+ // Build a slug map from ALL deployed definitions
14243
+ // This resolves bare model names (e.g. "item") to full slugs (e.g. "tutorial-1-item")
14244
+ const blueprintSlug = definition.slug || '';
14245
+ const allSlugs = (allDefinitions || []).map(d => d.slug).filter(Boolean);
14246
+
14247
+ function resolveSlug(bareSlug) {
14248
+ if (!bareSlug) return bareSlug;
14249
+ // Already a full slug (contains hyphen matching a known definition)
14250
+ if (allSlugs.includes(bareSlug)) return bareSlug;
14251
+ // Try blueprint-prefixed slug: "item" \u2192 "tutorial-1-item"
14252
+ const prefixed = blueprintSlug + '-' + bareSlug;
14253
+ if (allSlugs.includes(prefixed)) return prefixed;
14254
+ // Try matching by suffix across all definitions
14255
+ const match = allSlugs.find(s => s.endsWith('-' + bareSlug));
14256
+ if (match) return match;
14257
+ // Return original (will attempt as-is)
14258
+ return bareSlug;
14259
+ }
14260
+
14261
+ // Clone root node and attach dataSources
14262
+ const root = { ...exp };
14263
+ if (dataSources.length > 0 && !root.dataSources) {
14264
+ root.dataSources = dataSources.map(ds => ({
14265
+ ...ds,
14266
+ slug: resolveSlug(ds.slug),
14267
+ }));
14268
+ }
14269
+
14270
+ // Remap mutation targets the same way
14271
+ if (mutationTargets.length > 0) {
14272
+ const resolvedTargets = mutationTargets.map(t => resolveSlug(t));
14273
+ root.config = { ...(root.config || {}), _mutationTargets: resolvedTargets };
14274
+ }
14275
+
14276
+ return root;
14277
+ }
14278
+
14279
+ function App() {
14280
+ const [tree, setTree] = useState(null);
14281
+ const [error, setError] = useState(null);
14282
+
14283
+ useEffect(() => {
14284
+ async function loadTree() {
14629
14285
  try {
14630
- const res = await fetch('/api/v1/workflow/definitions');
14286
+ const res = await fetch(API_BASE + '/workflow/definitions');
14631
14287
  const json = await res.json();
14632
- const items = json.items || json.data || [];
14633
- let html = '<div style="padding:40px;max-width:960px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,sans-serif">';
14634
- html += '<h1 style="margin-bottom:8px">MindMatrix Dev</h1>';
14635
- html += '<p style="color:#666;margin-bottom:24px">' + items.length + ' definition(s) deployed</p>';
14636
- if (items.length === 0) {
14637
- html += '<div style="text-align:center;padding:60px 20px;color:#888"><p>No definitions yet.</p><p style="margin-top:8px;font-size:13px">Edit your model files and save.</p></div>';
14638
- }
14639
- for (const def of items) {
14640
- html += '<div style="border:1px solid #e0e0e0;border-radius:8px;padding:16px;margin-bottom:12px;background:#fff">';
14641
- html += '<h3 style="margin-bottom:4px">' + (def.name || def.slug || 'Unnamed') + '</h3>';
14642
- html += '<p style="color:#888;font-size:13px">slug: ' + (def.slug || '?') + ' | states: ' + (def.states?.length || 0) + ' | fields: ' + (def.fields?.length || 0) + '</p>';
14643
- html += '</div>';
14644
- }
14645
- html += '</div>';
14646
- root.innerHTML = html;
14288
+ const rawItems = json.items || json.data || [];
14289
+ // Flatten: API returns { id, slug, definition: { experience, ... } }
14290
+ // Merge definition fields up to the top level for easier access
14291
+ const items = rawItems.map(d => {
14292
+ const def = d.definition || {};
14293
+ return { ...d, ...def, definition: undefined };
14294
+ });
14295
+ // Find the main blueprint definition (has experience tree)
14296
+ const main = items.find(d => d.experience && !(Array.isArray(d.category) ? d.category.includes('data') : d.category === 'data')) || items.find(d => d.experience) || items[0];
14297
+ if (main?.experience) {
14298
+ const prepared = prepareTree(main, items);
14299
+ if (prepared) {
14300
+ setTree(prepared);
14301
+ return;
14302
+ }
14303
+ }
14304
+ if (main) {
14305
+ // No experience tree \u2014 show definition info
14306
+ setTree({
14307
+ type: 'Stack',
14308
+ props: { style: { padding: 40, gap: 16 } },
14309
+ children: [
14310
+ { type: 'Text', props: { children: main.name || main.slug, style: { fontSize: 24, fontWeight: 'bold' } } },
14311
+ { type: 'Text', props: { children: (main.states?.length || 0) + ' states, ' + (main.fields?.length || 0) + ' fields', style: { color: '#666' } } },
14312
+ ...items.map((d, i) => ({
14313
+ type: 'Card',
14314
+ props: { key: i, style: { padding: 16, border: '1px solid #ddd', borderRadius: 8 } },
14315
+ children: [
14316
+ { type: 'Text', props: { children: d.name || d.slug, style: { fontWeight: 600 } } },
14317
+ { type: 'Text', props: { children: 'slug: ' + d.slug + ' | ' + (d.states?.length || 0) + ' states', style: { fontSize: 13, color: '#888' } } },
14318
+ ],
14319
+ })),
14320
+ ],
14321
+ });
14322
+ }
14647
14323
  } catch (e) {
14648
- root.innerHTML = '<div style="padding:40px"><h1>MindMatrix Dev</h1><p style="color:#c00;margin-top:8px">API Error: ' + e.message + '</p></div>';
14324
+ setError(e.message);
14649
14325
  }
14650
14326
  }
14651
- render();
14652
- setInterval(render, 3000);
14653
- </script>
14654
- </body>
14655
- </html>`;
14656
- const { mkdtempSync, writeFileSync: writeFileSync5 } = await import("fs");
14657
- const { join: join4 } = await import("path");
14658
- const { tmpdir } = await import("os");
14659
- const devRoot = mkdtempSync(join4(tmpdir(), "mm-dev-"));
14660
- writeFileSync5(join4(devRoot, "index.html"), devHtml, "utf-8");
14327
+ loadTree();
14328
+ }, []);
14329
+
14330
+ if (error) return React.createElement('div', { style: { padding: 40 } },
14331
+ React.createElement('h1', null, 'MindMatrix Dev'),
14332
+ React.createElement('p', { style: { color: '#c00' } }, 'Error: ' + error)
14333
+ );
14334
+
14335
+ if (!tree) return React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' } },
14336
+ React.createElement('p', null, 'Loading...')
14337
+ );
14338
+
14339
+ // If tree has 'component' field, use ExperienceRenderer; otherwise legacy DevPlayer
14340
+ if (tree.component) {
14341
+ return React.createElement(PlayerProvider, { resolver },
14342
+ React.createElement(ExperienceRenderer, { tree, className: 'mm-experience-root' })
14343
+ );
14344
+ }
14345
+
14346
+ return React.createElement(DevPlayer, { tree, title: document.title || 'MindMatrix Dev' });
14347
+ }
14348
+
14349
+ createRoot(document.getElementById('root')).render(React.createElement(App));
14350
+ `;
14351
+ }
14352
+ };
14661
14353
  let deployInFlight = false;
14662
14354
  const compileDeployPlugin = {
14663
14355
  name: "mindmatrix-dev-compile-deploy",
@@ -14688,16 +14380,16 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14688
14380
  }
14689
14381
  };
14690
14382
  const viteConfig = {
14691
- // Physical index.html lives in a temp dir so Vite serves it natively.
14692
- root: devRoot,
14693
- // Pre-bundle React so Vite doesn't warn about missing entry point.
14383
+ // Use the blueprint directory as Vite root (has node_modules with React).
14384
+ root: process.cwd(),
14385
+ // Pre-bundle React and the player for fast dev startup.
14694
14386
  optimizeDeps: { include: ["react", "react-dom/client"] },
14695
14387
  server: {
14696
14388
  port,
14697
14389
  open,
14698
14390
  host: true,
14699
14391
  // Allow serving files from the real project directory (fs.allow).
14700
- fs: { allow: [devRoot, process.cwd(), ".."] },
14392
+ fs: { allow: [process.cwd(), ".."] },
14701
14393
  proxy: {
14702
14394
  "/api": { target: proxyTarget, changeOrigin: true, secure: true, ws: true },
14703
14395
  "/health": { target: proxyTarget, changeOrigin: true, secure: true },
@@ -14707,6 +14399,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14707
14399
  plugins: [
14708
14400
  mindmatrixReact(pluginOpts),
14709
14401
  compileDeployPlugin,
14402
+ devEntryPlugin,
14710
14403
  devPlayerPlugin,
14711
14404
  { name: "mindmatrix-error-overlay", configureServer(server) {
14712
14405
  server.middlewares.use(errorOverlayMiddleware());
@@ -14714,8 +14407,8 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14714
14407
  ],
14715
14408
  logLevel: "warn"
14716
14409
  };
14717
- const { createServer: createServer2 } = await import("vite");
14718
- const vite = await createServer2(viteConfig);
14410
+ const { createServer } = await import("vite");
14411
+ const vite = await createServer(viteConfig);
14719
14412
  await vite.listen();
14720
14413
  const resolvedPort = vite.config.server.port ?? port;
14721
14414
  if (enableWs && vite.httpServer) {
@@ -14733,7 +14426,7 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14733
14426
  } catch {
14734
14427
  }
14735
14428
  }
14736
- printBanner({ port: resolvedPort, apiUrl: isLocalMode ? `${apiUrl} (local in-memory)` : apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
14429
+ printBanner({ port: resolvedPort, apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
14737
14430
  return {
14738
14431
  vite,
14739
14432
  port: resolvedPort,
@@ -14765,14 +14458,6 @@ createRoot(document.getElementById('root')).render(React.createElement(App));
14765
14458
  clients.clear();
14766
14459
  currentErrors = null;
14767
14460
  await vite.close();
14768
- if (localServer) {
14769
- await localServer.close();
14770
- }
14771
- try {
14772
- const { rmSync } = await import("fs");
14773
- rmSync(devRoot, { recursive: true, force: true });
14774
- } catch {
14775
- }
14776
14461
  }
14777
14462
  };
14778
14463
  }