@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.
- package/README.md +112 -0
- package/dist/index.d.mts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +70 -3
- package/dist/index.mjs +74 -12
- package/package.json +4 -3
- package/package.json.backup +0 -41
- package/src/Blueprint.ts +0 -216
- package/src/__tests__/Blueprint.test.ts +0 -106
- package/src/__tests__/action-context.test.ts +0 -166
- package/src/__tests__/actionCreators.test.ts +0 -179
- package/src/__tests__/builders.test.ts +0 -336
- package/src/__tests__/defineBlueprint-composition.test.ts +0 -106
- package/src/__tests__/factories.test.ts +0 -229
- package/src/__tests__/loader.test.ts +0 -159
- package/src/__tests__/logger.test.ts +0 -70
- package/src/__tests__/type-inference.test.ts +0 -160
- package/src/__tests__/typed-transitions.test.ts +0 -126
- package/src/__tests__/useModuleConfig.test.ts +0 -61
- package/src/actionCreators.ts +0 -132
- package/src/actions.ts +0 -547
- package/src/atoms/index.ts +0 -600
- package/src/authoring.ts +0 -92
- package/src/browser-player.ts +0 -783
- package/src/builders.ts +0 -1342
- package/src/components/ExperienceWorkflowBridge.tsx +0 -123
- package/src/components/PlayerProvider.tsx +0 -43
- package/src/components/atoms/index.tsx +0 -269
- package/src/components/index.ts +0 -36
- package/src/conditions.ts +0 -692
- package/src/config/defineBlueprint.ts +0 -329
- package/src/config/defineModel.ts +0 -753
- package/src/config/defineWorkspace.ts +0 -24
- package/src/core/WorkflowRuntime.ts +0 -153
- package/src/factories.ts +0 -425
- package/src/grammar/index.ts +0 -173
- package/src/hooks/index.ts +0 -106
- package/src/hooks/useAuth.ts +0 -288
- package/src/hooks/useChannel.ts +0 -304
- package/src/hooks/useComputed.ts +0 -154
- package/src/hooks/useDomainSubscription.ts +0 -110
- package/src/hooks/useDuringAction.ts +0 -99
- package/src/hooks/useExperienceState.ts +0 -59
- package/src/hooks/useExpressionLibrary.ts +0 -129
- package/src/hooks/useForm.ts +0 -352
- package/src/hooks/useGeolocation.ts +0 -207
- package/src/hooks/useMapView.ts +0 -259
- package/src/hooks/useMiddleware.ts +0 -291
- package/src/hooks/useModel.ts +0 -363
- package/src/hooks/useModule.ts +0 -59
- package/src/hooks/useModuleConfig.ts +0 -61
- package/src/hooks/useMutation.ts +0 -237
- package/src/hooks/useNotification.ts +0 -151
- package/src/hooks/useOnChange.ts +0 -30
- package/src/hooks/useOnEnter.ts +0 -59
- package/src/hooks/useOnEvent.ts +0 -37
- package/src/hooks/useOnExit.ts +0 -27
- package/src/hooks/useOnTransition.ts +0 -30
- package/src/hooks/usePackage.ts +0 -128
- package/src/hooks/useParams.ts +0 -33
- package/src/hooks/usePlayer.ts +0 -308
- package/src/hooks/useQuery.ts +0 -184
- package/src/hooks/useRealtimeQuery.ts +0 -222
- package/src/hooks/useRole.ts +0 -191
- package/src/hooks/useRouteParams.ts +0 -100
- package/src/hooks/useRouter.ts +0 -347
- package/src/hooks/useServerAction.ts +0 -178
- package/src/hooks/useServerState.ts +0 -284
- package/src/hooks/useToast.ts +0 -164
- package/src/hooks/useTransition.ts +0 -39
- package/src/hooks/useView.ts +0 -102
- package/src/hooks/useWhileIn.ts +0 -48
- package/src/hooks/useWorkflow.ts +0 -63
- package/src/index.ts +0 -465
- package/src/loader/experience-workflow-loader.ts +0 -192
- package/src/loader/index.ts +0 -6
- package/src/local/LocalEngine.ts +0 -388
- package/src/local/LocalEngineAdapter.ts +0 -175
- package/src/local/LocalEngineContext.ts +0 -30
- package/src/logger.ts +0 -37
- package/src/mixins.ts +0 -1160
- package/src/providers/RuntimeContext.ts +0 -20
- package/src/providers/WorkflowProvider.tsx +0 -28
- package/src/routing/instance-key.ts +0 -107
- package/src/server/transition-context.ts +0 -172
- package/src/testing/index.ts +0 -9
- package/src/testing/useBlueprintTestRunner.ts +0 -91
- package/src/testing/useGraphAnalysis.ts +0 -18
- package/src/testing/useTestRunner.ts +0 -77
- package/src/testing.ts +0 -995
- package/src/types/workflow-inference.ts +0 -158
- package/src/types.ts +0 -114
- package/tsconfig.json +0 -27
- 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
|
-
}
|