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

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 (251) hide show
  1. package/ATOM-PIPELINE.md +144 -0
  2. package/README.md +88 -40
  3. package/dist/babel/index.js +113 -6
  4. package/dist/babel/index.mjs +2 -2
  5. package/dist/chunk-3USIFFE4.mjs +2190 -0
  6. package/dist/chunk-45YMGEVT.mjs +186 -0
  7. package/dist/chunk-4FN2AISW.mjs +148 -0
  8. package/dist/chunk-4OPI5L7G.mjs +2593 -0
  9. package/dist/chunk-4RYTKOOJ.mjs +186 -0
  10. package/dist/chunk-5RKTOVR5.mjs +244 -0
  11. package/dist/chunk-5YDMOO4X.mjs +214 -0
  12. package/dist/chunk-64ZWEMLJ.mjs +148 -0
  13. package/dist/chunk-6XP4KSWQ.mjs +2190 -0
  14. package/dist/chunk-72QWL54I.mjs +175 -0
  15. package/dist/chunk-7B4TRI7C.mjs +4835 -0
  16. package/dist/chunk-7ZKGHTNB.mjs +4952 -0
  17. package/dist/chunk-CIESM3BP.mjs +33 -0
  18. package/dist/chunk-DE3ZGQAC.mjs +148 -0
  19. package/dist/chunk-DMCY3BBG.mjs +1933 -0
  20. package/dist/chunk-DPIK3PJS.mjs +244 -0
  21. package/dist/chunk-E5IVH4RE.mjs +186 -0
  22. package/dist/chunk-E6FZNUR5.mjs +4953 -0
  23. package/dist/chunk-EJRBDQDP.mjs +2607 -0
  24. package/dist/chunk-ELO4TXJL.mjs +186 -0
  25. package/dist/chunk-FKRO52XH.mjs +3446 -0
  26. package/dist/chunk-FL4YAKU6.mjs +4941 -0
  27. package/dist/chunk-FYT47UBU.mjs +5076 -0
  28. package/dist/chunk-GCLGPOJZ.mjs +148 -0
  29. package/dist/chunk-GXB4JOP7.mjs +5072 -0
  30. package/dist/chunk-HFXOUMTD.mjs +175 -0
  31. package/dist/chunk-HWIZ47US.mjs +214 -0
  32. package/dist/chunk-IB7MNPQL.mjs +4953 -0
  33. package/dist/chunk-ICSIHQCG.mjs +148 -0
  34. package/dist/chunk-JLA5VNQ3.mjs +186 -0
  35. package/dist/chunk-JQLWFCTM.mjs +214 -0
  36. package/dist/chunk-KFJJCQAL.mjs +148 -0
  37. package/dist/chunk-KJUIIEQE.mjs +186 -0
  38. package/dist/chunk-KNWTHRVQ.mjs +175 -0
  39. package/dist/chunk-KSG4XSZF.mjs +175 -0
  40. package/dist/chunk-LF5N6DOU.mjs +175 -0
  41. package/dist/chunk-LJQCM2IM.mjs +214 -0
  42. package/dist/chunk-NW6555WJ.mjs +186 -0
  43. package/dist/chunk-OMZE6VLQ.mjs +214 -0
  44. package/dist/chunk-P4BR7WVO.mjs +2190 -0
  45. package/dist/chunk-QQHVYH2X.mjs +244 -0
  46. package/dist/chunk-S5QLWLLT.mjs +186 -0
  47. package/dist/chunk-SCWGT2FY.mjs +2190 -0
  48. package/dist/chunk-SMKJUSB3.mjs +2190 -0
  49. package/dist/chunk-VCAY2KGM.mjs +175 -0
  50. package/dist/chunk-WECAV6QB.mjs +148 -0
  51. package/dist/chunk-WMKBXUCE.mjs +3228 -0
  52. package/dist/chunk-XAJ5BKKL.mjs +4947 -0
  53. package/dist/chunk-XG2X7AEA.mjs +175 -0
  54. package/dist/chunk-XG7Z23NQ.mjs +148 -0
  55. package/dist/chunk-XWZAOCQ7.mjs +2607 -0
  56. package/dist/chunk-Y6MA7ULW.mjs +148 -0
  57. package/dist/chunk-YMS7Q7LG.mjs +214 -0
  58. package/dist/chunk-ZA37XTGA.mjs +175 -0
  59. package/dist/cli/index.js +1616 -366
  60. package/dist/cli/index.mjs +8 -8
  61. package/dist/codemod/cli.mjs +1 -1
  62. package/dist/codemod/index.mjs +1 -1
  63. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  64. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  65. package/dist/dev-server.d.mts +1 -1
  66. package/dist/dev-server.d.ts +1 -1
  67. package/dist/dev-server.js +982 -53
  68. package/dist/dev-server.mjs +5 -5
  69. package/dist/envelope.js +113 -6
  70. package/dist/envelope.mjs +3 -3
  71. package/dist/index.d.mts +5 -1
  72. package/dist/index.d.ts +5 -1
  73. package/dist/index.js +992 -63
  74. package/dist/index.mjs +8 -8
  75. package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
  76. package/dist/init-EHO4VQ22.mjs +369 -0
  77. package/dist/init-UC3FWPIW.mjs +367 -0
  78. package/dist/init-UNSMVKIK.mjs +366 -0
  79. package/dist/init-UNV5XIDE.mjs +367 -0
  80. package/dist/project-compiler-2P4N4DR7.mjs +10 -0
  81. package/dist/project-compiler-D2LCC27O.mjs +10 -0
  82. package/dist/project-compiler-EJ3GANJE.mjs +10 -0
  83. package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
  84. package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
  85. package/dist/project-compiler-VWNNCHGO.mjs +10 -0
  86. package/dist/project-compiler-XVAAU4C5.mjs +10 -0
  87. package/dist/project-compiler-YES5FGMD.mjs +10 -0
  88. package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
  89. package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
  90. package/dist/project-decompiler-VLPR22QF.mjs +7 -0
  91. package/dist/pull-FUS5QYZS.mjs +109 -0
  92. package/dist/pull-LD5ENLGY.mjs +109 -0
  93. package/dist/testing/index.js +113 -6
  94. package/dist/testing/index.mjs +2 -2
  95. package/dist/vite/index.js +113 -6
  96. package/dist/vite/index.mjs +3 -3
  97. package/examples/uber-app/app/admin/fleet.tsx +19 -19
  98. package/package.json +4 -3
  99. package/compile-blueprint-chat.mjs +0 -99
  100. package/compile-blueprint-glass-console.mjs +0 -98
  101. package/compile-chat-defs.mjs +0 -92
  102. package/examples/uber-app/tests/payment.test.tsx +0 -129
  103. package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
  104. package/package.json.backup +0 -86
  105. package/scripts/decompile.ts +0 -226
  106. package/scripts/seed-auth.ts +0 -267
  107. package/scripts/seed-uber.ts +0 -248
  108. package/scripts/validate-uber.ts +0 -119
  109. package/seed-blueprint-chat.mjs +0 -444
  110. package/seed-blueprint-glass-console.mjs +0 -445
  111. package/seed-compiled.mjs +0 -318
  112. package/src/RoundTripValidator.ts +0 -400
  113. package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
  114. package/src/__tests__/auth-module-compilation.test.ts +0 -247
  115. package/src/__tests__/auth-template-compilation.test.ts +0 -589
  116. package/src/__tests__/change-extractor.test.ts +0 -142
  117. package/src/__tests__/cli-pull.test.ts +0 -73
  118. package/src/__tests__/cli-test.test.ts +0 -72
  119. package/src/__tests__/component-extractor.test.ts +0 -331
  120. package/src/__tests__/context-extractor.test.ts +0 -145
  121. package/src/__tests__/decompiler.test.ts +0 -718
  122. package/src/__tests__/define-blueprint.test.ts +0 -133
  123. package/src/__tests__/definition-validator.test.ts +0 -519
  124. package/src/__tests__/during-extractor.test.ts +0 -152
  125. package/src/__tests__/effect-extractor.test.ts +0 -107
  126. package/src/__tests__/event-emission.test.ts +0 -127
  127. package/src/__tests__/examples.test.ts +0 -236
  128. package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
  129. package/src/__tests__/golden-suite.test.ts +0 -403
  130. package/src/__tests__/grammar-island-extractor.test.ts +0 -289
  131. package/src/__tests__/instance-key.test.ts +0 -82
  132. package/src/__tests__/ir-migration.test.ts +0 -255
  133. package/src/__tests__/lock-file.test.ts +0 -117
  134. package/src/__tests__/model-extractor.test.ts +0 -195
  135. package/src/__tests__/model-field-acl.test.ts +0 -237
  136. package/src/__tests__/model-hooks.test.ts +0 -130
  137. package/src/__tests__/model-ref-resolution.test.ts +0 -268
  138. package/src/__tests__/model-roundtrip.test.ts +0 -502
  139. package/src/__tests__/model-runtime.test.ts +0 -112
  140. package/src/__tests__/model-transitions.test.ts +0 -183
  141. package/src/__tests__/nrt-action-trace.test.ts +0 -391
  142. package/src/__tests__/pipeline-hardening.test.ts +0 -413
  143. package/src/__tests__/project-compiler.test.ts +0 -546
  144. package/src/__tests__/project-decompiler.test.ts +0 -343
  145. package/src/__tests__/query-compilation.test.ts +0 -145
  146. package/src/__tests__/round-trip/PLAN.md +0 -158
  147. package/src/__tests__/round-trip/README.md +0 -52
  148. package/src/__tests__/round-trip/RESULTS.md +0 -86
  149. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
  150. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
  151. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
  152. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
  153. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
  154. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
  155. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
  156. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
  157. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
  158. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
  159. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
  160. package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
  161. package/src/__tests__/round-trip-ir.test.ts +0 -300
  162. package/src/__tests__/round-trip.test.ts +0 -1212
  163. package/src/__tests__/route-merging.test.ts +0 -372
  164. package/src/__tests__/router-composition.test.ts +0 -489
  165. package/src/__tests__/router-extractor.test.ts +0 -176
  166. package/src/__tests__/server-action-extractor.test.ts +0 -128
  167. package/src/__tests__/smart-type-inference.test.ts +0 -365
  168. package/src/__tests__/source-envelope.test.ts +0 -284
  169. package/src/__tests__/source-fidelity.test.ts +0 -516
  170. package/src/__tests__/state-extractor.test.ts +0 -115
  171. package/src/__tests__/strict-mode.test.ts +0 -227
  172. package/src/__tests__/transition-effect-extractor.test.ts +0 -119
  173. package/src/__tests__/transition-extractor.test.ts +0 -68
  174. package/src/__tests__/ts-to-expression.test.ts +0 -462
  175. package/src/__tests__/type-generator.test.ts +0 -201
  176. package/src/__tests__/uber-validation.test.ts +0 -502
  177. package/src/action-compiler.ts +0 -361
  178. package/src/babel/emitters/experience-transform.ts +0 -199
  179. package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
  180. package/src/babel/emitters/pure-form-emitter.ts +0 -1023
  181. package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
  182. package/src/babel/extractors/change-extractor.ts +0 -199
  183. package/src/babel/extractors/component-extractor.ts +0 -907
  184. package/src/babel/extractors/computed-extractor.ts +0 -262
  185. package/src/babel/extractors/context-extractor.ts +0 -277
  186. package/src/babel/extractors/during-extractor.ts +0 -295
  187. package/src/babel/extractors/effect-extractor.ts +0 -340
  188. package/src/babel/extractors/event-extractor.ts +0 -235
  189. package/src/babel/extractors/grammar-island-extractor.ts +0 -302
  190. package/src/babel/extractors/model-extractor.ts +0 -1018
  191. package/src/babel/extractors/router-extractor.ts +0 -303
  192. package/src/babel/extractors/server-action-extractor.ts +0 -173
  193. package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
  194. package/src/babel/extractors/server-state-extractor.ts +0 -88
  195. package/src/babel/extractors/state-extractor.ts +0 -214
  196. package/src/babel/extractors/transition-effect-extractor.ts +0 -176
  197. package/src/babel/extractors/transition-extractor.ts +0 -143
  198. package/src/babel/index.ts +0 -24
  199. package/src/babel/transpilers/ts-to-expression.ts +0 -674
  200. package/src/babel/visitor.ts +0 -807
  201. package/src/cli/auth.ts +0 -255
  202. package/src/cli/build.ts +0 -288
  203. package/src/cli/deploy.ts +0 -206
  204. package/src/cli/index.ts +0 -328
  205. package/src/cli/installer.ts +0 -261
  206. package/src/cli/lock-file.ts +0 -94
  207. package/src/cli/mmrc.ts +0 -22
  208. package/src/cli/pull.ts +0 -172
  209. package/src/cli/registry-client.ts +0 -175
  210. package/src/cli/test.ts +0 -397
  211. package/src/cli/type-generator.ts +0 -243
  212. package/src/codemod/__tests__/forward.test.ts +0 -239
  213. package/src/codemod/__tests__/reverse.test.ts +0 -145
  214. package/src/codemod/__tests__/round-trip.test.ts +0 -137
  215. package/src/codemod/annotation.ts +0 -97
  216. package/src/codemod/classify.ts +0 -197
  217. package/src/codemod/cli.ts +0 -207
  218. package/src/codemod/control-flow.ts +0 -409
  219. package/src/codemod/forward.ts +0 -244
  220. package/src/codemod/import-manager.ts +0 -171
  221. package/src/codemod/index.ts +0 -120
  222. package/src/codemod/reverse.ts +0 -197
  223. package/src/codemod/rules.ts +0 -174
  224. package/src/codemod/state-transform.ts +0 -126
  225. package/src/decompiler/ast-builder.ts +0 -538
  226. package/src/decompiler/config-generator.ts +0 -151
  227. package/src/decompiler/index.ts +0 -315
  228. package/src/decompiler/project-decompiler.ts +0 -1776
  229. package/src/decompiler/project.ts +0 -862
  230. package/src/decompiler/split-strategy.ts +0 -140
  231. package/src/decompiler/state-emitter.ts +0 -1053
  232. package/src/decompiler/sx-emitter.ts +0 -318
  233. package/src/decompiler/workspace-hydrator.ts +0 -189
  234. package/src/dev-server.ts +0 -238
  235. package/src/envelope/fs-tree.ts +0 -217
  236. package/src/envelope/source-envelope.ts +0 -264
  237. package/src/envelope.ts +0 -315
  238. package/src/incremental-compiler.ts +0 -401
  239. package/src/index.ts +0 -99
  240. package/src/model-compiler.ts +0 -277
  241. package/src/project-compiler.ts +0 -1629
  242. package/src/route-extractor.ts +0 -333
  243. package/src/testing/index.ts +0 -32
  244. package/src/testing/snapshot.ts +0 -252
  245. package/src/testing/test-utils.ts +0 -226
  246. package/src/types.ts +0 -68
  247. package/src/vite/index.ts +0 -288
  248. package/test-compile.mjs +0 -142
  249. package/tsconfig.json +0 -25
  250. package/tsup.config.ts +0 -23
  251. package/vitest.config.ts +0 -9
@@ -0,0 +1,144 @@
1
+ # Atom Pipeline — How Components Flow From Authoring to Runtime
2
+
3
+ ## The Two Atom Packages Are NOT Duplicates
4
+
5
+ There are two places where atom components live. They serve different purposes and are **not** supposed to be identical.
6
+
7
+ ### `packages/react/src/components/atoms/` — Authoring Atoms (14 components)
8
+
9
+ These are **specifications**, not implementations. They exist so that `.workflow.tsx` authors get TypeScript completion when writing views:
10
+
11
+ ```tsx
12
+ import { Stack, Text, Button, TextInput } from '@mindmatrix/react';
13
+
14
+ export default function MyPage() {
15
+ return (
16
+ <Stack gap={16}>
17
+ <Text variant="h3" value="Hello" />
18
+ <TextInput placeholder="Type here..." onChange={setName} />
19
+ <Button variant="primary" onPress={submit}>Save</Button>
20
+ </Stack>
21
+ );
22
+ }
23
+ ```
24
+
25
+ The Babel compiler (`react-compiler`) extracts this JSX into IR nodes. These components **never render in production**. They only need enough implementation to satisfy TypeScript and render a basic preview in tests.
26
+
27
+ **Full list:** Stack, Row, Column, Text, Heading, Button, TextInput, Each, Show, Slot, Badge, Card, Divider
28
+
29
+ ### `packages/frontend/src/experiences/atoms/` — Runtime Atoms (50+ components)
30
+
31
+ These are **real React components** that render in the browser. They implement the full visual and interactive behavior: Radix UI primitives, Tailwind styling, event handling, accessibility, config prop resolution, Style IR support.
32
+
33
+ The `ComponentTreeRenderer` maps IR node `component` strings to these atoms via the component registry (`component-registry.ts`). When the experience tree has `{ component: 'TextInput', config: { placeholder: '...' } }`, the registry resolves it to the `TextInput` atom from this directory.
34
+
35
+ **These include everything the authoring atoms have, plus:** Select, Icon, Image, Modal, Tabs, Accordion, Router, Route, NavLink, RoleGuard, AnimatedBox, Canvas3D, DataGrid, Chart, MetricCard, ServerGrid, all media atoms (Video, Audio, Map), interactive atoms (Slider, Toggle, Rating, Counter), data display (Tag, Timeline, Pricing), layout (Breadcrumb, Stepper, Drawer), and 17 sub-experience components.
36
+
37
+ ## Why They Differ
38
+
39
+ The authoring atoms are intentionally minimal because:
40
+
41
+ 1. **Different players implement different subsets.** A web player has 90+ renderers. An iOS player might implement 30. A console/CLI player might implement 10. The authoring atoms define the universal vocabulary — what authors can write. Each player decides what it can render, falling back gracefully for unknown components.
42
+
43
+ 2. **The IR is the contract, not the React components.** The compiled IR node `{ component: 'TextInput', config: { placeholder, maxLength, multiline } }` is the spec. Any player that can render a `TextInput` with those config keys is compliant. The React authoring atoms are just one convenient way to produce that IR.
44
+
45
+ 3. **Player-specific atoms shouldn't pollute the authoring SDK.** The web player has `Canvas3D`, `DataGrid`, `GlassConsoleRunner`. These are web-specific. They don't belong in the authoring package that iOS developers also import.
46
+
47
+ ## The Full Pipeline
48
+
49
+ ```
50
+ Author writes .workflow.tsx
51
+
52
+
53
+ @mindmatrix/react atoms (Stack, Text, Button, ...)
54
+ │ TypeScript types + basic rendering for preview
55
+
56
+
57
+ react-compiler Babel plugin
58
+ │ component-extractor.ts extracts JSX → IRExperienceNode
59
+ │ Each JSX element becomes { component, config, bindings, children }
60
+
61
+
62
+ IRExperienceNode tree (stored in database)
63
+ │ component: "TextInput"
64
+ │ config: { placeholder: "Search...", maxLength: 100 }
65
+ │ bindings: { value: "$local.query", onChange: "$action.setLocal('query', $value)" }
66
+
67
+
68
+ ComponentTreeRenderer (packages/frontend/src/experiences/)
69
+ │ Walks the IR tree, resolves each node.component via component-registry
70
+ │ Applies binding-resolver for dynamic values
71
+ │ Passes config as props, style from Style IR
72
+
73
+
74
+ Frontend atoms render in the browser
75
+ TextInput, SelectAtom, ButtonAtom, etc.
76
+ Full Radix UI + Tailwind + accessibility
77
+ ```
78
+
79
+ ## The Decompiler (Reverse Direction)
80
+
81
+ The decompiler converts IR back to `.workflow.tsx` source:
82
+
83
+ ```
84
+ IRExperienceNode tree
85
+
86
+
87
+ ast-builder.ts builds Babel JSX AST
88
+ │ Uses REACT_ATOMS set (32 components) to decide imports
89
+ │ Maps IR tokens ($local.x, $action.y) back to variable names
90
+
91
+
92
+ @babel/generator produces source code
93
+
94
+
95
+ .workflow.tsx with imports from @mindmatrix/react
96
+ ```
97
+
98
+ The `REACT_ATOMS` set in `ast-builder.ts` determines which components get `import { X } from '@mindmatrix/react'` in the decompiled output. Components not in this set are emitted as bare identifiers (assumed to be local imports or custom components).
99
+
100
+ ## Style IR and the `style` Prop
101
+
102
+ The compiled experience tree may attach Style IR to nodes as inline `style` objects. These flow through to the atom's wrapper div. This means **atoms receive border, borderRadius, background, etc. from the compiled view** — not just from their own CSS.
103
+
104
+ If an atom should always be borderless (like TextInput and Select inside Cards), it must explicitly override the style prop:
105
+
106
+ ```tsx
107
+ <div style={{ ...style, border: 'none', borderRadius: 0, background: 'transparent' }}>
108
+ ```
109
+
110
+ This is by design — Style IR gives authors control over visual appearance, but atoms can override properties that conflict with their intended rendering.
111
+
112
+ ## Coverage Validation
113
+
114
+ `atom-rendering-coverage.test.ts` validates the full pipeline:
115
+
116
+ 1. Compiles all blueprint packages and examples to IR
117
+ 2. Collects every unique `component` string from IR trees
118
+ 3. Checks each against `KNOWN_RENDERERS` (90+ registered) and `STRUCTURAL_COMPONENTS` (HTML elements)
119
+ 4. Fails if any IR component has no renderer
120
+
121
+ This catches: new authoring atoms missing a frontend renderer, typos in component names, and regression when atoms are renamed.
122
+
123
+ ## Key Files
124
+
125
+ | File | Purpose |
126
+ |------|---------|
127
+ | `packages/react/src/components/atoms/index.tsx` | Authoring atoms (14) — the author's vocabulary |
128
+ | `packages/frontend/src/experiences/atoms/` | Runtime atoms (50+) — what actually renders |
129
+ | `packages/frontend/src/experiences/ComponentTreeRenderer.tsx` | Walks IR tree, resolves components, applies bindings |
130
+ | `packages/frontend/src/experiences/component-registry.ts` | Maps component name strings → lazy-loaded React components |
131
+ | `packages/frontend/src/experiences/binding-resolver.ts` | Resolves $local, $instance, $action, $fn expressions |
132
+ | `packages/react-compiler/src/babel/extractors/component-extractor.ts` | Extracts JSX → IR nodes during compilation |
133
+ | `packages/react-compiler/src/decompiler/ast-builder.ts` | Builds JSX AST from IR nodes (REACT_ATOMS set) |
134
+ | `packages/react-compiler/src/__tests__/atom-rendering-coverage.test.ts` | Validates no gaps in the pipeline |
135
+
136
+ ## Rules of Thumb
137
+
138
+ - **Adding a new authoring atom?** Add it to `packages/react/src/components/atoms/`, export from the package, and add a matching renderer in `packages/frontend/src/experiences/atoms/`. Register it in `component-registry.ts`. The coverage test will catch if you forget the renderer.
139
+
140
+ - **Adding a web-only atom?** Add it only to `packages/frontend/src/experiences/atoms/` and register in `component-registry.ts`. Don't add it to the authoring package — it's platform-specific.
141
+
142
+ - **Adding a new player (iOS, CLI, etc.)?** Implement renderers for whichever atoms make sense for that platform. The IR is the contract. Unknown components should render a fallback, not crash.
143
+
144
+ - **Atom props drifting?** The coverage test catches missing atoms but not missing props. If you add a new config key to an authoring atom, make sure the corresponding frontend atom handles it. TypeScript won't catch this — it crosses package boundaries through IR serialization.
package/README.md CHANGED
@@ -1,21 +1,47 @@
1
- # @mindmatrix/react-compiler
1
+ # @mmapp/react-compiler
2
2
 
3
- Babel plugin + Vite integration that transforms React workflow files into Pure Form (IR) JSON output.
3
+ Babel plugin, Vite integration, and CLI for compiling React workflow files into Pure Form IR (Intermediate Representation).
4
+
5
+ Published on npm as `@mmapp/react-compiler` (internal workspace name: `@mindmatrix/react-compiler`).
4
6
 
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
- pnpm add -D @mindmatrix/react-compiler
10
+ npm install -g @mmapp/react-compiler # for mmrc CLI
11
+ npm install -D @mmapp/react-compiler # for Vite/Babel plugin
9
12
  ```
10
13
 
11
- ## Usage
14
+ ## CLI — `mmrc`
15
+
16
+ ```bash
17
+ # Scaffold a new blueprint
18
+ mmrc init my-app
19
+
20
+ # Build workflows to IR
21
+ mmrc build --src src/workflows --out dist/workflows
22
+
23
+ # Deploy to MindMatrix server
24
+ mmrc deploy --api-url https://dev.mindmatrix.club --token YOUR_TOKEN
25
+
26
+ # Start dev server with hot reload
27
+ mmrc dev
28
+
29
+ # Run workflow tests
30
+ mmrc test
31
+
32
+ # Pull remote definitions to local files
33
+ mmrc pull --slug my-workflow
12
34
 
13
- ### Vite Plugin
35
+ # Auth
36
+ mmrc login / mmrc logout / mmrc whoami
37
+ ```
38
+
39
+ ## Vite Plugin
14
40
 
15
41
  ```typescript
16
42
  // vite.config.ts
17
43
  import { defineConfig } from 'vite';
18
- import { mindmatrixReact } from '@mindmatrix/react-compiler/vite';
44
+ import { mindmatrixReact } from '@mmapp/react-compiler/vite';
19
45
 
20
46
  export default defineConfig({
21
47
  plugins: [
@@ -27,41 +53,36 @@ export default defineConfig({
27
53
  });
28
54
  ```
29
55
 
30
- ### Babel Plugin
56
+ ## Babel Plugin
31
57
 
32
58
  ```javascript
33
59
  // babel.config.js
34
60
  module.exports = {
35
61
  plugins: [
36
- ['@mindmatrix/react-compiler/babel', { mode: 'infer' }]
62
+ ['@mmapp/react-compiler/babel', { mode: 'infer' }]
37
63
  ]
38
64
  };
39
65
  ```
40
66
 
41
- ### CLI
42
-
43
- ```bash
44
- # Build workflows
45
- pnpm mmrc build --src src/workflows --out dist/workflows
46
-
47
- # Deploy workflows
48
- pnpm mmrc deploy --api-url http://localhost:3010 --token YOUR_TOKEN
49
- ```
50
-
51
- ## Features
52
-
53
- - Extract `useState` calls IR field definitions
54
- - Extract `useOnEnter`, `useOnExit` IR action definitions
55
- - Extract `useTransition` → IR transition definitions
56
- - Extract `useOnEvent` → IR event subscriptions
57
- - Extract JSX components → IR experience nodes
58
- - Emit Pure Form JSON matching `IRWorkflowDefinition` schema
67
+ ## What It Compiles
68
+
69
+ | Source Pattern | IR Output |
70
+ |---------------|-----------|
71
+ | `useState(initial)` | Field definition |
72
+ | `useOnEnter(state, fn)` | State on_enter actions |
73
+ | `useOnExit(state, fn)` | State on_exit actions |
74
+ | `useTransition(name)` | Transition definition |
75
+ | `useOnEvent(pattern, fn)` | Event subscription |
76
+ | JSX components | Experience tree nodes |
77
+ | `defineModel({...})` | Model IR (fields, states, transitions) |
78
+ | `defineBlueprint({...})` | Blueprint manifest |
79
+ | `useServerAction(name)` | Server action binding |
80
+ | Router/Route composition | Route definitions |
59
81
 
60
82
  ## Example
61
83
 
62
84
  ```tsx
63
- // src/workflows/approval.workflow.tsx
64
- import { useState, useOnEnter, useTransition } from '@mindmatrix/react';
85
+ import { useState, useOnEnter, useTransition } from '@mmapp/react';
65
86
 
66
87
  export function ApprovalWorkflow() {
67
88
  const [status, setStatus] = useState('pending');
@@ -83,25 +104,52 @@ Compiles to:
83
104
  {
84
105
  "slug": "approval",
85
106
  "name": "ApprovalWorkflow",
86
- "version": "0.1.0",
87
- "category": "workflow",
88
107
  "fields": [
89
108
  { "name": "status", "type": "text", "default_value": "pending" },
90
109
  { "name": "approver", "type": "text", "default_value": "" }
91
110
  ],
92
111
  "states": [
93
- {
94
- "name": "approved",
95
- "type": "REGULAR",
96
- "on_enter": [
97
- { "id": "auto_1", "type": "set_field", "mode": "auto", "config": { "field": "status", "value": "done" } }
98
- ],
99
- "during": [],
100
- "on_exit": []
101
- }
112
+ { "name": "approved", "type": "REGULAR",
113
+ "on_enter": [{ "type": "set_field", "config": { "field": "status", "value": "done" } }] }
102
114
  ],
103
115
  "transitions": [
104
- { "name": "approve", "from": [], "to": "approved", "actions": [], "conditions": [] }
116
+ { "name": "approve", "from": [], "to": "approved" }
105
117
  ]
106
118
  }
107
119
  ```
120
+
121
+ ## npm Publishing
122
+
123
+ This package is published to npm under the `@mmapp` scope.
124
+
125
+ | npm name | workspace name | current version |
126
+ |----------|---------------|-----------------|
127
+ | `@mmapp/react-compiler` | `@mindmatrix/react-compiler` | 0.1.0-alpha.1 |
128
+
129
+ ### Publishing a new version
130
+
131
+ Use the publish script from the monorepo root:
132
+
133
+ ```bash
134
+ # Publish all 3 packages (player-core, react, react-compiler)
135
+ ./scripts/publish-npm.sh patch # 0.1.0-alpha.1 → 0.1.0-alpha.2
136
+ ./scripts/publish-npm.sh minor # 0.1.0-alpha.1 → 0.2.0-alpha.1
137
+ ./scripts/publish-npm.sh 0.2.0-beta.1 # explicit version
138
+ ```
139
+
140
+ Or publish just this package:
141
+
142
+ ```bash
143
+ cd packages/react-compiler
144
+ pnpm run publish:alpha # auto-increments alpha version
145
+ ```
146
+
147
+ ### Version strategy
148
+
149
+ - `0.1.0-alpha.X` — internal development (current)
150
+ - `0.1.0-beta.X` — user testing
151
+ - `1.0.0` — production launch
152
+
153
+ ### Important: workspace dependency handling
154
+
155
+ When publishing to npm, `workspace:*` dependencies are automatically rewritten to `@mmapp/*` equivalents by the publish script. The internal `@mindmatrix/player-core` becomes `@mmapp/player-core` in the published tarball.
@@ -649,6 +649,8 @@ var TranspileContext = class {
649
649
  this.localFieldMap = options.localFieldMap ?? /* @__PURE__ */ new Map();
650
650
  this.derivedVarMap = options.derivedVarMap ?? /* @__PURE__ */ new Map();
651
651
  this.setterToFieldMap = options.setterToFieldMap ?? /* @__PURE__ */ new Map();
652
+ this.parameterMap = options.parameterMap ?? /* @__PURE__ */ new Map();
653
+ this.allMutable = options.allMutable ?? false;
652
654
  }
653
655
  /**
654
656
  * Emit an opaque JS fallback, marking the result as impure.
@@ -670,6 +672,8 @@ var TranspileContext = class {
670
672
  if (snakeName) return `$local.${snakeName}`;
671
673
  const derivedInit = this.derivedVarMap.get(node.name);
672
674
  if (derivedInit) return `(${this.visit(derivedInit)})`;
675
+ const paramField = this.parameterMap.get(node.name);
676
+ if (paramField) return paramField;
673
677
  if (node.name === "undefined") return "null";
674
678
  if (node.name === "NaN") return "null";
675
679
  if (node.name === "Infinity") return "null";
@@ -717,6 +721,9 @@ var TranspileContext = class {
717
721
  if (t5.isNewExpression(node)) {
718
722
  return this.visitNew(node);
719
723
  }
724
+ if (t5.isAssignmentExpression(node) && node.operator === "=" && t5.isIdentifier(node.left)) {
725
+ return `${node.left.name} = ${this.visit(node.right)}`;
726
+ }
720
727
  return this.opaque(fallbackGenerate(node));
721
728
  }
722
729
  // ---------------------------------------------------------------------------
@@ -917,6 +924,11 @@ var TranspileContext = class {
917
924
  return this.visitArrayHigherOrder("some", obj, args);
918
925
  case "every":
919
926
  return this.visitArrayHigherOrder("every", obj, args);
927
+ // --- Array push: arr.push(item) → arr = push(arr, item) ---
928
+ case "push": {
929
+ const [item] = this.visitArgsList(args);
930
+ return `${obj} = push(${obj}, ${item})`;
931
+ }
920
932
  // --- Array mutation-free methods ---
921
933
  case "reverse":
922
934
  return `reverse(${obj})`;
@@ -2752,6 +2764,71 @@ function modelStateTypeToIR(type) {
2752
2764
  if (type === "final") return "END";
2753
2765
  return "REGULAR";
2754
2766
  }
2767
+ function parseActionArray(raw, actionCounter) {
2768
+ if (!Array.isArray(raw)) return [];
2769
+ const actions = [];
2770
+ for (const item of raw) {
2771
+ if (typeof item !== "object" || item === null) continue;
2772
+ const a = item;
2773
+ actions.push({
2774
+ id: a.id ? String(a.id) : `auto_${++actionCounter.value}`,
2775
+ type: String(a.type || a.action_type || "unknown"),
2776
+ mode: a.mode || "auto",
2777
+ config: a.config && typeof a.config === "object" ? a.config : {},
2778
+ ...a.condition ? { condition: String(a.condition) } : {}
2779
+ });
2780
+ }
2781
+ return actions;
2782
+ }
2783
+ function parseDuringArray(raw, actionCounter) {
2784
+ if (!Array.isArray(raw)) return [];
2785
+ const result = [];
2786
+ for (const item of raw) {
2787
+ if (typeof item !== "object" || item === null) continue;
2788
+ const d = item;
2789
+ result.push({
2790
+ id: d.id ? String(d.id) : `during_${++actionCounter.value}`,
2791
+ type: d.type || "interval",
2792
+ ...d.interval_ms != null ? { interval_ms: Number(d.interval_ms) } : {},
2793
+ ...d.cron ? { cron: String(d.cron) } : {},
2794
+ ...d.delay_ms != null ? { delay_ms: Number(d.delay_ms) } : {},
2795
+ ...d.condition ? { condition: String(d.condition) } : {},
2796
+ actions: parseActionArray(d.actions, actionCounter)
2797
+ });
2798
+ }
2799
+ return result;
2800
+ }
2801
+ function parseOnEventArray(raw, actionCounter) {
2802
+ if (!Array.isArray(raw)) return [];
2803
+ const result = [];
2804
+ for (const item of raw) {
2805
+ if (typeof item !== "object" || item === null) continue;
2806
+ const e = item;
2807
+ const actions = [];
2808
+ if (Array.isArray(e.actions)) {
2809
+ for (const a of e.actions) {
2810
+ if (typeof a !== "object" || a === null) continue;
2811
+ const act = a;
2812
+ actions.push({
2813
+ type: act.type || "set_field",
2814
+ ...act.field ? { field: String(act.field) } : {},
2815
+ ...act.expression ? { expression: String(act.expression) } : {},
2816
+ ...act.key ? { key: String(act.key) } : {},
2817
+ ...act.message ? { message: String(act.message) } : {},
2818
+ ...act.config && typeof act.config === "object" ? { config: act.config } : {},
2819
+ ...act.conditions && Array.isArray(act.conditions) ? { conditions: act.conditions.map(String) } : {}
2820
+ });
2821
+ }
2822
+ }
2823
+ result.push({
2824
+ match: String(e.match || ""),
2825
+ ...e.description ? { description: String(e.description) } : {},
2826
+ ...e.conditions && Array.isArray(e.conditions) ? { conditions: e.conditions.map(String) } : {},
2827
+ actions
2828
+ });
2829
+ }
2830
+ return result;
2831
+ }
2755
2832
  function extractDefineModelCall(programPath, compilerState, actionCounter) {
2756
2833
  for (const node of programPath.node.body) {
2757
2834
  if (!t11.isExportDefaultDeclaration(node)) continue;
@@ -2791,17 +2868,27 @@ function extractDefineModelCall(programPath, compilerState, actionCounter) {
2791
2868
  const statesObj = config.states;
2792
2869
  for (const [name, stateConfig] of Object.entries(statesObj)) {
2793
2870
  const stateType = modelStateTypeToIR(stateConfig.type);
2871
+ const on_enter = parseActionArray(stateConfig.on_enter, actionCounter);
2872
+ const on_exit = parseActionArray(stateConfig.on_exit, actionCounter);
2873
+ const during = parseDuringArray(stateConfig.during, actionCounter);
2874
+ const on_event = parseOnEventArray(stateConfig.on_event, actionCounter);
2794
2875
  if (!compilerState.states.has(name)) {
2795
2876
  compilerState.states.set(name, {
2796
2877
  name,
2797
2878
  type: stateType,
2798
2879
  description: stateConfig.description,
2799
- on_enter: [],
2800
- during: [],
2801
- on_exit: []
2880
+ on_enter,
2881
+ during,
2882
+ on_exit,
2883
+ ...on_event.length > 0 ? { on_event } : {}
2802
2884
  });
2803
2885
  } else {
2804
- compilerState.states.get(name).type = stateType;
2886
+ const existing = compilerState.states.get(name);
2887
+ existing.type = stateType;
2888
+ if (on_enter.length > 0) existing.on_enter = on_enter;
2889
+ if (on_exit.length > 0) existing.on_exit = on_exit;
2890
+ if (during.length > 0) existing.during = during;
2891
+ if (on_event.length > 0) existing.on_event = on_event;
2805
2892
  }
2806
2893
  }
2807
2894
  }
@@ -2839,13 +2926,21 @@ function extractDefineModelCall(programPath, compilerState, actionCounter) {
2839
2926
  if (transConfig.guard) {
2840
2927
  conditions.push(parseGuardExpression(String(transConfig.guard)));
2841
2928
  }
2929
+ if (transConfig.conditions && Array.isArray(transConfig.conditions)) {
2930
+ for (const cond of transConfig.conditions) {
2931
+ if (typeof cond === "object" && cond !== null) {
2932
+ conditions.push(cond);
2933
+ }
2934
+ }
2935
+ }
2936
+ const actions = parseActionArray(transConfig.actions, actionCounter);
2842
2937
  compilerState.transitions.push({
2843
2938
  name,
2844
2939
  from,
2845
2940
  to,
2846
2941
  description: transConfig.description,
2847
2942
  conditions: conditions.length > 0 ? conditions : void 0,
2848
- actions: [],
2943
+ actions,
2849
2944
  roles: transConfig.roles,
2850
2945
  auto: transConfig.auto,
2851
2946
  required_fields: transConfig.required_fields
@@ -3771,6 +3866,18 @@ function emitIR(extracted) {
3771
3866
  });
3772
3867
  stateNames.add(transition.to);
3773
3868
  }
3869
+ for (const from of transition.from) {
3870
+ if (from && !stateNames.has(from)) {
3871
+ stateArray.push({
3872
+ name: from,
3873
+ type: "REGULAR",
3874
+ on_enter: [],
3875
+ during: [],
3876
+ on_exit: []
3877
+ });
3878
+ stateNames.add(from);
3879
+ }
3880
+ }
3774
3881
  }
3775
3882
  const fieldNames = new Set(fields.map((f) => f.name));
3776
3883
  let normalizedView;
@@ -4723,7 +4830,7 @@ function createVisitor(options = {}) {
4723
4830
  }
4724
4831
  }
4725
4832
  if (mode !== "strict") return;
4726
- if (source.startsWith("@mindmatrix/") || source === "react" || source.startsWith("react/") || source.startsWith(".") || source.startsWith("/")) {
4833
+ if (source.startsWith("@mindmatrix/") || source.startsWith("@mmapp/") || source === "react" || source.startsWith("react/") || source.startsWith(".") || source.startsWith("/")) {
4727
4834
  return;
4728
4835
  }
4729
4836
  const error = {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  babelPlugin
3
- } from "../chunk-HLRGCCIL.mjs";
4
- import "../chunk-Y6FXYEAI.mjs";
3
+ } from "../chunk-FYT47UBU.mjs";
4
+ import "../chunk-CIESM3BP.mjs";
5
5
  export {
6
6
  babelPlugin as default
7
7
  };