@orcalang/orca-lang 0.1.0

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 (183) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +128 -0
  3. package/dist/auth/index.d.ts +6 -0
  4. package/dist/auth/index.d.ts.map +1 -0
  5. package/dist/auth/index.js +6 -0
  6. package/dist/auth/index.js.map +1 -0
  7. package/dist/auth/lock.d.ts +2 -0
  8. package/dist/auth/lock.d.ts.map +1 -0
  9. package/dist/auth/lock.js +59 -0
  10. package/dist/auth/lock.js.map +1 -0
  11. package/dist/auth/providers/anthropic.d.ts +14 -0
  12. package/dist/auth/providers/anthropic.d.ts.map +1 -0
  13. package/dist/auth/providers/anthropic.js +145 -0
  14. package/dist/auth/providers/anthropic.js.map +1 -0
  15. package/dist/auth/providers/index.d.ts +3 -0
  16. package/dist/auth/providers/index.d.ts.map +1 -0
  17. package/dist/auth/providers/index.js +3 -0
  18. package/dist/auth/providers/index.js.map +1 -0
  19. package/dist/auth/providers/minimax.d.ts +6 -0
  20. package/dist/auth/providers/minimax.d.ts.map +1 -0
  21. package/dist/auth/providers/minimax.js +65 -0
  22. package/dist/auth/providers/minimax.js.map +1 -0
  23. package/dist/auth/refresh.d.ts +8 -0
  24. package/dist/auth/refresh.d.ts.map +1 -0
  25. package/dist/auth/refresh.js +104 -0
  26. package/dist/auth/refresh.js.map +1 -0
  27. package/dist/auth/store.d.ts +11 -0
  28. package/dist/auth/store.d.ts.map +1 -0
  29. package/dist/auth/store.js +63 -0
  30. package/dist/auth/store.js.map +1 -0
  31. package/dist/auth/types.d.ts +51 -0
  32. package/dist/auth/types.d.ts.map +1 -0
  33. package/dist/auth/types.js +2 -0
  34. package/dist/auth/types.js.map +1 -0
  35. package/dist/compiler/mermaid.d.ts +3 -0
  36. package/dist/compiler/mermaid.d.ts.map +1 -0
  37. package/dist/compiler/mermaid.js +86 -0
  38. package/dist/compiler/mermaid.js.map +1 -0
  39. package/dist/compiler/xstate.d.ts +15 -0
  40. package/dist/compiler/xstate.d.ts.map +1 -0
  41. package/dist/compiler/xstate.js +542 -0
  42. package/dist/compiler/xstate.js.map +1 -0
  43. package/dist/config/index.d.ts +3 -0
  44. package/dist/config/index.d.ts.map +1 -0
  45. package/dist/config/index.js +3 -0
  46. package/dist/config/index.js.map +1 -0
  47. package/dist/config/loader.d.ts +4 -0
  48. package/dist/config/loader.d.ts.map +1 -0
  49. package/dist/config/loader.js +109 -0
  50. package/dist/config/loader.js.map +1 -0
  51. package/dist/config/types.d.ts +13 -0
  52. package/dist/config/types.d.ts.map +1 -0
  53. package/dist/config/types.js +8 -0
  54. package/dist/config/types.js.map +1 -0
  55. package/dist/generators/index.d.ts +5 -0
  56. package/dist/generators/index.d.ts.map +1 -0
  57. package/dist/generators/index.js +5 -0
  58. package/dist/generators/index.js.map +1 -0
  59. package/dist/generators/registry.d.ts +12 -0
  60. package/dist/generators/registry.d.ts.map +1 -0
  61. package/dist/generators/registry.js +15 -0
  62. package/dist/generators/registry.js.map +1 -0
  63. package/dist/generators/typescript.d.ts +9 -0
  64. package/dist/generators/typescript.d.ts.map +1 -0
  65. package/dist/generators/typescript.js +55 -0
  66. package/dist/generators/typescript.js.map +1 -0
  67. package/dist/index.d.ts +10 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +630 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/llm/anthropic.d.ts +14 -0
  72. package/dist/llm/anthropic.d.ts.map +1 -0
  73. package/dist/llm/anthropic.js +87 -0
  74. package/dist/llm/anthropic.js.map +1 -0
  75. package/dist/llm/grok.d.ts +13 -0
  76. package/dist/llm/grok.d.ts.map +1 -0
  77. package/dist/llm/grok.js +60 -0
  78. package/dist/llm/grok.js.map +1 -0
  79. package/dist/llm/index.d.ts +11 -0
  80. package/dist/llm/index.d.ts.map +1 -0
  81. package/dist/llm/index.js +23 -0
  82. package/dist/llm/index.js.map +1 -0
  83. package/dist/llm/ollama.d.ts +11 -0
  84. package/dist/llm/ollama.d.ts.map +1 -0
  85. package/dist/llm/ollama.js +51 -0
  86. package/dist/llm/ollama.js.map +1 -0
  87. package/dist/llm/openai.d.ts +13 -0
  88. package/dist/llm/openai.d.ts.map +1 -0
  89. package/dist/llm/openai.js +61 -0
  90. package/dist/llm/openai.js.map +1 -0
  91. package/dist/llm/provider.d.ts +32 -0
  92. package/dist/llm/provider.d.ts.map +1 -0
  93. package/dist/llm/provider.js +2 -0
  94. package/dist/llm/provider.js.map +1 -0
  95. package/dist/parser/ast-to-markdown.d.ts +3 -0
  96. package/dist/parser/ast-to-markdown.d.ts.map +1 -0
  97. package/dist/parser/ast-to-markdown.js +209 -0
  98. package/dist/parser/ast-to-markdown.js.map +1 -0
  99. package/dist/parser/ast.d.ts +183 -0
  100. package/dist/parser/ast.d.ts.map +1 -0
  101. package/dist/parser/ast.js +3 -0
  102. package/dist/parser/ast.js.map +1 -0
  103. package/dist/parser/markdown-parser.d.ts +8 -0
  104. package/dist/parser/markdown-parser.d.ts.map +1 -0
  105. package/dist/parser/markdown-parser.js +838 -0
  106. package/dist/parser/markdown-parser.js.map +1 -0
  107. package/dist/runtime/effects.d.ts +17 -0
  108. package/dist/runtime/effects.d.ts.map +1 -0
  109. package/dist/runtime/effects.js +28 -0
  110. package/dist/runtime/effects.js.map +1 -0
  111. package/dist/runtime/machine.d.ts +8 -0
  112. package/dist/runtime/machine.d.ts.map +1 -0
  113. package/dist/runtime/machine.js +158 -0
  114. package/dist/runtime/machine.js.map +1 -0
  115. package/dist/runtime/types.d.ts +37 -0
  116. package/dist/runtime/types.d.ts.map +1 -0
  117. package/dist/runtime/types.js +3 -0
  118. package/dist/runtime/types.js.map +1 -0
  119. package/dist/skills.d.ts +114 -0
  120. package/dist/skills.d.ts.map +1 -0
  121. package/dist/skills.js +1103 -0
  122. package/dist/skills.js.map +1 -0
  123. package/dist/tools.d.ts +18 -0
  124. package/dist/tools.d.ts.map +1 -0
  125. package/dist/tools.js +124 -0
  126. package/dist/tools.js.map +1 -0
  127. package/dist/verifier/completeness.d.ts +4 -0
  128. package/dist/verifier/completeness.d.ts.map +1 -0
  129. package/dist/verifier/completeness.js +82 -0
  130. package/dist/verifier/completeness.js.map +1 -0
  131. package/dist/verifier/determinism.d.ts +17 -0
  132. package/dist/verifier/determinism.d.ts.map +1 -0
  133. package/dist/verifier/determinism.js +301 -0
  134. package/dist/verifier/determinism.js.map +1 -0
  135. package/dist/verifier/properties.d.ts +6 -0
  136. package/dist/verifier/properties.d.ts.map +1 -0
  137. package/dist/verifier/properties.js +404 -0
  138. package/dist/verifier/properties.js.map +1 -0
  139. package/dist/verifier/structural.d.ts +50 -0
  140. package/dist/verifier/structural.d.ts.map +1 -0
  141. package/dist/verifier/structural.js +692 -0
  142. package/dist/verifier/structural.js.map +1 -0
  143. package/dist/verifier/types.d.ts +40 -0
  144. package/dist/verifier/types.d.ts.map +1 -0
  145. package/dist/verifier/types.js +2 -0
  146. package/dist/verifier/types.js.map +1 -0
  147. package/package.json +49 -0
  148. package/src/auth/index.ts +5 -0
  149. package/src/auth/lock.ts +71 -0
  150. package/src/auth/providers/anthropic.ts +192 -0
  151. package/src/auth/providers/index.ts +17 -0
  152. package/src/auth/providers/minimax.ts +100 -0
  153. package/src/auth/refresh.ts +138 -0
  154. package/src/auth/store.ts +75 -0
  155. package/src/auth/types.ts +62 -0
  156. package/src/compiler/mermaid.ts +109 -0
  157. package/src/compiler/xstate.ts +615 -0
  158. package/src/config/index.ts +2 -0
  159. package/src/config/loader.ts +122 -0
  160. package/src/config/types.ts +21 -0
  161. package/src/generators/index.ts +6 -0
  162. package/src/generators/registry.ts +27 -0
  163. package/src/generators/typescript.ts +67 -0
  164. package/src/index.ts +671 -0
  165. package/src/llm/anthropic.ts +102 -0
  166. package/src/llm/grok.ts +73 -0
  167. package/src/llm/index.ts +29 -0
  168. package/src/llm/ollama.ts +62 -0
  169. package/src/llm/openai.ts +74 -0
  170. package/src/llm/provider.ts +35 -0
  171. package/src/parser/ast-to-markdown.ts +220 -0
  172. package/src/parser/ast.ts +236 -0
  173. package/src/parser/markdown-parser.ts +844 -0
  174. package/src/runtime/effects.ts +48 -0
  175. package/src/runtime/machine.ts +201 -0
  176. package/src/runtime/types.ts +44 -0
  177. package/src/skills.ts +1339 -0
  178. package/src/tools.ts +144 -0
  179. package/src/verifier/completeness.ts +89 -0
  180. package/src/verifier/determinism.ts +328 -0
  181. package/src/verifier/properties.ts +507 -0
  182. package/src/verifier/structural.ts +803 -0
  183. package/src/verifier/types.ts +45 -0
@@ -0,0 +1,615 @@
1
+ import { MachineDef, Transition, ActionSignature, StateDef } from '../parser/ast.js';
2
+ import { createMachine, assign } from 'xstate';
3
+
4
+ // Helper to find a state with a specific action (searches nested states)
5
+ function findStateByAction(machine: MachineDef, actionName: string): StateDef | undefined {
6
+ for (const state of machine.states) {
7
+ const found = findStateRecursively(state, actionName);
8
+ if (found) return found;
9
+ }
10
+ return undefined;
11
+ }
12
+
13
+ function findStateRecursively(state: StateDef, actionName: string): StateDef | undefined {
14
+ if (state.onEntry === actionName || state.onExit === actionName) {
15
+ return state;
16
+ }
17
+ if (state.contains) {
18
+ for (const child of state.contains) {
19
+ const found = findStateRecursively(child, actionName);
20
+ if (found) return found;
21
+ }
22
+ }
23
+ if (state.parallel) {
24
+ for (const region of state.parallel.regions) {
25
+ for (const child of region.states) {
26
+ const found = findStateRecursively(child, actionName);
27
+ if (found) return found;
28
+ }
29
+ }
30
+ }
31
+ return undefined;
32
+ }
33
+
34
+ export interface CompiledMachine {
35
+ config: any; // XState MachineConfig type
36
+ effectMeta: {
37
+ effectfulActions: Array<{
38
+ name: string;
39
+ effectType: string;
40
+ state: string;
41
+ transition?: string;
42
+ }>;
43
+ };
44
+ }
45
+
46
+ export function compileToXStateMachine(machine: MachineDef): CompiledMachine {
47
+ const effectMeta = {
48
+ effectfulActions: findEffectfulActions(machine),
49
+ };
50
+
51
+ const config: any = {
52
+ id: machine.name,
53
+ types: {
54
+ context: {} as any,
55
+ events: {} as any,
56
+ },
57
+ context: buildContext(machine),
58
+ initial: getInitialState(machine),
59
+ states: buildStates(machine),
60
+ };
61
+
62
+ return { config, effectMeta };
63
+ }
64
+
65
+ function findEffectfulActions(machine: MachineDef) {
66
+ const effectful: CompiledMachine['effectMeta']['effectfulActions'] = [];
67
+
68
+ for (const action of machine.actions) {
69
+ if (action.hasEffect && action.effectType) {
70
+ // Find which state uses this action (search all states including nested)
71
+ const stateWithAction = findStateByAction(machine, action.name);
72
+ if (stateWithAction) {
73
+ effectful.push({ name: action.name, effectType: action.effectType, state: stateWithAction.name });
74
+ }
75
+ // Find transitions using this action
76
+ for (const t of machine.transitions) {
77
+ if (t.action === action.name) {
78
+ effectful.push({ name: action.name, effectType: action.effectType, state: t.source, transition: t.target });
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ return effectful;
85
+ }
86
+
87
+ function buildContext(machine: MachineDef): Record<string, unknown> {
88
+ const ctx: Record<string, unknown> = {};
89
+ for (const field of machine.context) {
90
+ ctx[field.name] = field.defaultValue !== undefined
91
+ ? field.defaultValue
92
+ : getDefaultForType(field);
93
+ }
94
+ return ctx;
95
+ }
96
+
97
+ function buildStates(machine: MachineDef): Record<string, any> {
98
+ const states: Record<string, any> = {};
99
+
100
+ for (const state of machine.states) {
101
+ states[state.name] = buildStateConfig(state, machine, machine.states);
102
+ }
103
+
104
+ return states;
105
+ }
106
+
107
+ function buildStateConfig(state: StateDef, machine: MachineDef, allStates: StateDef[]): any {
108
+ const config: any = {};
109
+
110
+ if (state.description) {
111
+ config.description = state.description;
112
+ }
113
+
114
+ // Check if this is a compound state (has nested states)
115
+ if (state.contains && state.contains.length > 0) {
116
+ // Compound state with nested states
117
+ const initialChild = state.contains.find(s => s.isInitial) || state.contains[0];
118
+ config.initial = initialChild.name;
119
+ config.states = {};
120
+
121
+ // Handle transitions for compound states (BEFORE recursive calls)
122
+ // These transitions fire from any child state via XState event bubbling
123
+ const thisStateTransitions = machine.transitions.filter(t => t.source === state.name);
124
+ if (thisStateTransitions.length > 0) {
125
+ config.on = buildTransitions(thisStateTransitions, machine);
126
+ }
127
+
128
+ for (const child of state.contains) {
129
+ config.states[child.name] = buildStateConfig(child, machine, state.contains);
130
+ }
131
+
132
+ // Compound states don't use type: 'initial' or 'final' - they use initial + nested states
133
+ return config;
134
+ }
135
+
136
+ // Check if this is a parallel state (has parallel regions)
137
+ if (state.parallel) {
138
+ config.type = 'parallel';
139
+ config.states = {};
140
+
141
+ for (const region of state.parallel.regions) {
142
+ const regionConfig: any = {};
143
+ const initialChild = region.states.find(s => s.isInitial) || region.states[0];
144
+ regionConfig.initial = initialChild.name;
145
+ regionConfig.states = {};
146
+
147
+ for (const child of region.states) {
148
+ regionConfig.states[child.name] = buildStateConfig(child, machine, region.states);
149
+ }
150
+
151
+ config.states[region.name] = regionConfig;
152
+ }
153
+
154
+ // onDone for synchronization (all-final is the XState default)
155
+ if (state.onDone) {
156
+ config.onDone = { target: state.onDone };
157
+ }
158
+
159
+ // Parent-level transitions (event bubbling to all regions)
160
+ const thisStateTransitions = machine.transitions.filter(t => t.source === state.name);
161
+ if (thisStateTransitions.length > 0) {
162
+ config.on = buildTransitions(thisStateTransitions, machine);
163
+ }
164
+
165
+ return config;
166
+ }
167
+
168
+ // Leaf state configuration
169
+ // Note: initial state is designated by the parent's `initial` property, not by type
170
+ if (state.isFinal) {
171
+ config.type = 'final';
172
+ }
173
+ if (state.onEntry) {
174
+ const action = machine.actions.find(a => a.name === state.onEntry);
175
+ if (action?.hasEffect) {
176
+ // Effectful action - use invoke at state level to run the effect
177
+ // Don't set entry - the invoke replaces the entry action
178
+ config.invoke = buildEffectInvoke(state.onEntry, action, machine);
179
+ } else {
180
+ config.entry = state.onEntry;
181
+ }
182
+ }
183
+ if (state.onExit) {
184
+ config.exit = state.onExit;
185
+ }
186
+
187
+ // Handle machine invocation
188
+ if (state.invoke) {
189
+ config.invoke = buildMachineInvoke(state.invoke);
190
+ }
191
+
192
+ // Handle transitions - only use this state's transitions
193
+ // (transitions on compound states fire from any child via XState's event bubbling)
194
+ const thisStateTransitions = machine.transitions.filter(t => t.source === state.name);
195
+
196
+ if (thisStateTransitions.length > 0) {
197
+ config.on = buildTransitions(thisStateTransitions, machine);
198
+ }
199
+
200
+ // Handle timeout
201
+ if (state.timeout) {
202
+ config.after = {
203
+ [state.timeout.duration]: { target: state.timeout.target },
204
+ };
205
+ }
206
+
207
+ return config;
208
+ }
209
+
210
+ // Get transitions from parent compound states (these fire from any child state)
211
+ function getParentTransitions(state: StateDef, machine: MachineDef, allStates: StateDef[]): Transition[] {
212
+ if (!state.parent) return [];
213
+
214
+ // Find parent state
215
+ const parent = findStateByName(allStates, state.parent);
216
+ if (!parent) return [];
217
+
218
+ // Get parent's transitions
219
+ const parentTransitions = machine.transitions.filter(t => t.source === parent.name);
220
+
221
+ // Recursively get grandparent transitions
222
+ const grandparentTransitions = getParentTransitions(parent, machine, allStates);
223
+
224
+ return [...parentTransitions, ...grandparentTransitions];
225
+ }
226
+
227
+ // Find a state by name in a flat list of states (including nested and parallel regions)
228
+ function findStateByName(states: StateDef[], name: string): StateDef | undefined {
229
+ for (const state of states) {
230
+ if (state.name === name) return state;
231
+ if (state.contains) {
232
+ const found = findStateByName(state.contains, name);
233
+ if (found) return found;
234
+ }
235
+ if (state.parallel) {
236
+ for (const region of state.parallel.regions) {
237
+ const found = findStateByName(region.states, name);
238
+ if (found) return found;
239
+ }
240
+ }
241
+ }
242
+ return undefined;
243
+ }
244
+
245
+ function buildEffectInvoke(actionName: string, action: ActionSignature, machine: MachineDef): any {
246
+ // Find the state that has this action as entry or exit
247
+ // Search all states including nested ones
248
+ const stateWithAction = findStateByAction(machine, actionName);
249
+ const stateName = stateWithAction?.name;
250
+ let doneTarget: string | undefined;
251
+
252
+ // Find transitions from this state to determine the completion target.
253
+ // When an effect is invoked, the machine waits for the effect to complete.
254
+ // The completion event is derived from transitions that exit this state.
255
+ if (stateName) {
256
+ // Look for transitions that exit this state (these handle effect completion)
257
+ const exitTransitions = machine.transitions.filter(t => t.source === stateName);
258
+ if (exitTransitions.length > 0) {
259
+ // Use the first exit transition's target as the done target.
260
+ // This assumes effects have a simple completion path.
261
+ doneTarget = exitTransitions[0].target;
262
+ }
263
+ }
264
+
265
+ const effectType = action.effectType || 'Effect';
266
+
267
+ // Build the input expression - it will be used in the fromPromise
268
+ const inputExpr = ({ context, event }: { context: any; event: any }) => ({ context, event, action: actionName });
269
+
270
+ // Find an error/failed state if one exists
271
+ const errorState = machine.states.find(s => s.name === 'error')
272
+ || machine.states.find(s => s.name === 'failed');
273
+ const errorTarget = errorState?.name;
274
+
275
+ // Return the invoke config directly, not wrapped in another object.
276
+ const invokeConfig: any = {
277
+ src: `__effect__:${effectType}`,
278
+ input: inputExpr,
279
+ onDone: doneTarget ? {
280
+ target: doneTarget,
281
+ actions: assign({
282
+ _effectResult: ({ event }: any) => event.output,
283
+ }),
284
+ } : {
285
+ actions: assign({
286
+ _effectResult: ({ event }: any) => event.output,
287
+ }),
288
+ },
289
+ onError: {
290
+ actions: assign({
291
+ _effectError: ({ event }: any) => event.error?.message,
292
+ }),
293
+ },
294
+ };
295
+
296
+ if (errorTarget) {
297
+ invokeConfig.onError.target = errorTarget;
298
+ }
299
+
300
+ return invokeConfig;
301
+ }
302
+
303
+ function buildMachineInvoke(invokeDef: { machine: string; input?: Record<string, string>; onDone?: string; onError?: string }): any {
304
+ // Build input expression from input mapping
305
+ // input is like { id: "ctx.order_id" } -> { id: context.order_id }
306
+ const inputExpr = ({ context, event }: { context: any; event: any }) => {
307
+ const input: Record<string, unknown> = {};
308
+ if (invokeDef.input) {
309
+ for (const [key, value] of Object.entries(invokeDef.input)) {
310
+ // value is like "ctx.order_id" - extract the field name
311
+ const fieldName = value.replace(/^ctx\./, '');
312
+ input[key] = context[fieldName];
313
+ }
314
+ }
315
+ return input;
316
+ };
317
+
318
+ const invokeConfig: any = {
319
+ src: `__machine__:${invokeDef.machine}`,
320
+ input: inputExpr,
321
+ };
322
+
323
+ if (invokeDef.onDone) {
324
+ invokeConfig.onDone = { target: invokeDef.onDone };
325
+ }
326
+
327
+ if (invokeDef.onError) {
328
+ invokeConfig.onError = { target: invokeDef.onError };
329
+ }
330
+
331
+ return invokeConfig;
332
+ }
333
+
334
+ function buildTransitions(transitions: Transition[], machine: MachineDef): Record<string, any> {
335
+ const on: Record<string, any> = {};
336
+ const eventGroups = groupByEvent(transitions);
337
+
338
+ for (const [eventName, trans] of Object.entries(eventGroups)) {
339
+ if (trans.length === 1 && !trans[0].guard) {
340
+ // Single unguarded transition
341
+ const t = trans[0];
342
+ const action = machine.actions.find(a => a.name === t.action);
343
+
344
+ if (action?.hasEffect) {
345
+ // Effectful transition
346
+ on[eventName] = {
347
+ target: t.target,
348
+ actions: {
349
+ type: 'effectful',
350
+ name: t.action,
351
+ effectType: action.effectType,
352
+ },
353
+ };
354
+ } else {
355
+ on[eventName] = {
356
+ target: t.target,
357
+ actions: t.action || undefined,
358
+ };
359
+ }
360
+ } else {
361
+ // Multiple transitions or guarded - use array format
362
+ on[eventName] = trans.map(t => {
363
+ const action = machine.actions.find(a => a.name === t.action);
364
+ const target = t.target;
365
+
366
+ const transition: any = { target };
367
+ if (t.guard) {
368
+ const guardName = t.guard.negated ? `!${t.guard.name}` : t.guard.name;
369
+ transition.guard = { type: guardName };
370
+ }
371
+ if (t.action) {
372
+ transition.actions = t.action;
373
+ }
374
+
375
+ return transition;
376
+ });
377
+ }
378
+ }
379
+
380
+ return on;
381
+ }
382
+
383
+ export function compileToXState(machine: MachineDef): string {
384
+ const lines: string[] = [];
385
+
386
+ lines.push(`import { createMachine, assign } from 'xstate';`);
387
+ lines.push(``);
388
+ lines.push(`export const ${machine.name}Machine = createMachine({`);
389
+ lines.push(` id: '${machine.name}',`);
390
+ lines.push(` types: {} as {`);
391
+ lines.push(` context: {`);
392
+ for (const field of machine.context) {
393
+ lines.push(` ${field.name}: ${typeToTs(field)},`);
394
+ }
395
+ lines.push(` },`);
396
+ lines.push(` events: |`);
397
+ for (let i = 0; i < machine.events.length; i++) {
398
+ const event = machine.events[i];
399
+ lines.push(` | { type: '${event.name}' }${i < machine.events.length - 1 ? '' : ''}`);
400
+ }
401
+ lines.push(` },`);
402
+ lines.push(` context: {`);
403
+ for (const field of machine.context) {
404
+ const defaultVal = field.defaultValue || getDefaultForType(field);
405
+ lines.push(` ${field.name}: ${defaultVal},`);
406
+ }
407
+ lines.push(` },`);
408
+ lines.push(` initial: '${getInitialState(machine)}',`);
409
+ lines.push(` states: {`);
410
+
411
+ for (let i = 0; i < machine.states.length; i++) {
412
+ const state = machine.states[i];
413
+ lines.push(` ${state.name}: {`);
414
+ if (state.description) {
415
+ lines.push(` description: '${escapeString(state.description)}',`);
416
+ }
417
+
418
+ // Check if this is a compound state (has nested states)
419
+ if (state.contains && state.contains.length > 0) {
420
+ // Compound state with nested states
421
+ const initialChild = state.contains.find(s => s.isInitial) || state.contains[0];
422
+ lines.push(` initial: '${initialChild.name}',`);
423
+ lines.push(` states: {`);
424
+ for (let j = 0; j < state.contains.length; j++) {
425
+ const child = state.contains[j];
426
+ lines.push(` ${child.name}: {`);
427
+ if (child.description) {
428
+ lines.push(` description: '${escapeString(child.description)}',`);
429
+ }
430
+ if (child.isFinal) {
431
+ lines.push(` type: 'final',`);
432
+ }
433
+ lines.push(` }${j < state.contains.length - 1 ? ',' : ''}`);
434
+ }
435
+ lines.push(` },`);
436
+ lines.push(` }${i < machine.states.length - 1 ? ',' : ''}`);
437
+ continue;
438
+ }
439
+
440
+ // Check if this is a parallel state
441
+ if (state.parallel) {
442
+ lines.push(` type: 'parallel',`);
443
+ lines.push(` states: {`);
444
+ for (let r = 0; r < state.parallel.regions.length; r++) {
445
+ const region = state.parallel.regions[r];
446
+ const initialChild = region.states.find(s => s.isInitial) || region.states[0];
447
+ lines.push(` ${region.name}: {`);
448
+ lines.push(` initial: '${initialChild.name}',`);
449
+ lines.push(` states: {`);
450
+ for (let j = 0; j < region.states.length; j++) {
451
+ const child = region.states[j];
452
+ lines.push(` ${child.name}: {`);
453
+ if (child.description) {
454
+ lines.push(` description: '${escapeString(child.description)}',`);
455
+ }
456
+ if (child.isFinal) {
457
+ lines.push(` type: 'final',`);
458
+ }
459
+ if (child.onEntry) {
460
+ lines.push(` entry: '${child.onEntry}',`);
461
+ }
462
+ if (child.onExit) {
463
+ lines.push(` exit: '${child.onExit}',`);
464
+ }
465
+ lines.push(` }${j < region.states.length - 1 ? ',' : ''}`);
466
+ }
467
+ lines.push(` },`);
468
+ lines.push(` }${r < state.parallel.regions.length - 1 ? ',' : ''}`);
469
+ }
470
+ lines.push(` },`);
471
+ if (state.onDone) {
472
+ lines.push(` onDone: { target: '${state.onDone}' },`);
473
+ }
474
+ lines.push(` }${i < machine.states.length - 1 ? ',' : ''}`);
475
+ continue;
476
+ }
477
+
478
+ // Leaf state configuration
479
+ if (state.isFinal) {
480
+ lines.push(` type: 'final',`);
481
+ }
482
+ if (state.onEntry) {
483
+ lines.push(` entry: '${state.onEntry}',`);
484
+ }
485
+ if (state.onExit) {
486
+ lines.push(` exit: '${state.onExit}',`);
487
+ }
488
+
489
+ // Handle machine invocation
490
+ if (state.invoke) {
491
+ lines.push(` invoke: {`);
492
+ lines.push(` src: '__machine__:${state.invoke.machine}',`);
493
+ if (state.invoke.input) {
494
+ // Build input mapping expression
495
+ const inputPairs: string[] = [];
496
+ for (const [key, value] of Object.entries(state.invoke.input)) {
497
+ const fieldName = value.replace(/^ctx\./, '');
498
+ inputPairs.push(`${key}: context.${fieldName}`);
499
+ }
500
+ lines.push(` input: ({ context }) => ({ ${inputPairs.join(', ')} }),`);
501
+ }
502
+ if (state.invoke.onDone) {
503
+ lines.push(` onDone: { target: '${state.invoke.onDone}' },`);
504
+ }
505
+ if (state.invoke.onError) {
506
+ lines.push(` onError: { target: '${state.invoke.onError}' },`);
507
+ }
508
+ lines.push(` },`);
509
+ }
510
+
511
+ // Collect transitions for this state
512
+ const stateTransitions = machine.transitions.filter(t => t.source === state.name);
513
+
514
+ if (stateTransitions.length > 0) {
515
+ lines.push(` on: {`);
516
+ const eventGroups = groupByEvent(stateTransitions);
517
+ const eventEntries = Object.entries(eventGroups);
518
+ for (let ei = 0; ei < eventEntries.length; ei++) {
519
+ const [eventName, trans] = eventEntries[ei];
520
+ const isArrayFormat = trans.length > 1 || trans.some(t => t.guard);
521
+
522
+ if (isArrayFormat) {
523
+ // Multiple transitions or guarded transitions - use array format
524
+ lines.push(` ${eventName}: [`);
525
+ for (const t of trans) {
526
+ lines.push(` {`);
527
+ const target = t.target;
528
+ lines.push(` target: '${target}',`);
529
+ if (t.guard) {
530
+ const guardName = t.guard.negated ? `!${t.guard.name}` : t.guard.name;
531
+ lines.push(` guard: '${guardName}',`);
532
+ }
533
+ if (t.action) {
534
+ lines.push(` actions: '${t.action}',`);
535
+ }
536
+ lines.push(` },`);
537
+ }
538
+ lines.push(` ],`);
539
+ } else {
540
+ // Single unguarded transition - use object format
541
+ const t = trans[0];
542
+ lines.push(` ${eventName}: {`);
543
+ lines.push(` target: '${t.target}',`);
544
+ if (t.action) {
545
+ lines.push(` actions: '${t.action}',`);
546
+ }
547
+ lines.push(` },`);
548
+ }
549
+ }
550
+ lines.push(` },`);
551
+ }
552
+
553
+ lines.push(` }${i < machine.states.length - 1 ? ',' : ''}`);
554
+ }
555
+
556
+ lines.push(` },`);
557
+ lines.push(`});`);
558
+
559
+ return lines.join('\n');
560
+ }
561
+
562
+ function typeToTs(field: { type: { kind: string; name?: string; elementType?: string; innerType?: string; keyType?: string; valueType?: string } }): string {
563
+ const t = field.type;
564
+ switch (t.kind) {
565
+ case 'string': return 'string';
566
+ case 'int': return 'number';
567
+ case 'decimal': return 'number';
568
+ case 'bool': return 'boolean';
569
+ case 'array': return `${t.elementType}[]`;
570
+ case 'optional': return `${t.innerType} | null`;
571
+ case 'map': return `Record<${t.keyType || 'string'}, ${t.valueType || 'any'}>`;
572
+ case 'custom': return t.name || 'any';
573
+ default: return 'any';
574
+ }
575
+ }
576
+
577
+ function getDefaultForType(field: { type: { kind: string } }): string {
578
+ switch (field.type.kind) {
579
+ case 'string': return "''";
580
+ case 'int':
581
+ case 'decimal': return '0';
582
+ case 'bool': return 'false';
583
+ case 'array': return '[]';
584
+ case 'map': return '{}';
585
+ case 'optional': return 'null';
586
+ default: return 'undefined';
587
+ }
588
+ }
589
+
590
+ function getInitialState(machine: MachineDef): string {
591
+ // Find top-level initial state (not nested)
592
+ const initial = machine.states.find(s => s.isInitial);
593
+ if (initial) {
594
+ // If initial is compound, return its initial child
595
+ if (initial.contains && initial.contains.length > 0) {
596
+ const initialChild = initial.contains.find(s => s.isInitial) || initial.contains[0];
597
+ return initial.name; // XState will enter the compound state and use its initial
598
+ }
599
+ return initial.name;
600
+ }
601
+ return machine.states[0]?.name || 'unknown';
602
+ }
603
+
604
+ function groupByEvent(transitions: Transition[]): Record<string, Transition[]> {
605
+ const groups: Record<string, Transition[]> = {};
606
+ for (const t of transitions) {
607
+ if (!groups[t.event]) groups[t.event] = [];
608
+ groups[t.event].push(t);
609
+ }
610
+ return groups;
611
+ }
612
+
613
+ function escapeString(s: string): string {
614
+ return s.replace(/'/g, "\\'");
615
+ }
@@ -0,0 +1,2 @@
1
+ export * from './types.js';
2
+ export { loadConfig, resolveConfigOverrides } from './loader.js';