@mmapp/player-core 0.1.0-alpha.1
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/dist/index.d.mts +1436 -0
- package/dist/index.d.ts +1436 -0
- package/dist/index.js +4828 -0
- package/dist/index.mjs +4762 -0
- package/package.json +35 -0
- package/package.json.backup +35 -0
- package/src/__tests__/actions.test.ts +187 -0
- package/src/__tests__/blueprint-e2e.test.ts +706 -0
- package/src/__tests__/blueprint-test-runner.test.ts +680 -0
- package/src/__tests__/core-functions.test.ts +78 -0
- package/src/__tests__/dsl-compiler.test.ts +1382 -0
- package/src/__tests__/dsl-grammar.test.ts +1682 -0
- package/src/__tests__/events.test.ts +200 -0
- package/src/__tests__/expression.test.ts +296 -0
- package/src/__tests__/failure-policies.test.ts +110 -0
- package/src/__tests__/frontend-context.test.ts +182 -0
- package/src/__tests__/integration.test.ts +256 -0
- package/src/__tests__/security.test.ts +190 -0
- package/src/__tests__/state-machine.test.ts +450 -0
- package/src/__tests__/testing-engine.test.ts +671 -0
- package/src/actions/dispatcher.ts +80 -0
- package/src/actions/index.ts +7 -0
- package/src/actions/types.ts +25 -0
- package/src/dsl/compiler/component-mapper.ts +289 -0
- package/src/dsl/compiler/field-mapper.ts +187 -0
- package/src/dsl/compiler/index.ts +82 -0
- package/src/dsl/compiler/manifest-compiler.ts +76 -0
- package/src/dsl/compiler/symbol-table.ts +214 -0
- package/src/dsl/compiler/utils.ts +48 -0
- package/src/dsl/compiler/view-compiler.ts +286 -0
- package/src/dsl/compiler/workflow-compiler.ts +600 -0
- package/src/dsl/index.ts +66 -0
- package/src/dsl/ir-migration.ts +221 -0
- package/src/dsl/ir-types.ts +416 -0
- package/src/dsl/lexer.ts +579 -0
- package/src/dsl/parser.ts +115 -0
- package/src/dsl/types.ts +256 -0
- package/src/events/event-bus.ts +68 -0
- package/src/events/index.ts +9 -0
- package/src/events/pattern-matcher.ts +61 -0
- package/src/events/types.ts +27 -0
- package/src/expression/evaluator.ts +676 -0
- package/src/expression/functions.ts +214 -0
- package/src/expression/index.ts +13 -0
- package/src/expression/types.ts +64 -0
- package/src/index.ts +61 -0
- package/src/state-machine/index.ts +16 -0
- package/src/state-machine/interpreter.ts +319 -0
- package/src/state-machine/types.ts +89 -0
- package/src/testing/action-trace.ts +209 -0
- package/src/testing/blueprint-test-runner.ts +214 -0
- package/src/testing/graph-walker.ts +249 -0
- package/src/testing/index.ts +69 -0
- package/src/testing/nrt-comparator.ts +199 -0
- package/src/testing/nrt-types.ts +230 -0
- package/src/testing/test-actions.ts +645 -0
- package/src/testing/test-compiler.ts +278 -0
- package/src/testing/test-runner.ts +444 -0
- package/src/testing/types.ts +231 -0
- package/src/validation/definition-validator.ts +812 -0
- package/src/validation/index.ts +13 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +8 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression Engine Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract for expression evaluation across all player contexts.
|
|
5
|
+
* See player-specification.md §2.3 for full specification.
|
|
6
|
+
*/
|
|
7
|
+
/** Result of evaluating an expression */
|
|
8
|
+
interface ExpressionResult<T = unknown> {
|
|
9
|
+
value: T;
|
|
10
|
+
status: 'ok' | 'error' | 'fallback';
|
|
11
|
+
error?: string;
|
|
12
|
+
diagnostics?: ExpressionDiagnostics;
|
|
13
|
+
}
|
|
14
|
+
/** Diagnostic information for debugging expressions */
|
|
15
|
+
interface ExpressionDiagnostics {
|
|
16
|
+
expression: string;
|
|
17
|
+
duration_ms: number;
|
|
18
|
+
resolved_paths: string[];
|
|
19
|
+
unresolved_paths: string[];
|
|
20
|
+
}
|
|
21
|
+
/** Failure policy determines behavior when an expression fails */
|
|
22
|
+
interface FailurePolicy {
|
|
23
|
+
on_error: 'return_fallback' | 'throw' | 'log_and_skip';
|
|
24
|
+
fallback_value: unknown;
|
|
25
|
+
log_level: 'silent' | 'warn' | 'error';
|
|
26
|
+
}
|
|
27
|
+
/** Expression context for evaluation */
|
|
28
|
+
interface ExpressionContext {
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
/** A registered function available in expressions */
|
|
32
|
+
interface ExpressionFunction {
|
|
33
|
+
name: string;
|
|
34
|
+
fn: (...args: unknown[]) => unknown;
|
|
35
|
+
arity?: number;
|
|
36
|
+
description?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Configuration for createEvaluator factory */
|
|
39
|
+
interface EvaluatorConfig {
|
|
40
|
+
functions: ExpressionFunction[];
|
|
41
|
+
failurePolicy: FailurePolicy;
|
|
42
|
+
maxDepth?: number;
|
|
43
|
+
maxOperations?: number;
|
|
44
|
+
}
|
|
45
|
+
/** The evaluator interface returned by createEvaluator */
|
|
46
|
+
interface Evaluator {
|
|
47
|
+
evaluate<T = unknown>(expression: string, context: ExpressionContext): ExpressionResult<T>;
|
|
48
|
+
evaluateTemplate(template: string, context: ExpressionContext): ExpressionResult<string>;
|
|
49
|
+
validate(expression: string): {
|
|
50
|
+
valid: boolean;
|
|
51
|
+
errors: string[];
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Pre-defined failure policy contexts for the Web Player */
|
|
55
|
+
type FailurePolicyContext = 'VIEW_BINDING' | 'EVENT_REACTION' | 'DURING_ACTION' | 'CONDITIONAL_VISIBILITY';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Core Expression Functions — 31 functions across 6 categories.
|
|
59
|
+
*
|
|
60
|
+
* These are the standard functions available in every player context.
|
|
61
|
+
* Platform-specific functions ($domain, $fe_ref, etc.) are registered
|
|
62
|
+
* separately by the host environment.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
declare const CORE_FUNCTIONS: ExpressionFunction[];
|
|
66
|
+
/** Lookup map for O(1) function resolution */
|
|
67
|
+
declare function buildFunctionMap(functions: ExpressionFunction[]): Map<string, ExpressionFunction['fn']>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Expression Evaluator — createEvaluator factory and evaluation engine.
|
|
71
|
+
*
|
|
72
|
+
* Parses and evaluates expressions with a safe recursive descent parser.
|
|
73
|
+
* NO dynamic code compilation (no new Function(), no eval(), no with()).
|
|
74
|
+
*
|
|
75
|
+
* Supported syntax:
|
|
76
|
+
* - Path resolution: `state_data.title`, `$instance.fields.name`
|
|
77
|
+
* - Function calls: `add(1, 2)`, `if(eq(x, 1), 'yes', 'no')`
|
|
78
|
+
* - Template interpolation: `"Hello {{name}}, you have {{count}} items"`
|
|
79
|
+
* - Ternary expressions: `condition ? 'yes' : 'no'`
|
|
80
|
+
* - Operators: `==`, `!=`, `>`, `<`, `>=`, `<=`, `&&`, `||`, `!`
|
|
81
|
+
* - Literals: strings, numbers, booleans, null
|
|
82
|
+
*
|
|
83
|
+
* Grammar:
|
|
84
|
+
* expression → ternary
|
|
85
|
+
* ternary → logicalOr ('?' expression ':' expression)?
|
|
86
|
+
* logicalOr → logicalAnd ('||' logicalAnd)*
|
|
87
|
+
* logicalAnd → equality ('&&' equality)*
|
|
88
|
+
* equality → comparison (('=='|'!=') comparison)*
|
|
89
|
+
* comparison → unary (('>'|'<'|'>='|'<=') unary)*
|
|
90
|
+
* unary → ('!' unary) | call
|
|
91
|
+
* call → primary ('(' args? ')')? ('.' IDENT ('(' args? ')')?)*
|
|
92
|
+
* primary → NUMBER | STRING | BOOL | NULL | IDENT | '(' expression ')'
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* WEB_FAILURE_POLICIES — predefined failure policies for Web Player contexts.
|
|
97
|
+
*/
|
|
98
|
+
declare const WEB_FAILURE_POLICIES: Record<string, FailurePolicy>;
|
|
99
|
+
/**
|
|
100
|
+
* createEvaluator — factory function that builds a configured evaluator.
|
|
101
|
+
*
|
|
102
|
+
* @param config - Function registry and failure policy
|
|
103
|
+
* @returns Evaluator instance with evaluate, evaluateTemplate, and validate methods
|
|
104
|
+
*/
|
|
105
|
+
declare function createEvaluator(config: EvaluatorConfig): Evaluator;
|
|
106
|
+
/** Clear parsed AST cache (for testing) */
|
|
107
|
+
declare function clearExpressionCache(): void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* State Machine Types — browser-side workflow interpretation.
|
|
111
|
+
*
|
|
112
|
+
* Mirrors the backend PureWorkflowDefinition/Instance structure
|
|
113
|
+
* but stripped to only what the browser engine needs.
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
/** State type in the workflow */
|
|
117
|
+
type StateType = 'START' | 'REGULAR' | 'END' | 'CANCELLED';
|
|
118
|
+
/** Action that runs during on_event handling */
|
|
119
|
+
interface PlayerAction {
|
|
120
|
+
type: string;
|
|
121
|
+
config: Record<string, unknown>;
|
|
122
|
+
condition?: string;
|
|
123
|
+
}
|
|
124
|
+
/** On-event subscription for browser-side processing */
|
|
125
|
+
interface PlayerOnEventSubscription {
|
|
126
|
+
match: string;
|
|
127
|
+
conditions?: string[];
|
|
128
|
+
actions: PlayerAction[];
|
|
129
|
+
}
|
|
130
|
+
/** State definition for the browser engine */
|
|
131
|
+
interface PlayerStateDefinition {
|
|
132
|
+
name: string;
|
|
133
|
+
type: StateType;
|
|
134
|
+
on_enter?: PlayerAction[];
|
|
135
|
+
on_exit?: PlayerAction[];
|
|
136
|
+
on_event?: PlayerOnEventSubscription[];
|
|
137
|
+
}
|
|
138
|
+
/** Transition definition */
|
|
139
|
+
interface PlayerTransitionDefinition {
|
|
140
|
+
name: string;
|
|
141
|
+
from: string[];
|
|
142
|
+
to: string;
|
|
143
|
+
conditions?: string[];
|
|
144
|
+
auto?: boolean;
|
|
145
|
+
actions?: PlayerAction[];
|
|
146
|
+
}
|
|
147
|
+
/** Workflow definition for the browser engine */
|
|
148
|
+
interface PlayerWorkflowDefinition {
|
|
149
|
+
id: string;
|
|
150
|
+
slug: string;
|
|
151
|
+
states: PlayerStateDefinition[];
|
|
152
|
+
transitions: PlayerTransitionDefinition[];
|
|
153
|
+
}
|
|
154
|
+
/** Runtime instance state */
|
|
155
|
+
interface PlayerInstance {
|
|
156
|
+
definition: PlayerWorkflowDefinition;
|
|
157
|
+
current_state: string;
|
|
158
|
+
state_data: Record<string, unknown>;
|
|
159
|
+
memory: Record<string, unknown>;
|
|
160
|
+
status: 'ACTIVE' | 'COMPLETED' | 'CANCELLED';
|
|
161
|
+
}
|
|
162
|
+
/** Transition result from the state machine */
|
|
163
|
+
interface TransitionResult {
|
|
164
|
+
success: boolean;
|
|
165
|
+
from_state: string;
|
|
166
|
+
to_state: string;
|
|
167
|
+
actions_executed: PlayerAction[];
|
|
168
|
+
error?: string;
|
|
169
|
+
}
|
|
170
|
+
/** State machine event listener */
|
|
171
|
+
type StateMachineListener = (event: StateMachineEvent) => void;
|
|
172
|
+
/** Events emitted by the state machine */
|
|
173
|
+
interface StateMachineEvent {
|
|
174
|
+
type: 'transition' | 'state_enter' | 'state_exit' | 'action_executed' | 'error';
|
|
175
|
+
instance_id: string;
|
|
176
|
+
from_state?: string;
|
|
177
|
+
to_state?: string;
|
|
178
|
+
action?: PlayerAction;
|
|
179
|
+
error?: string;
|
|
180
|
+
}
|
|
181
|
+
/** Action handler registered with the state machine */
|
|
182
|
+
type ActionHandler = (action: PlayerAction, context: ExpressionContext) => void | Promise<void>;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* State Machine Interpreter — browser-side workflow engine.
|
|
186
|
+
*
|
|
187
|
+
* Interprets PlayerWorkflowDefinition instances with:
|
|
188
|
+
* - State transitions (manual + auto)
|
|
189
|
+
* - on_enter / on_exit action execution
|
|
190
|
+
* - Condition evaluation via expression engine
|
|
191
|
+
* - Listener-based event emission
|
|
192
|
+
* - Auto-transition drain loop with depth limit
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
interface StateMachineConfig {
|
|
196
|
+
evaluator: Evaluator;
|
|
197
|
+
actionHandlers?: Map<string, ActionHandler>;
|
|
198
|
+
}
|
|
199
|
+
declare class StateMachine {
|
|
200
|
+
private readonly evaluator;
|
|
201
|
+
private readonly actionHandlers;
|
|
202
|
+
private readonly listeners;
|
|
203
|
+
private instance;
|
|
204
|
+
constructor(definition: PlayerWorkflowDefinition, initialData: Record<string, unknown> | undefined, config: StateMachineConfig);
|
|
205
|
+
/** Get the current instance snapshot (immutable copy) */
|
|
206
|
+
getSnapshot(): Readonly<PlayerInstance>;
|
|
207
|
+
/** Get current state name */
|
|
208
|
+
get currentState(): string;
|
|
209
|
+
/** Get current state_data */
|
|
210
|
+
get stateData(): Record<string, unknown>;
|
|
211
|
+
/** Get current status */
|
|
212
|
+
get status(): string;
|
|
213
|
+
/** Subscribe to state machine events */
|
|
214
|
+
on(listener: StateMachineListener): () => void;
|
|
215
|
+
/** Register an action handler */
|
|
216
|
+
registerAction(type: string, handler: ActionHandler): void;
|
|
217
|
+
/** Execute a named transition */
|
|
218
|
+
transition(transitionName: string, data?: Record<string, unknown>): Promise<TransitionResult>;
|
|
219
|
+
/** Update state_data directly (for on_event set_field actions) */
|
|
220
|
+
setField(field: string, value: unknown): void;
|
|
221
|
+
/** Update memory */
|
|
222
|
+
setMemory(key: string, value: unknown): void;
|
|
223
|
+
/** Get available transitions from the current state */
|
|
224
|
+
getAvailableTransitions(): PlayerTransitionDefinition[];
|
|
225
|
+
/** Get the current state definition */
|
|
226
|
+
getCurrentStateDefinition(): PlayerStateDefinition | undefined;
|
|
227
|
+
private executeTransition;
|
|
228
|
+
private drainAutoTransitions;
|
|
229
|
+
private findMatchingAutoTransition;
|
|
230
|
+
private executeActions;
|
|
231
|
+
private buildContext;
|
|
232
|
+
private emit;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Event System Types — browser-side on_event matching and dispatch.
|
|
237
|
+
*/
|
|
238
|
+
/** Subscription registered with the event bus */
|
|
239
|
+
interface EventSubscription {
|
|
240
|
+
/** Glob-style pattern: "workflow.*:state_enter" or "*:*:completed" */
|
|
241
|
+
pattern: string;
|
|
242
|
+
/** Compiled regex for fast matching (derived from pattern) */
|
|
243
|
+
regex: RegExp;
|
|
244
|
+
/** Conditions evaluated before actions fire */
|
|
245
|
+
conditions?: string[];
|
|
246
|
+
/** Handler called when pattern matches and conditions pass */
|
|
247
|
+
handler: EventHandler;
|
|
248
|
+
}
|
|
249
|
+
/** Event payload passed to handlers */
|
|
250
|
+
interface BusEvent {
|
|
251
|
+
topic: string;
|
|
252
|
+
payload: Record<string, unknown>;
|
|
253
|
+
}
|
|
254
|
+
/** Handler function for matched events */
|
|
255
|
+
type EventHandler = (event: BusEvent) => void | Promise<void>;
|
|
256
|
+
/** Unsubscribe function returned by subscribe */
|
|
257
|
+
type Unsubscribe = () => void;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Pattern Matcher — glob-style topic matching for on_event subscriptions.
|
|
261
|
+
*
|
|
262
|
+
* Supports:
|
|
263
|
+
* - Exact match: "workflow.order:state_enter"
|
|
264
|
+
* - Single-level wildcard (*): "workflow.*:state_enter" matches "workflow.order:state_enter"
|
|
265
|
+
* - Multi-level wildcard (**): "workflow.**" matches "workflow.order:state_enter:completed"
|
|
266
|
+
* - Colon-separated segments (like EventEmitter2)
|
|
267
|
+
*
|
|
268
|
+
* Compiled regex is cached per pattern for performance.
|
|
269
|
+
*/
|
|
270
|
+
/**
|
|
271
|
+
* Compile a glob pattern into a RegExp.
|
|
272
|
+
*
|
|
273
|
+
* Pattern rules:
|
|
274
|
+
* - Segments separated by `:` or `.`
|
|
275
|
+
* - `*` matches exactly one segment (any chars except `:` and `.`)
|
|
276
|
+
* - `**` matches zero or more segments (greedy)
|
|
277
|
+
* - Everything else is literal
|
|
278
|
+
*/
|
|
279
|
+
declare function compilePattern(pattern: string): RegExp;
|
|
280
|
+
/**
|
|
281
|
+
* Test if a topic matches a compiled pattern.
|
|
282
|
+
*/
|
|
283
|
+
declare function matchTopic(pattern: RegExp, topic: string): boolean;
|
|
284
|
+
/** Clear pattern cache (for testing) */
|
|
285
|
+
declare function clearPatternCache(): void;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Event Bus — browser-side pub/sub for on_event subscriptions.
|
|
289
|
+
*
|
|
290
|
+
* Lightweight event bus that matches published topics against
|
|
291
|
+
* glob-pattern subscriptions. Used by the player to wire
|
|
292
|
+
* on_event declarations to action handlers.
|
|
293
|
+
*/
|
|
294
|
+
|
|
295
|
+
declare class EventBus {
|
|
296
|
+
private subscriptions;
|
|
297
|
+
/**
|
|
298
|
+
* Subscribe to events matching a glob pattern.
|
|
299
|
+
* Returns an unsubscribe function.
|
|
300
|
+
*/
|
|
301
|
+
subscribe(pattern: string, handler: EventHandler): Unsubscribe;
|
|
302
|
+
/**
|
|
303
|
+
* Publish an event. All matching subscriptions fire (async).
|
|
304
|
+
* Errors in handlers are caught and logged, never propagated.
|
|
305
|
+
*/
|
|
306
|
+
publish(topic: string, payload?: Record<string, unknown>): Promise<void>;
|
|
307
|
+
/**
|
|
308
|
+
* Publish synchronously (fire-and-forget).
|
|
309
|
+
* Useful when you don't need to await handler completion.
|
|
310
|
+
*/
|
|
311
|
+
emit(topic: string, payload?: Record<string, unknown>): void;
|
|
312
|
+
/** Get count of active subscriptions */
|
|
313
|
+
get size(): number;
|
|
314
|
+
/** Remove all subscriptions */
|
|
315
|
+
clear(): void;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Action System Types — handler registry and dispatch.
|
|
320
|
+
*/
|
|
321
|
+
|
|
322
|
+
/** Action definition from workflow state/transition */
|
|
323
|
+
interface ActionDefinition {
|
|
324
|
+
type: string;
|
|
325
|
+
config: Record<string, unknown>;
|
|
326
|
+
condition?: string;
|
|
327
|
+
}
|
|
328
|
+
/** Registered action handler */
|
|
329
|
+
type ActionHandlerFn = (config: Record<string, unknown>, context: ExpressionContext) => void | Promise<void>;
|
|
330
|
+
/** Result of executing an action */
|
|
331
|
+
interface ActionResult {
|
|
332
|
+
type: string;
|
|
333
|
+
success: boolean;
|
|
334
|
+
error?: string;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Action Dispatcher — executes action definitions against registered handlers.
|
|
339
|
+
*
|
|
340
|
+
* The dispatcher:
|
|
341
|
+
* - Maintains a registry of handler functions by action type
|
|
342
|
+
* - Evaluates per-action conditions before execution
|
|
343
|
+
* - Collects results (success/failure) for each action
|
|
344
|
+
* - Never throws — errors are captured in ActionResult
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
declare class ActionDispatcher {
|
|
348
|
+
private readonly handlers;
|
|
349
|
+
/** Register a handler for an action type */
|
|
350
|
+
register(type: string, handler: ActionHandlerFn): void;
|
|
351
|
+
/** Unregister a handler */
|
|
352
|
+
unregister(type: string): void;
|
|
353
|
+
/** Check if a handler is registered for the given type */
|
|
354
|
+
has(type: string): boolean;
|
|
355
|
+
/**
|
|
356
|
+
* Execute a list of actions sequentially.
|
|
357
|
+
* Each action's condition is evaluated first (if present).
|
|
358
|
+
* Missing handlers are skipped with a warning.
|
|
359
|
+
*/
|
|
360
|
+
execute(actions: ActionDefinition[], context: ExpressionContext, evaluator?: Evaluator): Promise<ActionResult[]>;
|
|
361
|
+
/** Get count of registered handlers */
|
|
362
|
+
get size(): number;
|
|
363
|
+
/** Remove all handlers */
|
|
364
|
+
clear(): void;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Testing OS Types — test declarations for the player-core engine.
|
|
369
|
+
*
|
|
370
|
+
* All types are JSON-serializable, enabling:
|
|
371
|
+
* - LLM generation of test programs
|
|
372
|
+
* - Database storage of test suites
|
|
373
|
+
* - Cross-platform execution (browser, iOS, Node)
|
|
374
|
+
*/
|
|
375
|
+
|
|
376
|
+
/** A node in the workflow state graph */
|
|
377
|
+
interface StateNode {
|
|
378
|
+
name: string;
|
|
379
|
+
type: 'START' | 'REGULAR' | 'END' | 'CANCELLED';
|
|
380
|
+
reachable: boolean;
|
|
381
|
+
hasOnEvent: boolean;
|
|
382
|
+
hasOnEnter: boolean;
|
|
383
|
+
hasOnExit: boolean;
|
|
384
|
+
}
|
|
385
|
+
/** An edge (transition) in the workflow state graph */
|
|
386
|
+
interface TransitionEdge {
|
|
387
|
+
name: string;
|
|
388
|
+
from: string;
|
|
389
|
+
to: string;
|
|
390
|
+
auto: boolean;
|
|
391
|
+
hasConditions: boolean;
|
|
392
|
+
}
|
|
393
|
+
/** A path through the state graph */
|
|
394
|
+
interface GraphPath {
|
|
395
|
+
states: string[];
|
|
396
|
+
transitions: string[];
|
|
397
|
+
}
|
|
398
|
+
/** Summary statistics for a workflow definition */
|
|
399
|
+
interface AnalysisSummary {
|
|
400
|
+
totalStates: number;
|
|
401
|
+
totalTransitions: number;
|
|
402
|
+
reachableStates: number;
|
|
403
|
+
terminalPaths: number;
|
|
404
|
+
cycles: number;
|
|
405
|
+
unreachableStates: number;
|
|
406
|
+
deadEndStates: number;
|
|
407
|
+
}
|
|
408
|
+
/** Full static analysis result for a workflow definition */
|
|
409
|
+
interface AnalysisResult {
|
|
410
|
+
states: StateNode[];
|
|
411
|
+
edges: TransitionEdge[];
|
|
412
|
+
terminalPaths: GraphPath[];
|
|
413
|
+
cycles: GraphPath[];
|
|
414
|
+
unreachableStates: string[];
|
|
415
|
+
deadEndStates: string[];
|
|
416
|
+
summary: AnalysisSummary;
|
|
417
|
+
}
|
|
418
|
+
/** A complete test program with scenarios */
|
|
419
|
+
interface TestProgram {
|
|
420
|
+
name: string;
|
|
421
|
+
definitionSlug: string;
|
|
422
|
+
definitions: Record<string, PlayerWorkflowDefinition>;
|
|
423
|
+
scenarios: TestScenario[];
|
|
424
|
+
}
|
|
425
|
+
/** Instance declaration for multi-instance scenarios */
|
|
426
|
+
interface TestInstance {
|
|
427
|
+
id: string;
|
|
428
|
+
definitionSlug: string;
|
|
429
|
+
initialData?: Record<string, unknown>;
|
|
430
|
+
}
|
|
431
|
+
/** A test scenario with steps */
|
|
432
|
+
interface TestScenario {
|
|
433
|
+
name: string;
|
|
434
|
+
tags?: string[];
|
|
435
|
+
instances?: TestInstance[];
|
|
436
|
+
connectEventBuses?: boolean;
|
|
437
|
+
steps: TestStep[];
|
|
438
|
+
}
|
|
439
|
+
/** A single test step: action + assertions */
|
|
440
|
+
interface TestStep {
|
|
441
|
+
name: string;
|
|
442
|
+
instanceId?: string;
|
|
443
|
+
action: {
|
|
444
|
+
type: 'transition';
|
|
445
|
+
name: string;
|
|
446
|
+
data?: Record<string, unknown>;
|
|
447
|
+
} | {
|
|
448
|
+
type: 'set_field';
|
|
449
|
+
field: string;
|
|
450
|
+
value: unknown;
|
|
451
|
+
} | {
|
|
452
|
+
type: 'publish_event';
|
|
453
|
+
topic: string;
|
|
454
|
+
payload?: Record<string, unknown>;
|
|
455
|
+
} | {
|
|
456
|
+
type: 'assert_only';
|
|
457
|
+
};
|
|
458
|
+
assertions: TestAssertion[];
|
|
459
|
+
}
|
|
460
|
+
/** A single assertion to verify after an action */
|
|
461
|
+
interface TestAssertion {
|
|
462
|
+
target: 'state' | 'status' | 'state_data' | 'available_transitions';
|
|
463
|
+
instanceId?: string;
|
|
464
|
+
path?: string;
|
|
465
|
+
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'truthy' | 'falsy';
|
|
466
|
+
expected: unknown;
|
|
467
|
+
label?: string;
|
|
468
|
+
}
|
|
469
|
+
/** Result of a single assertion */
|
|
470
|
+
interface AssertionResult {
|
|
471
|
+
passed: boolean;
|
|
472
|
+
assertion: TestAssertion;
|
|
473
|
+
actual: unknown;
|
|
474
|
+
error?: string;
|
|
475
|
+
}
|
|
476
|
+
/** Result of a single step */
|
|
477
|
+
interface StepResult {
|
|
478
|
+
stepName: string;
|
|
479
|
+
passed: boolean;
|
|
480
|
+
assertionResults: AssertionResult[];
|
|
481
|
+
durationMs: number;
|
|
482
|
+
error?: string;
|
|
483
|
+
}
|
|
484
|
+
/** Result of a single scenario */
|
|
485
|
+
interface ScenarioResult {
|
|
486
|
+
scenarioName: string;
|
|
487
|
+
passed: boolean;
|
|
488
|
+
stepResults: StepResult[];
|
|
489
|
+
durationMs: number;
|
|
490
|
+
error?: string;
|
|
491
|
+
}
|
|
492
|
+
/** Result of a complete test program */
|
|
493
|
+
interface TestProgramResult {
|
|
494
|
+
passed: boolean;
|
|
495
|
+
scenarioResults: ScenarioResult[];
|
|
496
|
+
durationMs: number;
|
|
497
|
+
analysis?: AnalysisResult;
|
|
498
|
+
}
|
|
499
|
+
/** Configuration for the in-process test runner */
|
|
500
|
+
interface TestRunnerConfig {
|
|
501
|
+
actionHandlers?: Record<string, ActionHandlerFn>;
|
|
502
|
+
onStepComplete?: (scenarioIdx: number, step: StepResult) => void;
|
|
503
|
+
onScenarioComplete?: (result: ScenarioResult) => void;
|
|
504
|
+
abortSignal?: AbortSignal;
|
|
505
|
+
}
|
|
506
|
+
/** Snapshot of a server-side workflow instance */
|
|
507
|
+
interface ApiInstanceSnapshot {
|
|
508
|
+
id: string;
|
|
509
|
+
currentState: string;
|
|
510
|
+
status: string;
|
|
511
|
+
stateData: Record<string, unknown>;
|
|
512
|
+
availableTransitions: string[];
|
|
513
|
+
}
|
|
514
|
+
/** Result of triggering a transition via API */
|
|
515
|
+
interface ApiTransitionResult {
|
|
516
|
+
success: boolean;
|
|
517
|
+
fromState: string;
|
|
518
|
+
toState: string;
|
|
519
|
+
error?: string;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Adapter interface for the API test runner.
|
|
523
|
+
* Platform-agnostic — implement with fetch, axios, or any HTTP client.
|
|
524
|
+
* The server (NestJS backend) is the MM Player; the adapter is the client.
|
|
525
|
+
*/
|
|
526
|
+
interface ApiTestAdapter {
|
|
527
|
+
createInstance(params: {
|
|
528
|
+
definitionSlug: string;
|
|
529
|
+
stateData?: Record<string, unknown>;
|
|
530
|
+
entityType?: string;
|
|
531
|
+
entityId?: string;
|
|
532
|
+
}): Promise<ApiInstanceSnapshot>;
|
|
533
|
+
startInstance(instanceId: string): Promise<ApiInstanceSnapshot>;
|
|
534
|
+
deleteInstance(instanceId: string): Promise<void>;
|
|
535
|
+
triggerTransition(instanceId: string, transitionName: string, data?: Record<string, unknown>): Promise<ApiTransitionResult>;
|
|
536
|
+
updateStateData(instanceId: string, data: Record<string, unknown>): Promise<ApiInstanceSnapshot>;
|
|
537
|
+
getInstance(instanceId: string): Promise<ApiInstanceSnapshot>;
|
|
538
|
+
}
|
|
539
|
+
/** Configuration for the API test runner */
|
|
540
|
+
interface ApiTestRunnerConfig {
|
|
541
|
+
adapter: ApiTestAdapter;
|
|
542
|
+
/** Delay (ms) after actions to let EventRouter settle cross-instance reactions. Default: 200 */
|
|
543
|
+
settleDelayMs?: number;
|
|
544
|
+
/** Delete instances after scenario completes. Default: true */
|
|
545
|
+
cleanupInstances?: boolean;
|
|
546
|
+
onStepComplete?: (scenarioIdx: number, step: StepResult) => void;
|
|
547
|
+
onScenarioComplete?: (result: ScenarioResult) => void;
|
|
548
|
+
/** Called when a real instance is created on the server */
|
|
549
|
+
onInstanceCreated?: (testInstanceId: string, serverId: string) => void;
|
|
550
|
+
abortSignal?: AbortSignal;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Graph Walker — static analysis of workflow definitions.
|
|
555
|
+
*
|
|
556
|
+
* Treats a PlayerWorkflowDefinition as a directed graph and provides:
|
|
557
|
+
* - analyzeDefinition(): Full graph analysis (states, edges, paths, cycles)
|
|
558
|
+
* - generateCoverageScenarios(): Auto-generate test scenarios from paths
|
|
559
|
+
*
|
|
560
|
+
* Pure functions, zero dependencies beyond player-core types.
|
|
561
|
+
*/
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Analyze a workflow definition as a directed graph.
|
|
565
|
+
* Returns all states, edges, terminal paths, cycles, unreachable/dead-end states.
|
|
566
|
+
*/
|
|
567
|
+
declare function analyzeDefinition(def: PlayerWorkflowDefinition): AnalysisResult;
|
|
568
|
+
/**
|
|
569
|
+
* Generate one test scenario per terminal path in the definition.
|
|
570
|
+
* Each step = one transition, asserting the landing state.
|
|
571
|
+
*/
|
|
572
|
+
declare function generateCoverageScenarios(def: PlayerWorkflowDefinition, analysis?: AnalysisResult): TestScenario[];
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Test Runner — executes test programs against the player-core engine.
|
|
576
|
+
*
|
|
577
|
+
* Runs test scenarios in-memory using StateMachine + EventBus.
|
|
578
|
+
* No database, no network, no UI — pure state machine execution.
|
|
579
|
+
*
|
|
580
|
+
* Exports:
|
|
581
|
+
* - runTestProgram(): Execute a full test program (all scenarios)
|
|
582
|
+
* - runScenario(): Execute a single scenario
|
|
583
|
+
*/
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Execute a complete test program (all scenarios).
|
|
587
|
+
*/
|
|
588
|
+
declare function runTestProgram(program: TestProgram, config?: TestRunnerConfig): Promise<TestProgramResult>;
|
|
589
|
+
/**
|
|
590
|
+
* Execute a single test scenario.
|
|
591
|
+
*/
|
|
592
|
+
declare function runScenario(scenario: TestScenario, definitions: Record<string, PlayerWorkflowDefinition>, config?: TestRunnerConfig): Promise<ScenarioResult>;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Test Compiler — converts TestProgram → PlayerWorkflowDefinition.
|
|
596
|
+
*
|
|
597
|
+
* The test program IS a Blueprint. This compiler takes the ergonomic
|
|
598
|
+
* TestProgram format (scenarios, steps, assertions) and produces a
|
|
599
|
+
* standard PlayerWorkflowDefinition that any Player can execute.
|
|
600
|
+
*
|
|
601
|
+
* The compiled Blueprint uses test-specific action types (__test_*)
|
|
602
|
+
* in on_enter hooks. The Player executes these via registered action
|
|
603
|
+
* handlers — which are platform-specific (in-process, API, cross-platform).
|
|
604
|
+
*
|
|
605
|
+
* Structure of a compiled scenario Blueprint:
|
|
606
|
+
* __setup (START) → on_enter creates instances
|
|
607
|
+
* __step_0 (REGULAR) → on_enter performs action + assertions
|
|
608
|
+
* __step_1 (REGULAR) → ...
|
|
609
|
+
* __pass (END) → test passed
|
|
610
|
+
* __fail (CANCELLED) → test failed
|
|
611
|
+
*
|
|
612
|
+
* Transitions: all named 'next' with different from states.
|
|
613
|
+
* The runner drives by calling sm.transition('next') in a loop.
|
|
614
|
+
* On failure: sm.transition('abort') → __fail.
|
|
615
|
+
*/
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Compile a single TestScenario into a PlayerWorkflowDefinition (Blueprint).
|
|
619
|
+
*
|
|
620
|
+
* The compiled Blueprint can be:
|
|
621
|
+
* - Stored in the database
|
|
622
|
+
* - Serialized to JSON / .mm file
|
|
623
|
+
* - Executed on any Player (browser, iOS, server)
|
|
624
|
+
* - Generated by an LLM
|
|
625
|
+
*/
|
|
626
|
+
declare function compileTestScenario(scenario: TestScenario, definitions: Record<string, unknown>, scenarioIndex?: number): PlayerWorkflowDefinition;
|
|
627
|
+
/**
|
|
628
|
+
* Compile all scenarios in a TestProgram into Blueprints.
|
|
629
|
+
*/
|
|
630
|
+
declare function compileTestProgram(program: TestProgram): PlayerWorkflowDefinition[];
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Test Action Handlers — pluggable capabilities for test Blueprints.
|
|
634
|
+
*
|
|
635
|
+
* The test Blueprint uses __test_* action types. Which action handlers
|
|
636
|
+
* are registered determines HOW the test executes:
|
|
637
|
+
*
|
|
638
|
+
* - In-process: creates StateMachines in memory, sub-millisecond
|
|
639
|
+
* - API: makes HTTP calls to a running NestJS backend (MM Server Player)
|
|
640
|
+
* - Cross-platform (future): orchestrates across multiple Players
|
|
641
|
+
*
|
|
642
|
+
* Same Blueprint → different capabilities → same results.
|
|
643
|
+
*
|
|
644
|
+
* Architecture (API mode):
|
|
645
|
+
* Player (browser/iOS) executes Test Blueprint
|
|
646
|
+
* → __test_transition action handler
|
|
647
|
+
* → HTTP POST /workflow/instances/:id/transitions/:name
|
|
648
|
+
* → NestJS Backend (MM Server Player)
|
|
649
|
+
* → EventRouter handles cross-instance reactions
|
|
650
|
+
*/
|
|
651
|
+
|
|
652
|
+
interface TestActionState {
|
|
653
|
+
/** All step results collected during execution */
|
|
654
|
+
stepResults: StepResult[];
|
|
655
|
+
/** Whether any step has failed */
|
|
656
|
+
failed: boolean;
|
|
657
|
+
/** Current step tracking */
|
|
658
|
+
currentStep: {
|
|
659
|
+
name: string;
|
|
660
|
+
index: number;
|
|
661
|
+
startTime: number;
|
|
662
|
+
assertions: AssertionResult[];
|
|
663
|
+
error?: string;
|
|
664
|
+
} | null;
|
|
665
|
+
/** Set field on the orchestrator StateMachine (bound after creation) */
|
|
666
|
+
setOrchestratorField: (field: string, value: unknown) => void;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Create action handlers for in-process test execution.
|
|
670
|
+
*
|
|
671
|
+
* Test instances are StateMachines running in memory.
|
|
672
|
+
* Cross-instance reactions via shared EventBus.
|
|
673
|
+
* Sub-millisecond execution.
|
|
674
|
+
*/
|
|
675
|
+
declare function createInProcessTestActions(definitions: Record<string, PlayerWorkflowDefinition>): {
|
|
676
|
+
handlers: Map<string, ActionHandler>;
|
|
677
|
+
state: TestActionState;
|
|
678
|
+
};
|
|
679
|
+
/**
|
|
680
|
+
* Create action handlers for API test execution.
|
|
681
|
+
*
|
|
682
|
+
* Test instances are real DB-backed workflow instances on the NestJS backend.
|
|
683
|
+
* The backend's EventRouter handles cross-instance reactions automatically.
|
|
684
|
+
* Settle delay allows async reactions to complete before assertions.
|
|
685
|
+
*/
|
|
686
|
+
declare function createApiTestActions(adapter: ApiTestAdapter, options?: {
|
|
687
|
+
settleDelayMs?: number;
|
|
688
|
+
}): {
|
|
689
|
+
handlers: Map<string, ActionHandler>;
|
|
690
|
+
state: TestActionState;
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Blueprint Test Runner — executes test programs AS Blueprints on a Player.
|
|
695
|
+
*
|
|
696
|
+
* The test orchestrator IS a Player. This runner:
|
|
697
|
+
* 1. Compiles TestProgram → PlayerWorkflowDefinition (Blueprint)
|
|
698
|
+
* 2. Creates a StateMachine (Player) with the compiled Blueprint
|
|
699
|
+
* 3. Registers test action handlers (in-process or API)
|
|
700
|
+
* 4. Drives the StateMachine through transitions: next, next, next...
|
|
701
|
+
* 5. Collects results from the action handler state
|
|
702
|
+
*
|
|
703
|
+
* The compiled Blueprint can also be extracted, stored, and executed
|
|
704
|
+
* independently on any Player (browser, iOS, server).
|
|
705
|
+
*
|
|
706
|
+
* Exports:
|
|
707
|
+
* - runBlueprintTestProgram(): Full program execution
|
|
708
|
+
* - runBlueprintScenario(): Single scenario execution
|
|
709
|
+
* - Supports both in-process and API modes via action handler factory
|
|
710
|
+
*/
|
|
711
|
+
|
|
712
|
+
interface BlueprintTestConfig {
|
|
713
|
+
onStepComplete?: (scenarioIdx: number, step: StepResult) => void;
|
|
714
|
+
onScenarioComplete?: (result: ScenarioResult) => void;
|
|
715
|
+
abortSignal?: AbortSignal;
|
|
716
|
+
}
|
|
717
|
+
interface InProcessConfig extends BlueprintTestConfig {
|
|
718
|
+
mode: 'in-process';
|
|
719
|
+
}
|
|
720
|
+
interface ApiConfig extends BlueprintTestConfig {
|
|
721
|
+
mode: 'api';
|
|
722
|
+
adapter: ApiTestAdapter;
|
|
723
|
+
settleDelayMs?: number;
|
|
724
|
+
cleanupInstances?: boolean;
|
|
725
|
+
}
|
|
726
|
+
type BlueprintRunnerConfig = InProcessConfig | ApiConfig;
|
|
727
|
+
/**
|
|
728
|
+
* Execute a complete test program using the Blueprint runner.
|
|
729
|
+
*
|
|
730
|
+
* The test program is compiled to Blueprints and executed on a Player
|
|
731
|
+
* (StateMachine) with the appropriate action handlers for the mode.
|
|
732
|
+
*/
|
|
733
|
+
declare function runBlueprintTestProgram(program: TestProgram, config: BlueprintRunnerConfig): Promise<TestProgramResult>;
|
|
734
|
+
/**
|
|
735
|
+
* Execute a single scenario as a Blueprint on a Player.
|
|
736
|
+
*/
|
|
737
|
+
declare function runBlueprintScenario(scenario: TestScenario, definitions: Record<string, PlayerWorkflowDefinition>, scenarioIndex: number, config: BlueprintRunnerConfig): Promise<ScenarioResult>;
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* NRT (Normalized Render Trace) — platform-agnostic representation of rendered UI.
|
|
741
|
+
*
|
|
742
|
+
* NRT captures the semantic structure of a rendered workflow view,
|
|
743
|
+
* abstracting away platform-specific rendering details. This enables:
|
|
744
|
+
*
|
|
745
|
+
* 1. Cross-platform testing (browser, iOS, terminal all produce same NRT)
|
|
746
|
+
* 2. Visual regression detection without screenshots
|
|
747
|
+
* 3. Accessibility auditing (every visible element has semantic info)
|
|
748
|
+
* 4. LLM-based test generation (structured, predictable format)
|
|
749
|
+
*
|
|
750
|
+
* Usage:
|
|
751
|
+
* const nrt = exportNRT(renderedTree);
|
|
752
|
+
* expect(nrt.root.children).toHaveLength(3);
|
|
753
|
+
* expect(findNode(nrt, 'submit-button').events?.click.target.transition).toBe('submit');
|
|
754
|
+
*/
|
|
755
|
+
/**
|
|
756
|
+
* A complete Normalized Render Trace snapshot.
|
|
757
|
+
*/
|
|
758
|
+
interface NRT {
|
|
759
|
+
/** NRT format version. */
|
|
760
|
+
version: 1;
|
|
761
|
+
/** The root node of the rendered tree. */
|
|
762
|
+
root: NRTNode;
|
|
763
|
+
/** Currently focused element ID (if applicable). */
|
|
764
|
+
focus?: string;
|
|
765
|
+
/** Navigation state (if router workflow is active). */
|
|
766
|
+
nav?: NRTNavState;
|
|
767
|
+
/** Timestamp of the snapshot. */
|
|
768
|
+
timestamp?: string;
|
|
769
|
+
/** Workflow state at time of capture. */
|
|
770
|
+
workflowState?: string;
|
|
771
|
+
/** Instance ID if bound to a specific instance. */
|
|
772
|
+
instanceId?: string;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* A single node in the NRT tree.
|
|
776
|
+
*/
|
|
777
|
+
interface NRTNode {
|
|
778
|
+
/** Unique ID within the tree. */
|
|
779
|
+
id: string;
|
|
780
|
+
/** Component type (e.g., 'Stack', 'Button', 'Text', 'TextInput'). */
|
|
781
|
+
type: string;
|
|
782
|
+
/** Text content (for text-bearing elements). */
|
|
783
|
+
text?: string;
|
|
784
|
+
/** Current value (for input elements). */
|
|
785
|
+
value?: unknown;
|
|
786
|
+
/** Whether this node is visible. */
|
|
787
|
+
visible: boolean;
|
|
788
|
+
/** Whether this node is enabled/interactive. */
|
|
789
|
+
enabled?: boolean;
|
|
790
|
+
/** Whether this node has focus. */
|
|
791
|
+
focused?: boolean;
|
|
792
|
+
/** Semantic role (for accessibility). */
|
|
793
|
+
role?: string;
|
|
794
|
+
/** Accessibility label. */
|
|
795
|
+
label?: string;
|
|
796
|
+
/** Key-value props that affect behavior (not styling). */
|
|
797
|
+
props?: Record<string, unknown>;
|
|
798
|
+
/** Interactive events this node can receive. */
|
|
799
|
+
events?: Record<string, NRTEvent>;
|
|
800
|
+
/** Child nodes. */
|
|
801
|
+
children: NRTNode[];
|
|
802
|
+
/** Data binding expression (if bound to workflow data). */
|
|
803
|
+
binding?: string;
|
|
804
|
+
/** Condition that controls visibility. */
|
|
805
|
+
visibleWhen?: string;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* An interactive event on an NRT node.
|
|
809
|
+
*/
|
|
810
|
+
interface NRTEvent {
|
|
811
|
+
/** What kind of action this event triggers. */
|
|
812
|
+
kind: 'transition' | 'mutation' | 'navigation' | 'custom';
|
|
813
|
+
/** Target workflow operation. */
|
|
814
|
+
target: NRTEventTarget;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Target of an NRT event.
|
|
818
|
+
*/
|
|
819
|
+
interface NRTEventTarget {
|
|
820
|
+
/** Workflow slug. */
|
|
821
|
+
workflow?: string;
|
|
822
|
+
/** Transition name to fire. */
|
|
823
|
+
transition?: string;
|
|
824
|
+
/** Mutation type (create, update, delete). */
|
|
825
|
+
mutation?: string;
|
|
826
|
+
/** Navigation path. */
|
|
827
|
+
path?: string;
|
|
828
|
+
/** Custom handler name. */
|
|
829
|
+
handler?: string;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Navigation state for router-bound NRT.
|
|
833
|
+
*/
|
|
834
|
+
interface NRTNavState {
|
|
835
|
+
/** Current route state name. */
|
|
836
|
+
routeState: string;
|
|
837
|
+
/** Current URL path. */
|
|
838
|
+
path: string;
|
|
839
|
+
/** Route parameters. */
|
|
840
|
+
params: Record<string, string>;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Result of comparing two NRT snapshots.
|
|
844
|
+
*/
|
|
845
|
+
interface NRTDiff {
|
|
846
|
+
/** Whether the two snapshots are semantically equivalent. */
|
|
847
|
+
equal: boolean;
|
|
848
|
+
/** List of differences found. */
|
|
849
|
+
changes: NRTChange[];
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* A single difference between two NRT snapshots.
|
|
853
|
+
*/
|
|
854
|
+
interface NRTChange {
|
|
855
|
+
/** Type of change. */
|
|
856
|
+
type: 'added' | 'removed' | 'modified';
|
|
857
|
+
/** Path to the changed node (e.g., 'root.children[0].children[1]'). */
|
|
858
|
+
path: string;
|
|
859
|
+
/** Node ID if available. */
|
|
860
|
+
nodeId?: string;
|
|
861
|
+
/** What changed. */
|
|
862
|
+
field: string;
|
|
863
|
+
/** Previous value (for modified/removed). */
|
|
864
|
+
oldValue?: unknown;
|
|
865
|
+
/** New value (for modified/added). */
|
|
866
|
+
newValue?: unknown;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Creates an empty NRT snapshot.
|
|
870
|
+
*/
|
|
871
|
+
declare function createEmptyNRT(): NRT;
|
|
872
|
+
/**
|
|
873
|
+
* Finds a node by ID in the NRT tree.
|
|
874
|
+
*/
|
|
875
|
+
declare function findNode(nrt: NRT, nodeId: string): NRTNode | undefined;
|
|
876
|
+
/**
|
|
877
|
+
* Finds all visible nodes in the NRT tree.
|
|
878
|
+
*/
|
|
879
|
+
declare function findVisibleNodes(nrt: NRT): NRTNode[];
|
|
880
|
+
/**
|
|
881
|
+
* Finds all interactive nodes (nodes with events) in the NRT tree.
|
|
882
|
+
*/
|
|
883
|
+
declare function findInteractiveNodes(nrt: NRT): NRTNode[];
|
|
884
|
+
/**
|
|
885
|
+
* Counts total nodes in the NRT tree.
|
|
886
|
+
*/
|
|
887
|
+
declare function countNodes(nrt: NRT): number;
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* NRT Comparator — semantic diff between two NRT snapshots.
|
|
891
|
+
*
|
|
892
|
+
* Compares structure, text, visibility, and events while ignoring
|
|
893
|
+
* platform-specific details like classNames, styles, and layout metrics.
|
|
894
|
+
*
|
|
895
|
+
* Usage:
|
|
896
|
+
* const diff = compareNRT(before, after);
|
|
897
|
+
* if (!diff.equal) {
|
|
898
|
+
* console.log('Changes:', diff.changes);
|
|
899
|
+
* }
|
|
900
|
+
*/
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Compares two NRT snapshots and returns a semantic diff.
|
|
904
|
+
*/
|
|
905
|
+
declare function compareNRT(before: NRT, after: NRT): NRTDiff;
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Action Trace — records all workflow actions during execution.
|
|
909
|
+
*
|
|
910
|
+
* Captures a chronological trace of every transition, action, timer,
|
|
911
|
+
* and query that occurs during a workflow session. This enables:
|
|
912
|
+
*
|
|
913
|
+
* 1. Replay debugging (step through recorded actions)
|
|
914
|
+
* 2. Test assertions on action sequences
|
|
915
|
+
* 3. Performance profiling (timing of each tick)
|
|
916
|
+
* 4. Compliance auditing (complete action log)
|
|
917
|
+
*
|
|
918
|
+
* Usage:
|
|
919
|
+
* const recorder = createActionRecorder();
|
|
920
|
+
* // ... hook into workflow runtime ...
|
|
921
|
+
* const trace = recorder.getTrace();
|
|
922
|
+
* expect(trace.ticks).toHaveLength(3);
|
|
923
|
+
* expect(trace.ticks[0].kind).toBe('transition');
|
|
924
|
+
*/
|
|
925
|
+
/**
|
|
926
|
+
* A complete action trace for a workflow session.
|
|
927
|
+
*/
|
|
928
|
+
interface ActionTrace {
|
|
929
|
+
/** Trace format version. */
|
|
930
|
+
version: 1;
|
|
931
|
+
/** Workflow definition slug. */
|
|
932
|
+
workflowSlug: string;
|
|
933
|
+
/** Instance ID (if tracking a specific instance). */
|
|
934
|
+
instanceId?: string;
|
|
935
|
+
/** When the trace started. */
|
|
936
|
+
startedAt: string;
|
|
937
|
+
/** When the trace ended (if finalized). */
|
|
938
|
+
endedAt?: string;
|
|
939
|
+
/** Ordered list of action ticks. */
|
|
940
|
+
ticks: ActionTick[];
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* A single action tick in the trace.
|
|
944
|
+
*/
|
|
945
|
+
interface ActionTick {
|
|
946
|
+
/** Monotonically increasing tick number. */
|
|
947
|
+
tick: number;
|
|
948
|
+
/** ISO timestamp of the tick. */
|
|
949
|
+
timestamp: string;
|
|
950
|
+
/** Kind of action. */
|
|
951
|
+
kind: 'transition' | 'action' | 'timer' | 'query' | 'mutation' | 'event' | 'error';
|
|
952
|
+
/** Name/identifier of the action. */
|
|
953
|
+
name: string;
|
|
954
|
+
/** Duration in milliseconds (if measurable). */
|
|
955
|
+
durationMs?: number;
|
|
956
|
+
/** Workflow state before this tick. */
|
|
957
|
+
fromState?: string;
|
|
958
|
+
/** Workflow state after this tick. */
|
|
959
|
+
toState?: string;
|
|
960
|
+
/** Detailed information about the tick. */
|
|
961
|
+
detail: Record<string, unknown>;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Action recorder — accumulates ticks into a trace.
|
|
965
|
+
*/
|
|
966
|
+
interface ActionRecorder {
|
|
967
|
+
/** Record a transition. */
|
|
968
|
+
recordTransition: (name: string, fromState: string, toState: string, detail?: Record<string, unknown>) => void;
|
|
969
|
+
/** Record an action execution. */
|
|
970
|
+
recordAction: (name: string, detail?: Record<string, unknown>) => void;
|
|
971
|
+
/** Record a timer event. */
|
|
972
|
+
recordTimer: (name: string, detail?: Record<string, unknown>) => void;
|
|
973
|
+
/** Record a data query. */
|
|
974
|
+
recordQuery: (name: string, detail?: Record<string, unknown>) => void;
|
|
975
|
+
/** Record a mutation. */
|
|
976
|
+
recordMutation: (name: string, detail?: Record<string, unknown>) => void;
|
|
977
|
+
/** Record an event. */
|
|
978
|
+
recordEvent: (name: string, detail?: Record<string, unknown>) => void;
|
|
979
|
+
/** Record an error. */
|
|
980
|
+
recordError: (name: string, detail?: Record<string, unknown>) => void;
|
|
981
|
+
/** Get the current trace. */
|
|
982
|
+
getTrace: () => ActionTrace;
|
|
983
|
+
/** Finalize the trace (sets endedAt). */
|
|
984
|
+
finalize: () => ActionTrace;
|
|
985
|
+
/** Reset the recorder. */
|
|
986
|
+
reset: () => void;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Creates a new action recorder.
|
|
990
|
+
*/
|
|
991
|
+
declare function createActionRecorder(workflowSlug: string, instanceId?: string): ActionRecorder;
|
|
992
|
+
/**
|
|
993
|
+
* Filters a trace to only transition ticks.
|
|
994
|
+
*/
|
|
995
|
+
declare function getTransitionPath(trace: ActionTrace): string[];
|
|
996
|
+
/**
|
|
997
|
+
* Gets the final state from a trace.
|
|
998
|
+
*/
|
|
999
|
+
declare function getFinalState(trace: ActionTrace): string | undefined;
|
|
1000
|
+
/**
|
|
1001
|
+
* Counts ticks by kind.
|
|
1002
|
+
*/
|
|
1003
|
+
declare function countByKind(trace: ActionTrace): Record<string, number>;
|
|
1004
|
+
/**
|
|
1005
|
+
* Checks if a trace contains a specific transition.
|
|
1006
|
+
*/
|
|
1007
|
+
declare function hasTransition(trace: ActionTrace, from: string, to: string): boolean;
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* IR Types — the complete Intermediate Representation contract.
|
|
1011
|
+
*
|
|
1012
|
+
* These types mirror the MM-IR Reference (0-MM-IR-REFERENCE.md).
|
|
1013
|
+
* Standalone — no imports from backend/frontend packages.
|
|
1014
|
+
* The compiler transforms AST nodes into these shapes.
|
|
1015
|
+
*/
|
|
1016
|
+
type IRStateType = 'START' | 'REGULAR' | 'END' | 'CANCELLED';
|
|
1017
|
+
type IRActionMode = 'auto' | 'manual';
|
|
1018
|
+
/**
|
|
1019
|
+
* Field type is an open string slug referencing an Element definition.
|
|
1020
|
+
*
|
|
1021
|
+
* Canonical seed types: text, rich_text, number, boolean, date, datetime,
|
|
1022
|
+
* select, multi_select, url, email, phone, currency, percentage, rating,
|
|
1023
|
+
* duration, color, file, image, link_to_one, link_to_many, formula, rollup,
|
|
1024
|
+
* count, lookup, created_at, updated_at, created_by, auto_number, barcode.
|
|
1025
|
+
*
|
|
1026
|
+
* User-defined types use slugs like 'my-org/custom-address'.
|
|
1027
|
+
* The 14 original types remain valid — this is backward compatible.
|
|
1028
|
+
*/
|
|
1029
|
+
type IRWorkflowFieldType = string;
|
|
1030
|
+
type RuntimeProfile = 'backend' | 'collaborative' | 'p2p' | 'local' | 'ephemeral' | 'edge';
|
|
1031
|
+
interface IRStateHome {
|
|
1032
|
+
scope: 'ephemeral' | 'route' | 'instance' | 'global';
|
|
1033
|
+
persistence: 'none' | 'local' | 'durable';
|
|
1034
|
+
sync: 'none' | 'tenant' | 'p2p';
|
|
1035
|
+
}
|
|
1036
|
+
interface IRActionDefinition {
|
|
1037
|
+
id: string;
|
|
1038
|
+
type: string;
|
|
1039
|
+
mode: IRActionMode;
|
|
1040
|
+
config: Record<string, unknown>;
|
|
1041
|
+
condition?: string;
|
|
1042
|
+
}
|
|
1043
|
+
interface IRDuringAction {
|
|
1044
|
+
id: string;
|
|
1045
|
+
type: 'interval' | 'timeout' | 'poll' | 'once' | 'cron';
|
|
1046
|
+
interval_ms?: number;
|
|
1047
|
+
cron?: string;
|
|
1048
|
+
delay_ms?: number;
|
|
1049
|
+
actions: IRActionDefinition[];
|
|
1050
|
+
condition?: string;
|
|
1051
|
+
}
|
|
1052
|
+
interface IROnEventAction {
|
|
1053
|
+
type: 'set_field' | 'set_memory' | 'log_event' | 'create_instance';
|
|
1054
|
+
field?: string;
|
|
1055
|
+
expression?: string;
|
|
1056
|
+
key?: string;
|
|
1057
|
+
message?: string;
|
|
1058
|
+
config?: Record<string, unknown>;
|
|
1059
|
+
conditions?: string[];
|
|
1060
|
+
}
|
|
1061
|
+
interface IROnEventSubscription {
|
|
1062
|
+
match: string;
|
|
1063
|
+
description?: string;
|
|
1064
|
+
conditions?: string[];
|
|
1065
|
+
actions: IROnEventAction[];
|
|
1066
|
+
}
|
|
1067
|
+
interface IRStateDefinition {
|
|
1068
|
+
name: string;
|
|
1069
|
+
description?: string;
|
|
1070
|
+
type: IRStateType;
|
|
1071
|
+
on_enter: IRActionDefinition[];
|
|
1072
|
+
during: IRDuringAction[];
|
|
1073
|
+
on_exit: IRActionDefinition[];
|
|
1074
|
+
on_event?: IROnEventSubscription[];
|
|
1075
|
+
}
|
|
1076
|
+
interface IRConditionDefinition {
|
|
1077
|
+
type?: string;
|
|
1078
|
+
field?: string;
|
|
1079
|
+
operator?: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'contains' | 'is_set' | 'is_empty';
|
|
1080
|
+
value?: unknown;
|
|
1081
|
+
expression?: string;
|
|
1082
|
+
OR?: IRConditionDefinition[];
|
|
1083
|
+
AND?: IRConditionDefinition[];
|
|
1084
|
+
}
|
|
1085
|
+
interface IRTransitionDefinition {
|
|
1086
|
+
name: string;
|
|
1087
|
+
description?: string;
|
|
1088
|
+
from: string[];
|
|
1089
|
+
to: string;
|
|
1090
|
+
conditions?: IRConditionDefinition[];
|
|
1091
|
+
actions: IRActionDefinition[];
|
|
1092
|
+
roles?: string[];
|
|
1093
|
+
auto?: boolean;
|
|
1094
|
+
required_fields?: string[];
|
|
1095
|
+
}
|
|
1096
|
+
interface IRFieldValidation {
|
|
1097
|
+
min?: number;
|
|
1098
|
+
max?: number;
|
|
1099
|
+
minLength?: number;
|
|
1100
|
+
maxLength?: number;
|
|
1101
|
+
options?: string[];
|
|
1102
|
+
rules?: Array<{
|
|
1103
|
+
expression: string;
|
|
1104
|
+
message: string;
|
|
1105
|
+
severity: 'error' | 'warning';
|
|
1106
|
+
}>;
|
|
1107
|
+
}
|
|
1108
|
+
interface IRFieldDefinition {
|
|
1109
|
+
name: string;
|
|
1110
|
+
type: IRWorkflowFieldType;
|
|
1111
|
+
/** Version pin: { type, typeVersion } is canonical storage/API form */
|
|
1112
|
+
typeVersion?: string;
|
|
1113
|
+
/** Structural inheritance: storage + widget + primitive intrinsics from base type */
|
|
1114
|
+
baseType?: string;
|
|
1115
|
+
label?: string;
|
|
1116
|
+
required?: boolean;
|
|
1117
|
+
default_value?: unknown;
|
|
1118
|
+
validation?: IRFieldValidation;
|
|
1119
|
+
computed?: string;
|
|
1120
|
+
computed_deps?: string[];
|
|
1121
|
+
visible_in_states?: string[];
|
|
1122
|
+
editable_in_states?: string[];
|
|
1123
|
+
visible_to_roles?: string[];
|
|
1124
|
+
editable_by_roles?: string[];
|
|
1125
|
+
visible_when?: string;
|
|
1126
|
+
editable_when?: string;
|
|
1127
|
+
state_home?: IRStateHome;
|
|
1128
|
+
}
|
|
1129
|
+
interface IRRoleDefinition {
|
|
1130
|
+
name: string;
|
|
1131
|
+
description?: string;
|
|
1132
|
+
permissions: string[];
|
|
1133
|
+
}
|
|
1134
|
+
interface IRWorkflowDefinition {
|
|
1135
|
+
slug: string;
|
|
1136
|
+
name: string;
|
|
1137
|
+
version: string;
|
|
1138
|
+
description?: string;
|
|
1139
|
+
category: string | string[];
|
|
1140
|
+
states: IRStateDefinition[];
|
|
1141
|
+
transitions: IRTransitionDefinition[];
|
|
1142
|
+
fields: IRFieldDefinition[];
|
|
1143
|
+
roles: IRRoleDefinition[];
|
|
1144
|
+
tags?: Array<{
|
|
1145
|
+
tag_name: string;
|
|
1146
|
+
}>;
|
|
1147
|
+
metadata?: Record<string, unknown>;
|
|
1148
|
+
on_event?: IROnEventSubscription[];
|
|
1149
|
+
extensions?: Record<string, IRGrammarIsland[]>;
|
|
1150
|
+
}
|
|
1151
|
+
interface IRGrammarIsland {
|
|
1152
|
+
slug: string;
|
|
1153
|
+
contextTag: string;
|
|
1154
|
+
rawSource: string;
|
|
1155
|
+
parsed?: unknown;
|
|
1156
|
+
}
|
|
1157
|
+
interface IRExperienceNode {
|
|
1158
|
+
id: string;
|
|
1159
|
+
/** User-assigned display name for authoring UIs (e.g., "Hero Section", "Login Form") */
|
|
1160
|
+
displayName?: string;
|
|
1161
|
+
/** Reference to a registered experience definition (prefab instantiation) */
|
|
1162
|
+
experienceId?: string;
|
|
1163
|
+
component?: string;
|
|
1164
|
+
/** Slot placeholder — rendered by collecting contributions from sub-workflows */
|
|
1165
|
+
slot?: string;
|
|
1166
|
+
children?: IRExperienceNode[];
|
|
1167
|
+
dataSources?: IRDataSource[];
|
|
1168
|
+
dataScope?: string;
|
|
1169
|
+
layout?: string;
|
|
1170
|
+
/** CSS class on the wrapper div */
|
|
1171
|
+
className?: string;
|
|
1172
|
+
/** Style IR — structured cross-platform styling (opaque to player-core) */
|
|
1173
|
+
style?: Record<string, unknown>;
|
|
1174
|
+
config?: Record<string, unknown>;
|
|
1175
|
+
bindings?: Record<string, string>;
|
|
1176
|
+
/** Overrides for a referenced experience's defaults */
|
|
1177
|
+
overrides?: Record<string, unknown>;
|
|
1178
|
+
visible_when?: string;
|
|
1179
|
+
}
|
|
1180
|
+
interface IRWorkflowDataSource {
|
|
1181
|
+
type: 'workflow';
|
|
1182
|
+
name: string;
|
|
1183
|
+
slug?: string;
|
|
1184
|
+
query: 'latest' | 'list' | 'count';
|
|
1185
|
+
/** Fetch a specific instance by ID. Supports template interpolation: "{{entity_id}}" */
|
|
1186
|
+
instanceId?: string;
|
|
1187
|
+
/** Entity scope for the query (null = no filtering, omitted = inherited) */
|
|
1188
|
+
entity?: {
|
|
1189
|
+
type: string;
|
|
1190
|
+
id: string;
|
|
1191
|
+
} | null;
|
|
1192
|
+
paginated?: boolean;
|
|
1193
|
+
pageSize?: number;
|
|
1194
|
+
/** Static filters always applied (e.g., { current_state: 'todo' }) */
|
|
1195
|
+
filter?: Record<string, string>;
|
|
1196
|
+
/** Dynamic filters with bind expressions */
|
|
1197
|
+
filters?: Record<string, string | {
|
|
1198
|
+
bind: string;
|
|
1199
|
+
}>;
|
|
1200
|
+
sort?: string;
|
|
1201
|
+
search?: string;
|
|
1202
|
+
searchFields?: string[];
|
|
1203
|
+
facets?: string[];
|
|
1204
|
+
/** Range filters: { field: { min?, max? } } */
|
|
1205
|
+
range?: Record<string, {
|
|
1206
|
+
min?: unknown;
|
|
1207
|
+
max?: unknown;
|
|
1208
|
+
}>;
|
|
1209
|
+
/** Aggregate functions: "field:fn,field:fn" */
|
|
1210
|
+
aggregate?: string;
|
|
1211
|
+
groupBy?: string;
|
|
1212
|
+
parentInstanceId?: string;
|
|
1213
|
+
autoStart?: boolean;
|
|
1214
|
+
initialData?: Record<string, unknown>;
|
|
1215
|
+
includeDefinition?: boolean;
|
|
1216
|
+
}
|
|
1217
|
+
interface IRApiDataSource {
|
|
1218
|
+
type: 'api';
|
|
1219
|
+
name: string;
|
|
1220
|
+
endpoint: string;
|
|
1221
|
+
method?: 'GET' | 'POST';
|
|
1222
|
+
/** Map API response to InstanceData shape */
|
|
1223
|
+
mapping?: {
|
|
1224
|
+
id?: string;
|
|
1225
|
+
state?: string;
|
|
1226
|
+
data?: string;
|
|
1227
|
+
items?: string;
|
|
1228
|
+
total?: string;
|
|
1229
|
+
};
|
|
1230
|
+
staleTime?: number;
|
|
1231
|
+
}
|
|
1232
|
+
interface IRRefDataSource {
|
|
1233
|
+
type: 'ref';
|
|
1234
|
+
name: string;
|
|
1235
|
+
expression: string;
|
|
1236
|
+
}
|
|
1237
|
+
interface IRStaticDataSource {
|
|
1238
|
+
type: 'static';
|
|
1239
|
+
name: string;
|
|
1240
|
+
data: Record<string, unknown> | Record<string, unknown>[];
|
|
1241
|
+
}
|
|
1242
|
+
type IRDataSource = IRWorkflowDataSource | IRApiDataSource | IRRefDataSource | IRStaticDataSource;
|
|
1243
|
+
interface IRExperienceDefinition {
|
|
1244
|
+
slug: string;
|
|
1245
|
+
version: string;
|
|
1246
|
+
name: string;
|
|
1247
|
+
description?: string;
|
|
1248
|
+
category: string | string[];
|
|
1249
|
+
view_definition: IRExperienceNode;
|
|
1250
|
+
workflows: string[];
|
|
1251
|
+
children: string[];
|
|
1252
|
+
data_bindings: unknown[];
|
|
1253
|
+
is_default: boolean;
|
|
1254
|
+
}
|
|
1255
|
+
interface IRBlueprintManifest {
|
|
1256
|
+
workflows: Array<{
|
|
1257
|
+
slug: string;
|
|
1258
|
+
role: 'primary' | 'child' | 'derived' | 'utility';
|
|
1259
|
+
}>;
|
|
1260
|
+
experience_id: string;
|
|
1261
|
+
routes?: Array<{
|
|
1262
|
+
path: string;
|
|
1263
|
+
node: string;
|
|
1264
|
+
entityType?: string;
|
|
1265
|
+
entityIdSource?: 'user' | 'param';
|
|
1266
|
+
}>;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Canonical workflow tree per spec 041 §1.1.
|
|
1270
|
+
* This is the universal type: { slug, category, parts, metadata }.
|
|
1271
|
+
*
|
|
1272
|
+
* Category invariants (INV-1):
|
|
1273
|
+
* category.length >= 1
|
|
1274
|
+
* category[0] = primary (dispatch target, must be IDENT)
|
|
1275
|
+
* category[1..] = tags (sorted bytewise UTF-8, unique, primary not in tags)
|
|
1276
|
+
*/
|
|
1277
|
+
interface PureFormWorkflow {
|
|
1278
|
+
slug: string;
|
|
1279
|
+
category: string[];
|
|
1280
|
+
parts?: PureFormWorkflow[];
|
|
1281
|
+
metadata?: Record<string, unknown>;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Compiled output: canonical tree (truth) + lowered IR (cache).
|
|
1285
|
+
* Per spec 048 §1.2: canonical tree IS the storage format,
|
|
1286
|
+
* IR is a derivable runtime cache.
|
|
1287
|
+
*/
|
|
1288
|
+
interface CompiledOutput {
|
|
1289
|
+
canonical: PureFormWorkflow;
|
|
1290
|
+
ir: IRWorkflowDefinition;
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Normalizes a category array to satisfy INV-1:
|
|
1294
|
+
* - category[0] = primary (unchanged)
|
|
1295
|
+
* - category[1..] = sorted bytewise UTF-8, unique, primary excluded
|
|
1296
|
+
*/
|
|
1297
|
+
declare function normalizeCategory(primary: string, ...tags: string[]): string[];
|
|
1298
|
+
type CompilerErrorCode = 'MISSING_STARTS_AT' | 'UNKNOWN_TARGET_STATE' | 'DUPLICATE_FIELD' | 'DUPLICATE_STATE' | 'UNKNOWN_FIELD_TYPE' | 'INVALID_EXPRESSION' | 'UNKNOWN_FRAGMENT' | 'EMPTY_WORKFLOW' | 'STRICT_USE_EFFECT' | 'STRICT_USE_REF' | 'STRICT_USE_MEMO' | 'STRICT_USE_CALLBACK' | 'STRICT_USE_LAYOUT_EFFECT' | 'STRICT_FORBIDDEN_IMPORT' | 'INFER_RAW_JSX';
|
|
1299
|
+
interface CompilerError {
|
|
1300
|
+
code: CompilerErrorCode;
|
|
1301
|
+
message: string;
|
|
1302
|
+
lineNumber?: number;
|
|
1303
|
+
severity: 'error' | 'warning';
|
|
1304
|
+
}
|
|
1305
|
+
interface CompilationResult {
|
|
1306
|
+
workflows: IRWorkflowDefinition[];
|
|
1307
|
+
experiences: IRExperienceDefinition[];
|
|
1308
|
+
manifest?: IRBlueprintManifest;
|
|
1309
|
+
errors: CompilerError[];
|
|
1310
|
+
warnings: CompilerError[];
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Definition Validator — static validation of compiled IR definitions.
|
|
1315
|
+
*
|
|
1316
|
+
* Validates structural integrity, referential consistency, and semantic
|
|
1317
|
+
* correctness of IRWorkflowDefinition before it reaches the runtime.
|
|
1318
|
+
*
|
|
1319
|
+
* Categories:
|
|
1320
|
+
* - STRUCTURAL: Missing required fields, type mismatches
|
|
1321
|
+
* - REFERENTIAL: Dangling state refs, missing transition targets
|
|
1322
|
+
* - SEMANTIC: Unreachable states, dead transitions, missing START
|
|
1323
|
+
* - EXPERIENCE: Unknown components, invalid bindings, missing data sources
|
|
1324
|
+
*/
|
|
1325
|
+
|
|
1326
|
+
type ValidationSeverity = 'error' | 'warning' | 'info';
|
|
1327
|
+
type ValidationCategory = 'structural' | 'referential' | 'semantic' | 'experience';
|
|
1328
|
+
interface ValidationIssue {
|
|
1329
|
+
/** Unique code for programmatic matching. */
|
|
1330
|
+
code: string;
|
|
1331
|
+
/** Human-readable message. */
|
|
1332
|
+
message: string;
|
|
1333
|
+
/** Severity level. */
|
|
1334
|
+
severity: ValidationSeverity;
|
|
1335
|
+
/** Validation category. */
|
|
1336
|
+
category: ValidationCategory;
|
|
1337
|
+
/** Location path (e.g., 'states[0].on_enter[1]'). */
|
|
1338
|
+
path?: string;
|
|
1339
|
+
/** Suggested fix. */
|
|
1340
|
+
suggestion?: string;
|
|
1341
|
+
}
|
|
1342
|
+
interface ValidationResult {
|
|
1343
|
+
/** Whether the definition passed all error-level checks. */
|
|
1344
|
+
valid: boolean;
|
|
1345
|
+
/** All issues found. */
|
|
1346
|
+
issues: ValidationIssue[];
|
|
1347
|
+
/** Quick access to error count. */
|
|
1348
|
+
errorCount: number;
|
|
1349
|
+
/** Quick access to warning count. */
|
|
1350
|
+
warningCount: number;
|
|
1351
|
+
/** Summary statistics. */
|
|
1352
|
+
summary: {
|
|
1353
|
+
stateCount: number;
|
|
1354
|
+
transitionCount: number;
|
|
1355
|
+
fieldCount: number;
|
|
1356
|
+
hasExperience: boolean;
|
|
1357
|
+
hasExtensions: boolean;
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
interface ValidatorOptions {
|
|
1361
|
+
/** Known component IDs (for experience validation). */
|
|
1362
|
+
knownComponents?: string[];
|
|
1363
|
+
/** Known binding roots (default: $instance, $definition, $instances, $local, $entity, $user, $fn, $action, $item, $index). */
|
|
1364
|
+
knownBindingRoots?: string[];
|
|
1365
|
+
/** Skip experience tree validation. */
|
|
1366
|
+
skipExperience?: boolean;
|
|
1367
|
+
/** Skip semantic analysis (reachability, etc.). */
|
|
1368
|
+
skipSemantic?: boolean;
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Validates an IRWorkflowDefinition for structural, referential,
|
|
1372
|
+
* semantic, and experience correctness.
|
|
1373
|
+
*/
|
|
1374
|
+
declare function validateDefinition(def: IRWorkflowDefinition, options?: ValidatorOptions): ValidationResult;
|
|
1375
|
+
/**
|
|
1376
|
+
* Quick structural check — returns true if the definition has minimum viable structure.
|
|
1377
|
+
* Much faster than full validateDefinition() for use in render-path guards.
|
|
1378
|
+
*/
|
|
1379
|
+
declare function isViableDefinition(def: unknown): def is IRWorkflowDefinition;
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* DSL Compiler — entry point.
|
|
1383
|
+
*
|
|
1384
|
+
* Pipeline: tokenize → parse → collectSymbols → compileWorkflows → compileViews → compileManifest
|
|
1385
|
+
*/
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Compile DSL source text into the full IR.
|
|
1389
|
+
*/
|
|
1390
|
+
declare function compile(source: string): CompilationResult;
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* IR Migration — forward-compatibility layer for workflow definitions.
|
|
1394
|
+
*
|
|
1395
|
+
* Handles differences between IR versions so that older compiled definitions
|
|
1396
|
+
* continue to work with newer runtimes. Each migration transforms from
|
|
1397
|
+
* one version to the next; they chain automatically.
|
|
1398
|
+
*
|
|
1399
|
+
* Version history:
|
|
1400
|
+
* - v1.0: Original format (states use state_type, actions use action_type)
|
|
1401
|
+
* - v1.1: Normalized format (states use type, actions use type, fields use type)
|
|
1402
|
+
* - v1.2: Added extensions, during actions, on_event at workflow level
|
|
1403
|
+
*
|
|
1404
|
+
* The current version is always the latest. Older definitions are migrated
|
|
1405
|
+
* transparently by normalizeDefinition().
|
|
1406
|
+
*/
|
|
1407
|
+
|
|
1408
|
+
declare const CURRENT_IR_VERSION = "1.2";
|
|
1409
|
+
interface MigrationResult {
|
|
1410
|
+
/** The migrated definition. */
|
|
1411
|
+
definition: IRWorkflowDefinition;
|
|
1412
|
+
/** Original IR version detected. */
|
|
1413
|
+
fromVersion: string;
|
|
1414
|
+
/** Target IR version after migration. */
|
|
1415
|
+
toVersion: string;
|
|
1416
|
+
/** Whether any migration was applied. */
|
|
1417
|
+
migrated: boolean;
|
|
1418
|
+
/** Migrations applied (in order). */
|
|
1419
|
+
appliedMigrations: string[];
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Detect the IR version of a definition.
|
|
1423
|
+
* Uses heuristics based on field naming conventions.
|
|
1424
|
+
*/
|
|
1425
|
+
declare function detectIRVersion(def: Record<string, unknown>): string;
|
|
1426
|
+
/**
|
|
1427
|
+
* Normalize a definition to the current IR version.
|
|
1428
|
+
* Applies all necessary migrations in sequence.
|
|
1429
|
+
*/
|
|
1430
|
+
declare function normalizeDefinition(def: Record<string, unknown>): MigrationResult;
|
|
1431
|
+
/**
|
|
1432
|
+
* Check if a definition needs migration.
|
|
1433
|
+
*/
|
|
1434
|
+
declare function needsMigration(def: Record<string, unknown>): boolean;
|
|
1435
|
+
|
|
1436
|
+
export { type ActionDefinition, ActionDispatcher, type ActionHandler, type ActionHandlerFn, type ActionRecorder, type ActionResult, type ActionTick, type ActionTrace, type AnalysisResult, type AnalysisSummary, type ApiInstanceSnapshot, type ApiTestAdapter, type ApiTestRunnerConfig, type ApiTransitionResult, type AssertionResult, type BlueprintRunnerConfig, type BusEvent, CORE_FUNCTIONS, CURRENT_IR_VERSION, type CompilationResult, type CompiledOutput, type CompilerError, type CompilerErrorCode, type Evaluator, type EvaluatorConfig, EventBus, type EventHandler, type EventSubscription, type ExpressionContext, type ExpressionDiagnostics, type ExpressionFunction, type ExpressionResult, type FailurePolicy, type FailurePolicyContext, type GraphPath, type IRActionDefinition, type IRActionMode, type IRApiDataSource, type IRBlueprintManifest, type IRConditionDefinition, type IRDataSource, type IRDuringAction, type IRExperienceDefinition, type IRExperienceNode, type IRFieldDefinition, type IRFieldValidation, type IRGrammarIsland, type IROnEventAction, type IROnEventSubscription, type IRRefDataSource, type IRRoleDefinition, type IRStateDefinition, type IRStateHome, type IRStateType, type IRStaticDataSource, type IRTransitionDefinition, type IRWorkflowDataSource, type IRWorkflowDefinition, type IRWorkflowFieldType, type MigrationResult, type NRT, type NRTChange, type NRTDiff, type NRTEvent, type NRTEventTarget, type NRTNavState, type NRTNode, type PlayerAction, type PlayerInstance, type PlayerOnEventSubscription, type PlayerStateDefinition, type PlayerTransitionDefinition, type PlayerWorkflowDefinition, type PureFormWorkflow, type RuntimeProfile, type ScenarioResult, StateMachine, type StateMachineConfig, type StateMachineEvent, type StateMachineListener, type StateNode, type StateType, type StepResult, type TestActionState, type TestAssertion, type TestInstance, type TestProgram, type TestProgramResult, type TestRunnerConfig, type TestScenario, type TestStep, type TransitionEdge, type TransitionResult, type Unsubscribe, type ValidationCategory, type ValidationIssue, type ValidationResult, type ValidationSeverity, type ValidatorOptions, WEB_FAILURE_POLICIES, analyzeDefinition, buildFunctionMap, clearExpressionCache, clearPatternCache, compareNRT, compile, compilePattern, compileTestProgram, compileTestScenario, countByKind, countNodes, createActionRecorder, createApiTestActions, createEmptyNRT, createEvaluator, createInProcessTestActions, detectIRVersion, findInteractiveNodes, findNode, findVisibleNodes, generateCoverageScenarios, getFinalState, getTransitionPath, hasTransition, isViableDefinition, matchTopic, needsMigration, normalizeCategory, normalizeDefinition, runBlueprintScenario, runBlueprintTestProgram, runScenario, runTestProgram, validateDefinition };
|