@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.
Files changed (63) hide show
  1. package/dist/index.d.mts +1436 -0
  2. package/dist/index.d.ts +1436 -0
  3. package/dist/index.js +4828 -0
  4. package/dist/index.mjs +4762 -0
  5. package/package.json +35 -0
  6. package/package.json.backup +35 -0
  7. package/src/__tests__/actions.test.ts +187 -0
  8. package/src/__tests__/blueprint-e2e.test.ts +706 -0
  9. package/src/__tests__/blueprint-test-runner.test.ts +680 -0
  10. package/src/__tests__/core-functions.test.ts +78 -0
  11. package/src/__tests__/dsl-compiler.test.ts +1382 -0
  12. package/src/__tests__/dsl-grammar.test.ts +1682 -0
  13. package/src/__tests__/events.test.ts +200 -0
  14. package/src/__tests__/expression.test.ts +296 -0
  15. package/src/__tests__/failure-policies.test.ts +110 -0
  16. package/src/__tests__/frontend-context.test.ts +182 -0
  17. package/src/__tests__/integration.test.ts +256 -0
  18. package/src/__tests__/security.test.ts +190 -0
  19. package/src/__tests__/state-machine.test.ts +450 -0
  20. package/src/__tests__/testing-engine.test.ts +671 -0
  21. package/src/actions/dispatcher.ts +80 -0
  22. package/src/actions/index.ts +7 -0
  23. package/src/actions/types.ts +25 -0
  24. package/src/dsl/compiler/component-mapper.ts +289 -0
  25. package/src/dsl/compiler/field-mapper.ts +187 -0
  26. package/src/dsl/compiler/index.ts +82 -0
  27. package/src/dsl/compiler/manifest-compiler.ts +76 -0
  28. package/src/dsl/compiler/symbol-table.ts +214 -0
  29. package/src/dsl/compiler/utils.ts +48 -0
  30. package/src/dsl/compiler/view-compiler.ts +286 -0
  31. package/src/dsl/compiler/workflow-compiler.ts +600 -0
  32. package/src/dsl/index.ts +66 -0
  33. package/src/dsl/ir-migration.ts +221 -0
  34. package/src/dsl/ir-types.ts +416 -0
  35. package/src/dsl/lexer.ts +579 -0
  36. package/src/dsl/parser.ts +115 -0
  37. package/src/dsl/types.ts +256 -0
  38. package/src/events/event-bus.ts +68 -0
  39. package/src/events/index.ts +9 -0
  40. package/src/events/pattern-matcher.ts +61 -0
  41. package/src/events/types.ts +27 -0
  42. package/src/expression/evaluator.ts +676 -0
  43. package/src/expression/functions.ts +214 -0
  44. package/src/expression/index.ts +13 -0
  45. package/src/expression/types.ts +64 -0
  46. package/src/index.ts +61 -0
  47. package/src/state-machine/index.ts +16 -0
  48. package/src/state-machine/interpreter.ts +319 -0
  49. package/src/state-machine/types.ts +89 -0
  50. package/src/testing/action-trace.ts +209 -0
  51. package/src/testing/blueprint-test-runner.ts +214 -0
  52. package/src/testing/graph-walker.ts +249 -0
  53. package/src/testing/index.ts +69 -0
  54. package/src/testing/nrt-comparator.ts +199 -0
  55. package/src/testing/nrt-types.ts +230 -0
  56. package/src/testing/test-actions.ts +645 -0
  57. package/src/testing/test-compiler.ts +278 -0
  58. package/src/testing/test-runner.ts +444 -0
  59. package/src/testing/types.ts +231 -0
  60. package/src/validation/definition-validator.ts +812 -0
  61. package/src/validation/index.ts +13 -0
  62. package/tsconfig.json +26 -0
  63. package/vitest.config.ts +8 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * AST types for the noun-first DSL grammar.
3
+ *
4
+ * The DSL is line-oriented: each line is one thought, and indentation
5
+ * defines parent-child relationships. This module defines the types
6
+ * for line-level classification and parsed data.
7
+ */
8
+
9
+ // =============================================================================
10
+ // Line Classification
11
+ // =============================================================================
12
+
13
+ export type LineType =
14
+ | 'comment'
15
+ | 'blank'
16
+ | 'space_decl'
17
+ | 'thing_decl'
18
+ | 'thing_ref'
19
+ | 'fragment_def'
20
+ | 'field_def'
21
+ | 'state_decl'
22
+ | 'starts_at'
23
+ | 'transition'
24
+ | 'when'
25
+ | 'set_action'
26
+ | 'do_action'
27
+ | 'go_action'
28
+ | 'tell_action'
29
+ | 'show_action'
30
+ | 'data_source'
31
+ | 'iteration'
32
+ | 'grouping'
33
+ | 'content'
34
+ | 'string_literal'
35
+ | 'search'
36
+ | 'qualifier'
37
+ | 'navigation'
38
+ | 'path_mapping'
39
+ | 'section'
40
+ | 'tagged'
41
+ | 'level_def'
42
+ | 'pages'
43
+ | 'unknown';
44
+
45
+ // =============================================================================
46
+ // Emphasis & Modifiers
47
+ // =============================================================================
48
+
49
+ export type Emphasis = 'big' | 'small';
50
+
51
+ export interface Constraint {
52
+ kind: string; // max, min, default, between, unique
53
+ value: string | number;
54
+ value2?: string | number; // for "between X and Y"
55
+ }
56
+
57
+ // =============================================================================
58
+ // Parsed Line Data (per line type)
59
+ // =============================================================================
60
+
61
+ export interface SpaceDeclData {
62
+ name: string;
63
+ version: string;
64
+ }
65
+
66
+ export interface ThingDeclData {
67
+ name: string;
68
+ version?: string;
69
+ }
70
+
71
+ export interface ThingRefData {
72
+ name: string;
73
+ kind?: string; // primary, child, derived
74
+ }
75
+
76
+ export interface FragmentDefData {
77
+ name: string;
78
+ }
79
+
80
+ export interface FieldDefData {
81
+ name: string;
82
+ adjectives: string[];
83
+ baseType: string;
84
+ constraints: Constraint[];
85
+ }
86
+
87
+ export interface StateDeclData {
88
+ name: string;
89
+ isFinal: boolean;
90
+ }
91
+
92
+ export interface StartsAtData {
93
+ state: string;
94
+ }
95
+
96
+ export interface TransitionData {
97
+ verb: string;
98
+ target: string;
99
+ guard?: string; // role restriction like "admin only"
100
+ }
101
+
102
+ export interface WhenData {
103
+ condition: string;
104
+ }
105
+
106
+ export interface SetActionData {
107
+ field: string;
108
+ expression: string;
109
+ }
110
+
111
+ export interface DoActionData {
112
+ action: string;
113
+ }
114
+
115
+ export interface GoActionData {
116
+ path: string;
117
+ }
118
+
119
+ export interface TellActionData {
120
+ target: string;
121
+ message: string;
122
+ }
123
+
124
+ export interface ShowActionData {
125
+ content: string;
126
+ modifier?: string;
127
+ }
128
+
129
+ export interface DataSourceData {
130
+ alias: string;
131
+ source: string;
132
+ scope?: string; // "for this project"
133
+ isLive?: boolean;
134
+ qualifier?: string; // "5 newest", "current"
135
+ }
136
+
137
+ export interface IterationData {
138
+ subject: string;
139
+ role?: string; // "as card"
140
+ emphasis?: Emphasis;
141
+ }
142
+
143
+ export interface GroupingData {
144
+ collection: string;
145
+ key: string;
146
+ }
147
+
148
+ export interface ContentData {
149
+ pronoun?: string; // its, my, the
150
+ field: string;
151
+ emphasis?: Emphasis;
152
+ role?: string;
153
+ label?: string; // with "XP"
154
+ }
155
+
156
+ export interface StringLiteralData {
157
+ text: string;
158
+ emphasis?: Emphasis;
159
+ }
160
+
161
+ export interface SearchData {
162
+ target: string;
163
+ }
164
+
165
+ export interface QualifierData {
166
+ kind: 'order' | 'searchable' | 'filterable' | 'pagination';
167
+ value: string;
168
+ }
169
+
170
+ export interface NavigationData {
171
+ trigger: string; // tap, label text
172
+ target: string;
173
+ }
174
+
175
+ export interface PathMappingData {
176
+ path: string;
177
+ view: string;
178
+ context?: string;
179
+ }
180
+
181
+ export interface SectionData {
182
+ name: string;
183
+ }
184
+
185
+ export interface TaggedData {
186
+ tags: string[];
187
+ }
188
+
189
+ export interface LevelDefData {
190
+ level: number;
191
+ title: string;
192
+ fromXp: number;
193
+ }
194
+
195
+ // =============================================================================
196
+ // Unified Line Token
197
+ // =============================================================================
198
+
199
+ export type LineData =
200
+ | { type: 'comment'; text: string }
201
+ | { type: 'blank' }
202
+ | { type: 'space_decl' } & SpaceDeclData
203
+ | { type: 'thing_decl' } & ThingDeclData
204
+ | { type: 'thing_ref' } & ThingRefData
205
+ | { type: 'fragment_def' } & FragmentDefData
206
+ | { type: 'field_def' } & FieldDefData
207
+ | { type: 'state_decl' } & StateDeclData
208
+ | { type: 'starts_at' } & StartsAtData
209
+ | { type: 'transition' } & TransitionData
210
+ | { type: 'when' } & WhenData
211
+ | { type: 'set_action' } & SetActionData
212
+ | { type: 'do_action' } & DoActionData
213
+ | { type: 'go_action' } & GoActionData
214
+ | { type: 'tell_action' } & TellActionData
215
+ | { type: 'show_action' } & ShowActionData
216
+ | { type: 'data_source' } & DataSourceData
217
+ | { type: 'iteration' } & IterationData
218
+ | { type: 'grouping' } & GroupingData
219
+ | { type: 'content' } & ContentData
220
+ | { type: 'string_literal' } & StringLiteralData
221
+ | { type: 'search' } & SearchData
222
+ | { type: 'qualifier' } & QualifierData
223
+ | { type: 'navigation' } & NavigationData
224
+ | { type: 'path_mapping' } & PathMappingData
225
+ | { type: 'section' } & SectionData
226
+ | { type: 'tagged' } & TaggedData
227
+ | { type: 'level_def' } & LevelDefData
228
+ | { type: 'pages' }
229
+ | { type: 'unknown'; text: string };
230
+
231
+ export interface LineToken {
232
+ indent: number;
233
+ lineNumber: number;
234
+ raw: string;
235
+ data: LineData;
236
+ }
237
+
238
+ // =============================================================================
239
+ // AST Tree (structural nesting)
240
+ // =============================================================================
241
+
242
+ export interface ASTNode {
243
+ token: LineToken;
244
+ children: ASTNode[];
245
+ }
246
+
247
+ export interface ParseResult {
248
+ nodes: ASTNode[];
249
+ errors: ParseError[];
250
+ }
251
+
252
+ export interface ParseError {
253
+ lineNumber: number;
254
+ message: string;
255
+ raw: string;
256
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Event Bus — browser-side pub/sub for on_event subscriptions.
3
+ *
4
+ * Lightweight event bus that matches published topics against
5
+ * glob-pattern subscriptions. Used by the player to wire
6
+ * on_event declarations to action handlers.
7
+ */
8
+
9
+ import { compilePattern, matchTopic } from './pattern-matcher';
10
+ import type { BusEvent, EventHandler, EventSubscription, Unsubscribe } from './types';
11
+
12
+ export class EventBus {
13
+ private subscriptions: EventSubscription[] = [];
14
+
15
+ /**
16
+ * Subscribe to events matching a glob pattern.
17
+ * Returns an unsubscribe function.
18
+ */
19
+ subscribe(pattern: string, handler: EventHandler): Unsubscribe {
20
+ const regex = compilePattern(pattern);
21
+ const subscription: EventSubscription = { pattern, regex, handler };
22
+ this.subscriptions.push(subscription);
23
+
24
+ return () => {
25
+ const idx = this.subscriptions.indexOf(subscription);
26
+ if (idx !== -1) this.subscriptions.splice(idx, 1);
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Publish an event. All matching subscriptions fire (async).
32
+ * Errors in handlers are caught and logged, never propagated.
33
+ */
34
+ async publish(topic: string, payload: Record<string, unknown> = {}): Promise<void> {
35
+ const event: BusEvent = { topic, payload };
36
+
37
+ for (const sub of this.subscriptions) {
38
+ if (matchTopic(sub.regex, topic)) {
39
+ try {
40
+ await sub.handler(event);
41
+ } catch (error) {
42
+ console.warn(
43
+ `[player-core] Event handler error for pattern "${sub.pattern}" on topic "${topic}":`,
44
+ error,
45
+ );
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Publish synchronously (fire-and-forget).
53
+ * Useful when you don't need to await handler completion.
54
+ */
55
+ emit(topic: string, payload: Record<string, unknown> = {}): void {
56
+ void this.publish(topic, payload);
57
+ }
58
+
59
+ /** Get count of active subscriptions */
60
+ get size(): number {
61
+ return this.subscriptions.length;
62
+ }
63
+
64
+ /** Remove all subscriptions */
65
+ clear(): void {
66
+ this.subscriptions.length = 0;
67
+ }
68
+ }
@@ -0,0 +1,9 @@
1
+ export type {
2
+ EventSubscription,
3
+ BusEvent,
4
+ EventHandler,
5
+ Unsubscribe,
6
+ } from './types';
7
+
8
+ export { compilePattern, matchTopic, clearPatternCache } from './pattern-matcher';
9
+ export { EventBus } from './event-bus';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Pattern Matcher — glob-style topic matching for on_event subscriptions.
3
+ *
4
+ * Supports:
5
+ * - Exact match: "workflow.order:state_enter"
6
+ * - Single-level wildcard (*): "workflow.*:state_enter" matches "workflow.order:state_enter"
7
+ * - Multi-level wildcard (**): "workflow.**" matches "workflow.order:state_enter:completed"
8
+ * - Colon-separated segments (like EventEmitter2)
9
+ *
10
+ * Compiled regex is cached per pattern for performance.
11
+ */
12
+
13
+ const patternCache = new Map<string, RegExp>();
14
+ const MAX_CACHE = 200;
15
+
16
+ /**
17
+ * Compile a glob pattern into a RegExp.
18
+ *
19
+ * Pattern rules:
20
+ * - Segments separated by `:` or `.`
21
+ * - `*` matches exactly one segment (any chars except `:` and `.`)
22
+ * - `**` matches zero or more segments (greedy)
23
+ * - Everything else is literal
24
+ */
25
+ export function compilePattern(pattern: string): RegExp {
26
+ const cached = patternCache.get(pattern);
27
+ if (cached) return cached;
28
+
29
+ // Escape regex special chars except * and separators
30
+ const escaped = pattern
31
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
32
+ // ** (double star) → placeholder
33
+ .replace(/\*\*/g, '<<DOUBLESTAR>>')
34
+ // * (single star) → match one segment
35
+ .replace(/\*/g, '[^:.]+')
36
+ // Restore ** → match anything
37
+ .replace(/<<DOUBLESTAR>>/g, '.*');
38
+
39
+ const regex = new RegExp(`^${escaped}$`);
40
+
41
+ // LRU eviction
42
+ if (patternCache.size >= MAX_CACHE) {
43
+ const firstKey = patternCache.keys().next().value;
44
+ if (firstKey) patternCache.delete(firstKey);
45
+ }
46
+
47
+ patternCache.set(pattern, regex);
48
+ return regex;
49
+ }
50
+
51
+ /**
52
+ * Test if a topic matches a compiled pattern.
53
+ */
54
+ export function matchTopic(pattern: RegExp, topic: string): boolean {
55
+ return pattern.test(topic);
56
+ }
57
+
58
+ /** Clear pattern cache (for testing) */
59
+ export function clearPatternCache(): void {
60
+ patternCache.clear();
61
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Event System Types — browser-side on_event matching and dispatch.
3
+ */
4
+
5
+ /** Subscription registered with the event bus */
6
+ export interface EventSubscription {
7
+ /** Glob-style pattern: "workflow.*:state_enter" or "*:*:completed" */
8
+ pattern: string;
9
+ /** Compiled regex for fast matching (derived from pattern) */
10
+ regex: RegExp;
11
+ /** Conditions evaluated before actions fire */
12
+ conditions?: string[];
13
+ /** Handler called when pattern matches and conditions pass */
14
+ handler: EventHandler;
15
+ }
16
+
17
+ /** Event payload passed to handlers */
18
+ export interface BusEvent {
19
+ topic: string;
20
+ payload: Record<string, unknown>;
21
+ }
22
+
23
+ /** Handler function for matched events */
24
+ export type EventHandler = (event: BusEvent) => void | Promise<void>;
25
+
26
+ /** Unsubscribe function returned by subscribe */
27
+ export type Unsubscribe = () => void;