@mmapp/react 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 (94) hide show
  1. package/README.md +112 -0
  2. package/dist/index.d.mts +27 -2
  3. package/dist/index.d.ts +27 -2
  4. package/dist/index.js +70 -3
  5. package/dist/index.mjs +74 -12
  6. package/package.json +4 -3
  7. package/package.json.backup +0 -41
  8. package/src/Blueprint.ts +0 -216
  9. package/src/__tests__/Blueprint.test.ts +0 -106
  10. package/src/__tests__/action-context.test.ts +0 -166
  11. package/src/__tests__/actionCreators.test.ts +0 -179
  12. package/src/__tests__/builders.test.ts +0 -336
  13. package/src/__tests__/defineBlueprint-composition.test.ts +0 -106
  14. package/src/__tests__/factories.test.ts +0 -229
  15. package/src/__tests__/loader.test.ts +0 -159
  16. package/src/__tests__/logger.test.ts +0 -70
  17. package/src/__tests__/type-inference.test.ts +0 -160
  18. package/src/__tests__/typed-transitions.test.ts +0 -126
  19. package/src/__tests__/useModuleConfig.test.ts +0 -61
  20. package/src/actionCreators.ts +0 -132
  21. package/src/actions.ts +0 -547
  22. package/src/atoms/index.ts +0 -600
  23. package/src/authoring.ts +0 -92
  24. package/src/browser-player.ts +0 -783
  25. package/src/builders.ts +0 -1342
  26. package/src/components/ExperienceWorkflowBridge.tsx +0 -123
  27. package/src/components/PlayerProvider.tsx +0 -43
  28. package/src/components/atoms/index.tsx +0 -269
  29. package/src/components/index.ts +0 -36
  30. package/src/conditions.ts +0 -692
  31. package/src/config/defineBlueprint.ts +0 -329
  32. package/src/config/defineModel.ts +0 -753
  33. package/src/config/defineWorkspace.ts +0 -24
  34. package/src/core/WorkflowRuntime.ts +0 -153
  35. package/src/factories.ts +0 -425
  36. package/src/grammar/index.ts +0 -173
  37. package/src/hooks/index.ts +0 -106
  38. package/src/hooks/useAuth.ts +0 -288
  39. package/src/hooks/useChannel.ts +0 -304
  40. package/src/hooks/useComputed.ts +0 -154
  41. package/src/hooks/useDomainSubscription.ts +0 -110
  42. package/src/hooks/useDuringAction.ts +0 -99
  43. package/src/hooks/useExperienceState.ts +0 -59
  44. package/src/hooks/useExpressionLibrary.ts +0 -129
  45. package/src/hooks/useForm.ts +0 -352
  46. package/src/hooks/useGeolocation.ts +0 -207
  47. package/src/hooks/useMapView.ts +0 -259
  48. package/src/hooks/useMiddleware.ts +0 -291
  49. package/src/hooks/useModel.ts +0 -363
  50. package/src/hooks/useModule.ts +0 -59
  51. package/src/hooks/useModuleConfig.ts +0 -61
  52. package/src/hooks/useMutation.ts +0 -237
  53. package/src/hooks/useNotification.ts +0 -151
  54. package/src/hooks/useOnChange.ts +0 -30
  55. package/src/hooks/useOnEnter.ts +0 -59
  56. package/src/hooks/useOnEvent.ts +0 -37
  57. package/src/hooks/useOnExit.ts +0 -27
  58. package/src/hooks/useOnTransition.ts +0 -30
  59. package/src/hooks/usePackage.ts +0 -128
  60. package/src/hooks/useParams.ts +0 -33
  61. package/src/hooks/usePlayer.ts +0 -308
  62. package/src/hooks/useQuery.ts +0 -184
  63. package/src/hooks/useRealtimeQuery.ts +0 -222
  64. package/src/hooks/useRole.ts +0 -191
  65. package/src/hooks/useRouteParams.ts +0 -100
  66. package/src/hooks/useRouter.ts +0 -347
  67. package/src/hooks/useServerAction.ts +0 -178
  68. package/src/hooks/useServerState.ts +0 -284
  69. package/src/hooks/useToast.ts +0 -164
  70. package/src/hooks/useTransition.ts +0 -39
  71. package/src/hooks/useView.ts +0 -102
  72. package/src/hooks/useWhileIn.ts +0 -48
  73. package/src/hooks/useWorkflow.ts +0 -63
  74. package/src/index.ts +0 -465
  75. package/src/loader/experience-workflow-loader.ts +0 -192
  76. package/src/loader/index.ts +0 -6
  77. package/src/local/LocalEngine.ts +0 -388
  78. package/src/local/LocalEngineAdapter.ts +0 -175
  79. package/src/local/LocalEngineContext.ts +0 -30
  80. package/src/logger.ts +0 -37
  81. package/src/mixins.ts +0 -1160
  82. package/src/providers/RuntimeContext.ts +0 -20
  83. package/src/providers/WorkflowProvider.tsx +0 -28
  84. package/src/routing/instance-key.ts +0 -107
  85. package/src/server/transition-context.ts +0 -172
  86. package/src/testing/index.ts +0 -9
  87. package/src/testing/useBlueprintTestRunner.ts +0 -91
  88. package/src/testing/useGraphAnalysis.ts +0 -18
  89. package/src/testing/useTestRunner.ts +0 -77
  90. package/src/testing.ts +0 -995
  91. package/src/types/workflow-inference.ts +0 -158
  92. package/src/types.ts +0 -114
  93. package/tsconfig.json +0 -27
  94. package/vitest.config.ts +0 -8
@@ -1,753 +0,0 @@
1
- /**
2
- * defineModel — declare a workflow model with preserved literal types.
3
- *
4
- * Used in model files (models/*.ts) to define workflow definitions
5
- * with full TypeScript type inference. The `const` type parameter (TS 5.0+)
6
- * ensures field names, state names, and transition names are inferred as
7
- * string literals — enabling typed hooks like useModel(), useQuery(), useMutation().
8
- *
9
- * @example
10
- * ```typescript
11
- * import { defineModel } from '@mindmatrix/react';
12
- *
13
- * export default defineModel({
14
- * slug: 'mod-authentication',
15
- * version: '2.0.0',
16
- * category: 'module',
17
- *
18
- * fields: {
19
- * appName: { type: 'string', default: '', label: 'Application Name' },
20
- * layout: { type: 'string', default: 'card', enum: ['card', 'split', 'minimal'] },
21
- * email: { type: 'email', required: true, validation: { maxLength: 255 } },
22
- * role: {
23
- * type: 'string',
24
- * default: 'viewer',
25
- * editableByRoles: ['admin'],
26
- * visibleInStates: ['active', 'suspended'],
27
- * },
28
- * },
29
- *
30
- * states: {
31
- * unauthenticated: { type: 'initial' },
32
- * authenticating: {},
33
- * authenticated: {
34
- * onEnter: [{ id: 'log', type: 'log_event', mode: 'auto', config: { event: 'auth.ok' } }],
35
- * },
36
- * error: {
37
- * timeout: { duration: '5m', fallback: { transition: 'retry' } },
38
- * },
39
- * },
40
- *
41
- * transitions: {
42
- * login: {
43
- * from: 'unauthenticated',
44
- * to: 'authenticating',
45
- * conditions: [
46
- * { type: 'expression', expression: 'input.email != null' },
47
- * ],
48
- * actions: [
49
- * { id: 'auth', type: 'server:authenticate', mode: 'auto', config: { method: 'login' } },
50
- * ],
51
- * },
52
- * logout: {
53
- * from: 'authenticated',
54
- * to: 'unauthenticated',
55
- * roles: ['user', 'admin'],
56
- * },
57
- * },
58
- *
59
- * roles: {
60
- * admin: { description: 'Full access', permissions: ['manage_users', 'manage_settings'] },
61
- * user: { description: 'Standard user', permissions: ['read', 'write'], inherits: ['viewer'] },
62
- * viewer: { permissions: ['read'] },
63
- * },
64
- * });
65
- * ```
66
- */
67
-
68
- // =============================================================================
69
- // Field-Level Access Control
70
- // =============================================================================
71
-
72
- /**
73
- * Controls where a field's data lives and how it syncs.
74
- *
75
- * @example
76
- * ```typescript
77
- * stateHome: { scope: 'instance', persistence: 'durable', sync: 'tenant' }
78
- * ```
79
- */
80
- export interface StateHome {
81
- /** Where the field value lives at runtime. */
82
- scope?: 'ephemeral' | 'route' | 'instance' | 'global';
83
- /** How the field is persisted. */
84
- persistence?: 'none' | 'local' | 'durable';
85
- /** How the field syncs across clients/tenants. */
86
- sync?: 'none' | 'tenant' | 'p2p';
87
- }
88
-
89
- /**
90
- * Validation rules for a field.
91
- *
92
- * @example
93
- * ```typescript
94
- * validation: {
95
- * min: 0,
96
- * max: 100,
97
- * rules: [
98
- * { expression: 'value > state_data.minPrice', message: 'Must exceed minimum price', severity: 'error' },
99
- * ],
100
- * }
101
- * ```
102
- */
103
- export interface FieldValidation {
104
- /** Minimum numeric value. */
105
- min?: number;
106
- /** Maximum numeric value. */
107
- max?: number;
108
- /** Minimum string length. */
109
- minLength?: number;
110
- /** Maximum string length. */
111
- maxLength?: number;
112
- /** Regex pattern the value must match. */
113
- pattern?: string;
114
- /** Allowed values (alternative to field-level `enum`). */
115
- options?: readonly string[];
116
- /**
117
- * Custom validation rules evaluated as expressions.
118
- * Each rule has an expression that must be truthy for the field to be valid.
119
- */
120
- rules?: readonly ValidationRule[];
121
- }
122
-
123
- /** A single expression-based validation rule. */
124
- export interface ValidationRule {
125
- /** Expression that must evaluate to truthy. `value` refers to the field value. */
126
- expression: string;
127
- /** Error message shown when validation fails. */
128
- message: string;
129
- /** Whether this rule blocks saves or just warns. Default: `'error'`. */
130
- severity?: 'error' | 'warning';
131
- }
132
-
133
- // =============================================================================
134
- // Field Descriptor
135
- // =============================================================================
136
-
137
- /**
138
- * Descriptor for a single workflow field in a model definition.
139
- *
140
- * Fields map to columns in the workflow's data model. Each field has a type,
141
- * optional default, and optional access control / validation rules.
142
- *
143
- * @example
144
- * ```typescript
145
- * price: {
146
- * type: 'currency',
147
- * required: true,
148
- * default: 0,
149
- * label: 'Unit Price',
150
- * validation: { min: 0, max: 10000 },
151
- * editableByRoles: ['admin', 'manager'],
152
- * editableInStates: ['draft', 'review'],
153
- * computed: 'state_data.quantity * state_data.unitCost',
154
- * computedDeps: ['quantity', 'unitCost'],
155
- * }
156
- * ```
157
- */
158
- export interface WorkflowFieldDescriptor {
159
- /** Field type — maps to TypeScript types via `InferFields`. */
160
- type: string;
161
- /** Whether the field must have a value before transitions. */
162
- required?: boolean;
163
- /** Default value when the field is not explicitly set. */
164
- default?: unknown;
165
- /** Allowed values for enum/select fields. */
166
- enum?: readonly string[];
167
- /** Array item type descriptor (for `type: 'array'`). */
168
- items?: { type: string };
169
- /** Human-readable label shown in UIs. */
170
- label?: string;
171
- /** Description / help text for the field. */
172
- description?: string;
173
-
174
- // --- Validation ---
175
-
176
- /** Structured validation rules (min, max, pattern, custom expressions). */
177
- validation?: FieldValidation | Record<string, unknown>;
178
-
179
- // --- Computed Fields ---
180
-
181
- /** Expression that computes this field's value. Field becomes read-only. */
182
- computed?: string;
183
- /** Field names this computed field depends on (for change tracking). */
184
- computedDeps?: readonly string[];
185
- /** @deprecated Use `computedDeps`. */
186
- computed_deps?: readonly string[];
187
-
188
- // --- Access Control ---
189
-
190
- /**
191
- * States in which this field is visible.
192
- * If omitted, visible in all states.
193
- *
194
- * @example `visibleInStates: ['active', 'review']`
195
- */
196
- visibleInStates?: readonly string[];
197
- /**
198
- * States in which this field is editable.
199
- * If omitted, editable in all states (subject to role checks).
200
- *
201
- * @example `editableInStates: ['draft', 'revision']`
202
- */
203
- editableInStates?: readonly string[];
204
- /**
205
- * Roles that can see this field.
206
- * If omitted, visible to all roles.
207
- *
208
- * @example `visibleToRoles: ['admin', 'manager']`
209
- */
210
- visibleToRoles?: readonly string[];
211
- /**
212
- * Roles that can edit this field.
213
- * If omitted, editable by all roles (subject to state checks).
214
- *
215
- * @example `editableByRoles: ['admin']`
216
- */
217
- editableByRoles?: readonly string[];
218
- /**
219
- * Expression that controls visibility. Evaluated at runtime.
220
- *
221
- * @example `visibleWhen: 'state_data.showAdvanced == true'`
222
- */
223
- visibleWhen?: string;
224
- /**
225
- * Expression that controls editability. Evaluated at runtime.
226
- *
227
- * @example `editableWhen: 'context.actor_id == state_data.ownerId'`
228
- */
229
- editableWhen?: string;
230
-
231
- // --- State Home (scope, persistence, sync) ---
232
-
233
- /**
234
- * Controls where this field's data lives and how it syncs.
235
- * Defaults to `{ scope: 'instance', persistence: 'durable', sync: 'none' }`.
236
- *
237
- * @example
238
- * ```typescript
239
- * stateHome: { scope: 'ephemeral', persistence: 'none' } // UI-only, not saved
240
- * stateHome: { scope: 'instance', persistence: 'durable', sync: 'tenant' } // saved + synced
241
- * ```
242
- */
243
- stateHome?: StateHome;
244
-
245
- // --- Type Versioning ---
246
-
247
- /**
248
- * Version pin for custom field types.
249
- * Used when the field type is a user-defined data-type workflow.
250
- */
251
- typeVersion?: string;
252
- /**
253
- * Base type this field inherits from (for custom data-type fields).
254
- *
255
- * @example `baseType: 'string'` — custom type structurally extends string
256
- */
257
- baseType?: string;
258
- }
259
-
260
- // =============================================================================
261
- // Action Types
262
- // =============================================================================
263
-
264
- /**
265
- * An action definition within a state or transition.
266
- *
267
- * Actions are the units of work in a workflow. They can set fields,
268
- * log events, send notifications, spawn sub-workflows, or call
269
- * opaque server/device functions.
270
- *
271
- * @example
272
- * ```typescript
273
- * // Simple set_field action
274
- * { id: 'clear-error', type: 'set_field', mode: 'auto', config: { field: 'error', expression: '""' } }
275
- *
276
- * // Server-side opaque action
277
- * { id: 'send-email', type: 'server:send_email', mode: 'auto', config: { template: 'welcome' } }
278
- *
279
- * // Spawn a sub-workflow
280
- * {
281
- * id: 'create-notification',
282
- * type: 'spawn_subworkflow',
283
- * mode: 'auto',
284
- * config: {
285
- * definition_slug: 'notification',
286
- * blocking: false,
287
- * input_mapping: { userId: 'context.actor_id', message: 'state_data.summary' },
288
- * },
289
- * }
290
- * ```
291
- */
292
- export interface ActionDefinition {
293
- /** Unique identifier for this action within its state/transition. */
294
- id: string;
295
- /**
296
- * Action type. Built-in types:
297
- * - `set_field` — set a single field via expression
298
- * - `set_fields` — set multiple fields via expressions
299
- * - `log_event` — emit an event to the audit log
300
- * - `send_notification` — send a notification to recipients
301
- * - `spawn_subworkflow` — create a child workflow instance
302
- * - `server:*` — opaque server-side actions (defined in actions.server.ts)
303
- * - `device:*` — opaque device-side actions (defined in actions.device.ts)
304
- * - `connector.*` — external service connector actions
305
- * - `llm.*` — LLM/AI actions
306
- */
307
- type: string;
308
- /** Whether this action runs automatically or requires manual trigger. Default: `'auto'`. */
309
- mode?: 'auto' | 'manual';
310
- /**
311
- * Action configuration. Shape depends on `type`:
312
- *
313
- * **set_field**: `{ field: string, expression: string }`
314
- * **set_fields**: `{ fields: Record<string, { expression: string }> }`
315
- * **log_event**: `{ event: string, data?: Record<string, string> }`
316
- * **send_notification**: `{ template: string, recipients: string, data?: Record<string, string> }`
317
- * **spawn_subworkflow**: `{ definition_slug: string, blocking?: boolean, input_mapping?: Record<string, string> }`
318
- */
319
- config?: Record<string, unknown>;
320
- /** Expression that must be truthy for this action to execute. */
321
- condition?: string;
322
- }
323
-
324
- /**
325
- * An on_event subscription — reacts to events from other workflow instances.
326
- *
327
- * @example
328
- * ```typescript
329
- * onEvent: [{
330
- * match: 'order:*:transition.completed',
331
- * description: 'Track order completions',
332
- * conditions: ['event.transition_name == "deliver"'],
333
- * actions: [
334
- * { id: 'inc', type: 'set_field', mode: 'auto', config: { field: 'count', expression: 'state_data.count + 1' } },
335
- * ],
336
- * }]
337
- * ```
338
- */
339
- export interface EventSubscription {
340
- /** Event pattern to match (glob-style). */
341
- match: string;
342
- /** Human-readable description of what this subscription does. */
343
- description?: string;
344
- /** Expression conditions that must ALL be truthy for the actions to fire. */
345
- conditions?: readonly string[];
346
- /** Actions to execute when the event matches and conditions pass. */
347
- actions: readonly ActionDefinition[];
348
- }
349
-
350
- /**
351
- * A scheduled/recurring action that runs while the workflow is in a given state.
352
- *
353
- * @example
354
- * ```typescript
355
- * during: [{
356
- * id: 'heartbeat',
357
- * type: 'interval',
358
- * interval_ms: 30000,
359
- * condition: 'state_data.monitoring == true',
360
- * actions: [{ id: 'ping', type: 'server:health_check', mode: 'auto', config: {} }],
361
- * }]
362
- * ```
363
- */
364
- export interface DuringAction {
365
- /** Unique identifier for this scheduled action. */
366
- id: string;
367
- /**
368
- * Schedule type:
369
- * - `interval` — repeats every `interval_ms` milliseconds
370
- * - `timeout` — fires once after `delay_ms` milliseconds
371
- * - `cron` — fires on a cron schedule
372
- * - `poll` — polls at `interval_ms`, stops when condition is false
373
- * - `once` — fires once on state entry
374
- */
375
- type?: 'interval' | 'timeout' | 'cron' | 'poll' | 'once';
376
- /** Repeat interval in milliseconds (for `interval` and `poll` types). */
377
- interval_ms?: number;
378
- /** Cron expression (for `cron` type). */
379
- cron?: string;
380
- /** Delay before firing in milliseconds (for `timeout` type). */
381
- delay_ms?: number;
382
- /** @deprecated Use `type` + `interval_ms`/`cron` directly. */
383
- schedule?: { type: string; cron?: string; interval?: number };
384
- /** Actions to execute on each tick. */
385
- actions: readonly ActionDefinition[];
386
- /** Expression condition — scheduled action only fires when truthy. */
387
- condition?: string;
388
- }
389
-
390
- // =============================================================================
391
- // State & Transition Types
392
- // =============================================================================
393
-
394
- /**
395
- * Descriptor for a workflow state.
396
- *
397
- * States are the nodes in the state machine. Each state can have entry/exit
398
- * actions, event subscriptions, scheduled actions, and timeouts.
399
- *
400
- * @example
401
- * ```typescript
402
- * authenticated: {
403
- * description: 'User is logged in and has a valid session',
404
- * onEnter: [
405
- * { id: 'log', type: 'log_event', mode: 'auto', config: { event: 'auth.success' } },
406
- * ],
407
- * onEvent: [{
408
- * match: 'session:*:expired',
409
- * actions: [{ id: 'kick', type: 'set_field', mode: 'auto', config: { field: 'reason', expression: '"session_expired"' } }],
410
- * }],
411
- * timeout: { duration: '24h', fallback: { transition: 'logout' } },
412
- * }
413
- * ```
414
- */
415
- export interface StateDescriptor {
416
- /**
417
- * State type:
418
- * - `'initial'` — the starting state (exactly one per model)
419
- * - `'end'` — a terminal state (no outgoing transitions)
420
- * - `'cancelled'` — a terminal state indicating cancellation
421
- * - omitted — regular intermediate state
422
- */
423
- type?: 'initial' | 'end' | 'cancelled';
424
- /** Human-readable description of what this state represents. */
425
- description?: string;
426
-
427
- // --- Lifecycle Actions ---
428
-
429
- /** Actions to run when entering this state (camelCase preferred). */
430
- onEnter?: readonly ActionDefinition[];
431
- /** @deprecated Use `onEnter`. */
432
- on_enter?: readonly ActionDefinition[];
433
- /** Actions to run when leaving this state. */
434
- onExit?: readonly ActionDefinition[];
435
- /** @deprecated Use `onExit`. */
436
- on_exit?: readonly ActionDefinition[];
437
-
438
- // --- Event Subscriptions ---
439
-
440
- /** React to events from other workflow instances while in this state. */
441
- onEvent?: readonly EventSubscription[];
442
- /** @deprecated Use `onEvent`. */
443
- on_event?: readonly EventSubscription[];
444
-
445
- // --- Scheduled Actions ---
446
-
447
- /** Scheduled/recurring actions that run while in this state. */
448
- during?: readonly DuringAction[];
449
-
450
- // --- Timeout ---
451
-
452
- /**
453
- * Automatic timeout — if the workflow stays in this state too long,
454
- * execute a fallback action or transition.
455
- *
456
- * @example
457
- * ```typescript
458
- * timeout: { duration: '30m', fallback: { transition: 'escalate' } }
459
- * timeout: { duration: '24h', fallback: { action: 'server:send_reminder' } }
460
- * ```
461
- */
462
- timeout?: {
463
- /** Duration string (e.g., `'5m'`, `'24h'`, `'7d'`). */
464
- duration: string;
465
- /** What to do when the timeout fires. */
466
- fallback?: {
467
- /** Action type to execute. */
468
- action?: string;
469
- /** Transition to auto-fire. */
470
- transition?: string;
471
- };
472
- };
473
- }
474
-
475
- /**
476
- * Condition for a transition guard.
477
- *
478
- * Guards prevent transitions from firing unless all conditions pass.
479
- * Supports expression-based, role-based, and field-based conditions,
480
- * as well as boolean combinators (AND/OR).
481
- *
482
- * @example
483
- * ```typescript
484
- * // Expression guard
485
- * { type: 'expression', expression: 'context.actor_id == state_data.ownerId' }
486
- *
487
- * // Role guard
488
- * { type: 'role', role: 'admin' }
489
- *
490
- * // Field guard with operator
491
- * { type: 'field', field: 'amount', operator: 'gte', value: 100 }
492
- *
493
- * // Nested boolean logic
494
- * { OR: [
495
- * { type: 'role', role: 'admin' },
496
- * { AND: [
497
- * { type: 'expression', expression: 'context.actor_id == state_data.ownerId' },
498
- * { type: 'field', field: 'status', operator: 'eq', value: 'draft' },
499
- * ]},
500
- * ]}
501
- *
502
- * // String shorthand (treated as expression)
503
- * 'context.actor_id == state_data.senderId'
504
- * ```
505
- */
506
- export interface TransitionCondition {
507
- /** Condition type. */
508
- type?: 'expression' | 'role' | 'field';
509
- /** Expression that must evaluate to truthy. */
510
- expression?: string;
511
- /** Role name required for a role-based guard. */
512
- role?: string;
513
- /** Field name for field-based conditions. */
514
- field?: string;
515
- /**
516
- * Comparison operator for field-based conditions:
517
- * `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `not_in`,
518
- * `contains`, `is_set`, `is_empty`, `matches`.
519
- */
520
- operator?: string;
521
- /** Value to compare against for field-based conditions. */
522
- value?: unknown;
523
- /**
524
- * Logical OR — at least one sub-condition must pass.
525
- *
526
- * @example `{ OR: [{ type: 'role', role: 'admin' }, { type: 'role', role: 'manager' }] }`
527
- */
528
- OR?: readonly (TransitionCondition | string)[];
529
- /**
530
- * Logical AND — all sub-conditions must pass.
531
- *
532
- * @example `{ AND: [{ type: 'expression', expression: '...' }, { type: 'role', role: 'admin' }] }`
533
- */
534
- AND?: readonly (TransitionCondition | string)[];
535
- }
536
-
537
- /**
538
- * Descriptor for a workflow transition.
539
- *
540
- * Transitions are the edges in the state machine — they move the workflow
541
- * from one state to another, optionally guarded by conditions and executing
542
- * actions along the way.
543
- *
544
- * @example
545
- * ```typescript
546
- * approve: {
547
- * from: ['review', 'escalated'],
548
- * to: 'approved',
549
- * description: 'Approve the request',
550
- * roles: ['admin', 'manager'],
551
- * requiredFields: ['approvalNote'],
552
- * conditions: [
553
- * { type: 'expression', expression: 'state_data.amount <= context.approvalLimit' },
554
- * ],
555
- * actions: [
556
- * { id: 'stamp', type: 'set_fields', mode: 'auto', config: {
557
- * fields: { approvedBy: { expression: 'context.actor_id' }, approvedAt: { expression: 'NOW()' } },
558
- * }},
559
- * { id: 'notify', type: 'send_notification', mode: 'auto', config: {
560
- * template: 'request_approved', recipients: '{{ state_data.requesterId }}',
561
- * }},
562
- * ],
563
- * }
564
- * ```
565
- */
566
- export interface TransitionDescriptor {
567
- /** Source state(s) — accepts a single string or array of strings. */
568
- from: string | readonly string[];
569
- /** Target state name. */
570
- to: string;
571
- /** Human-readable description of what this transition does. */
572
- description?: string;
573
- /**
574
- * Guard conditions — ALL must pass for the transition to fire.
575
- * Accepts `TransitionCondition` objects or expression strings.
576
- */
577
- conditions?: readonly (TransitionCondition | string)[];
578
- /** Actions to execute when this transition fires (after guards pass). */
579
- actions?: readonly ActionDefinition[];
580
- /**
581
- * Roles allowed to trigger this transition.
582
- * If omitted, any role can trigger it.
583
- *
584
- * @example `roles: ['admin', 'manager']`
585
- */
586
- roles?: readonly string[];
587
- /**
588
- * Fields that must have non-empty values before this transition can fire.
589
- *
590
- * @example `requiredFields: ['approvalNote', 'reason']`
591
- */
592
- requiredFields?: readonly string[];
593
- /** @deprecated Use `requiredFields`. */
594
- required_fields?: readonly string[];
595
- /**
596
- * If `true`, this transition fires automatically when its conditions are met,
597
- * without requiring an explicit trigger call.
598
- *
599
- * Used for pipeline/chain patterns where states flow automatically.
600
- *
601
- * @example
602
- * ```typescript
603
- * auto_advance: { from: 'processing', to: 'completed', auto: true,
604
- * conditions: [{ type: 'expression', expression: 'state_data.result != null' }],
605
- * }
606
- * ```
607
- */
608
- auto?: boolean;
609
- }
610
-
611
- // =============================================================================
612
- // Role Definitions
613
- // =============================================================================
614
-
615
- /**
616
- * Role definition for the workflow's permission model.
617
- *
618
- * @example
619
- * ```typescript
620
- * roles: {
621
- * admin: { description: 'Full access', permissions: ['manage_users', 'manage_settings', 'delete'] },
622
- * editor: { description: 'Can edit content', permissions: ['read', 'write', 'publish'], inherits: ['viewer'] },
623
- * viewer: { description: 'Read-only access', permissions: ['read'] },
624
- * }
625
- * ```
626
- */
627
- export interface RoleDefinition {
628
- /** Human-readable description of what this role can do. */
629
- description?: string;
630
- /** Permissions granted to this role. */
631
- permissions?: readonly string[];
632
- /** Roles this role inherits permissions from. */
633
- inherits?: readonly string[];
634
- }
635
-
636
- // =============================================================================
637
- // Model Definition
638
- // =============================================================================
639
-
640
- /**
641
- * Full shape of a model definition passed to `defineModel()`.
642
- *
643
- * This is the canonical TypeScript representation of a workflow definition.
644
- * It maps closely to the IR format stored in the database, but uses
645
- * camelCase and provides richer type information for IDE support.
646
- */
647
- export interface ModelDefinition {
648
- /** Unique slug identifier (kebab-case). */
649
- slug: string;
650
- /** Semantic version string. */
651
- version?: string;
652
- /**
653
- * Definition category — determines how the engine treats this definition.
654
- *
655
- * Common categories:
656
- * - `'model'` — a data model / state machine
657
- * - `'module'` — a reusable workflow module (importable via `mmrc install`)
658
- * - `'workflow'` — a business process
659
- * - `'data'` — a data table (fields = columns, instances = rows)
660
- * - `'blueprint'` — a full application (contains child definitions)
661
- * - `'view'` — a UI definition
662
- */
663
- category?: string | readonly string[];
664
- /** Human-readable description. */
665
- description?: string;
666
- /** Human-readable display name. */
667
- name?: string;
668
- /** Field definitions — the data schema for this workflow. */
669
- fields: Record<string, WorkflowFieldDescriptor>;
670
- /** State definitions — the nodes of the state machine. */
671
- states: Record<string, StateDescriptor>;
672
- /** Transition definitions — the edges of the state machine. */
673
- transitions: Record<string, TransitionDescriptor>;
674
- /**
675
- * Role definitions — the permission model for this workflow.
676
- * Roles can be referenced in transition `roles` guards and field ACL.
677
- */
678
- roles?: Record<string, RoleDefinition>;
679
- /** Arbitrary metadata (not interpreted by the engine). */
680
- metadata?: Record<string, unknown>;
681
- /**
682
- * Tags for categorization and discovery.
683
- *
684
- * @example `tags: ['finance', 'approval', 'internal']`
685
- */
686
- tags?: readonly string[];
687
- /**
688
- * Model-level event subscriptions — react to events from other workflows
689
- * regardless of what state this workflow is in.
690
- */
691
- onEvent?: readonly EventSubscription[];
692
- /** @deprecated Use `onEvent`. */
693
- on_event?: readonly EventSubscription[];
694
- }
695
-
696
- // =============================================================================
697
- // defineModel()
698
- // =============================================================================
699
-
700
- /**
701
- * Define a workflow model with preserved literal types.
702
- *
703
- * The `const` type parameter ensures that field names, state names, and
704
- * transition names are inferred as string literals, not widened to `string`.
705
- * This is what makes typed hooks (`useModel`, `useQuery`, `useMutation`) work.
706
- *
707
- * At runtime this is an identity function — it returns the definition as-is.
708
- *
709
- * @param def - The model definition object.
710
- * @returns The same definition, with narrowed literal types for IDE support.
711
- *
712
- * @example
713
- * ```typescript
714
- * import { defineModel } from '@mindmatrix/react';
715
- *
716
- * export default defineModel({
717
- * slug: 'invoice',
718
- * version: '1.0.0',
719
- * category: 'data',
720
- * fields: {
721
- * amount: { type: 'currency', required: true, validation: { min: 0 } },
722
- * status: { type: 'string', default: 'draft', enum: ['draft', 'sent', 'paid'] },
723
- * },
724
- * states: {
725
- * draft: { type: 'initial' },
726
- * sent: {},
727
- * paid: { type: 'end' },
728
- * },
729
- * transitions: {
730
- * send: { from: 'draft', to: 'sent', requiredFields: ['amount'] },
731
- * pay: { from: 'sent', to: 'paid', auto: true, conditions: [{ type: 'expression', expression: 'state_data.paymentReceived == true' }] },
732
- * },
733
- * });
734
- * ```
735
- */
736
- export function defineModel<const D extends ModelDefinition>(def: D): D {
737
- // Optional runtime validation (lightweight, skip in production)
738
- if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
739
- if (!def.slug) {
740
- throw new Error('defineModel: slug is required');
741
- }
742
- if (!def.fields || typeof def.fields !== 'object') {
743
- throw new Error(`defineModel(${def.slug}): fields object is required`);
744
- }
745
- if (!def.states || typeof def.states !== 'object') {
746
- throw new Error(`defineModel(${def.slug}): states object is required`);
747
- }
748
- if (!def.transitions || typeof def.transitions !== 'object') {
749
- throw new Error(`defineModel(${def.slug}): transitions object is required`);
750
- }
751
- }
752
- return def;
753
- }