@mmapp/react 0.1.0-alpha.1 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +112 -0
  2. package/dist/index.d.mts +1378 -94
  3. package/dist/index.d.ts +1378 -94
  4. package/dist/index.js +1094 -1309
  5. package/dist/index.mjs +1038 -1296
  6. package/package.json +4 -3
  7. package/package.json.backup +0 -41
  8. package/src/Blueprint.ts +0 -216
  9. package/src/__tests__/Blueprint.test.ts +0 -106
  10. package/src/__tests__/action-context.test.ts +0 -166
  11. package/src/__tests__/actionCreators.test.ts +0 -179
  12. package/src/__tests__/builders.test.ts +0 -336
  13. package/src/__tests__/defineBlueprint-composition.test.ts +0 -106
  14. package/src/__tests__/factories.test.ts +0 -229
  15. package/src/__tests__/loader.test.ts +0 -159
  16. package/src/__tests__/logger.test.ts +0 -70
  17. package/src/__tests__/type-inference.test.ts +0 -160
  18. package/src/__tests__/typed-transitions.test.ts +0 -126
  19. package/src/__tests__/useModuleConfig.test.ts +0 -61
  20. package/src/actionCreators.ts +0 -132
  21. package/src/actions.ts +0 -547
  22. package/src/atoms/index.ts +0 -600
  23. package/src/authoring.ts +0 -92
  24. package/src/browser-player.ts +0 -783
  25. package/src/builders.ts +0 -1342
  26. package/src/components/ExperienceWorkflowBridge.tsx +0 -123
  27. package/src/components/PlayerProvider.tsx +0 -43
  28. package/src/components/atoms/index.tsx +0 -269
  29. package/src/components/index.ts +0 -36
  30. package/src/conditions.ts +0 -692
  31. package/src/config/defineBlueprint.ts +0 -329
  32. package/src/config/defineModel.ts +0 -753
  33. package/src/config/defineWorkspace.ts +0 -24
  34. package/src/core/WorkflowRuntime.ts +0 -153
  35. package/src/factories.ts +0 -425
  36. package/src/grammar/index.ts +0 -173
  37. package/src/hooks/index.ts +0 -106
  38. package/src/hooks/useAuth.ts +0 -288
  39. package/src/hooks/useChannel.ts +0 -304
  40. package/src/hooks/useComputed.ts +0 -154
  41. package/src/hooks/useDomainSubscription.ts +0 -110
  42. package/src/hooks/useDuringAction.ts +0 -99
  43. package/src/hooks/useExperienceState.ts +0 -59
  44. package/src/hooks/useExpressionLibrary.ts +0 -129
  45. package/src/hooks/useForm.ts +0 -352
  46. package/src/hooks/useGeolocation.ts +0 -207
  47. package/src/hooks/useMapView.ts +0 -259
  48. package/src/hooks/useMiddleware.ts +0 -291
  49. package/src/hooks/useModel.ts +0 -363
  50. package/src/hooks/useModule.ts +0 -59
  51. package/src/hooks/useModuleConfig.ts +0 -61
  52. package/src/hooks/useMutation.ts +0 -237
  53. package/src/hooks/useNotification.ts +0 -151
  54. package/src/hooks/useOnChange.ts +0 -30
  55. package/src/hooks/useOnEnter.ts +0 -59
  56. package/src/hooks/useOnEvent.ts +0 -37
  57. package/src/hooks/useOnExit.ts +0 -27
  58. package/src/hooks/useOnTransition.ts +0 -30
  59. package/src/hooks/usePackage.ts +0 -128
  60. package/src/hooks/useParams.ts +0 -33
  61. package/src/hooks/usePlayer.ts +0 -308
  62. package/src/hooks/useQuery.ts +0 -184
  63. package/src/hooks/useRealtimeQuery.ts +0 -222
  64. package/src/hooks/useRole.ts +0 -191
  65. package/src/hooks/useRouteParams.ts +0 -100
  66. package/src/hooks/useRouter.ts +0 -347
  67. package/src/hooks/useServerAction.ts +0 -178
  68. package/src/hooks/useServerState.ts +0 -284
  69. package/src/hooks/useToast.ts +0 -164
  70. package/src/hooks/useTransition.ts +0 -39
  71. package/src/hooks/useView.ts +0 -102
  72. package/src/hooks/useWhileIn.ts +0 -48
  73. package/src/hooks/useWorkflow.ts +0 -63
  74. package/src/index.ts +0 -465
  75. package/src/loader/experience-workflow-loader.ts +0 -192
  76. package/src/loader/index.ts +0 -6
  77. package/src/local/LocalEngine.ts +0 -388
  78. package/src/local/LocalEngineAdapter.ts +0 -175
  79. package/src/local/LocalEngineContext.ts +0 -30
  80. package/src/logger.ts +0 -37
  81. package/src/mixins.ts +0 -1160
  82. package/src/providers/RuntimeContext.ts +0 -20
  83. package/src/providers/WorkflowProvider.tsx +0 -28
  84. package/src/routing/instance-key.ts +0 -107
  85. package/src/server/transition-context.ts +0 -172
  86. package/src/testing/index.ts +0 -9
  87. package/src/testing/useBlueprintTestRunner.ts +0 -91
  88. package/src/testing/useGraphAnalysis.ts +0 -18
  89. package/src/testing/useTestRunner.ts +0 -77
  90. package/src/testing.ts +0 -995
  91. package/src/types/workflow-inference.ts +0 -158
  92. package/src/types.ts +0 -114
  93. package/tsconfig.json +0 -27
  94. package/vitest.config.ts +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmapp/react",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "React integration for the MindMatrix Player — hooks, components, and WebSocket bridge for browser-side workflow engines",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -22,14 +22,15 @@
22
22
  "dev": "tsup src/index.ts --format cjs,esm --dts --external react --watch",
23
23
  "test": "vitest run",
24
24
  "test:watch": "vitest --watch",
25
- "type-check": "tsc --noEmit"
25
+ "type-check": "tsc --noEmit",
26
+ "publish:alpha": "cd ../.. && ./scripts/publish-npm.sh"
26
27
  },
27
28
  "peerDependencies": {
28
29
  "react": ">=18.0.0",
29
30
  "@tanstack/react-query": ">=5.0.0"
30
31
  },
31
32
  "dependencies": {
32
- "@mmapp/player-core": "^0.1.0-alpha.1"
33
+ "@mmapp/player-core": "workspace:*"
33
34
  },
34
35
  "publishConfig": {
35
36
  "access": "public"
@@ -1,41 +0,0 @@
1
- {
2
- "name": "@mindmatrix/react",
3
- "version": "0.1.0",
4
- "description": "React integration for the MindMatrix Player — hooks, components, and WebSocket bridge for browser-side workflow engines",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.mjs",
12
- "require": "./dist/index.js"
13
- },
14
- "./atoms": {
15
- "types": "./dist/atoms/index.d.ts",
16
- "import": "./dist/atoms/index.mjs",
17
- "require": "./dist/atoms/index.js"
18
- }
19
- },
20
- "scripts": {
21
- "build": "tsup src/index.ts --format cjs,esm --dts --external react",
22
- "dev": "tsup src/index.ts --format cjs,esm --dts --external react --watch",
23
- "test": "vitest run",
24
- "test:watch": "vitest --watch",
25
- "type-check": "tsc --noEmit"
26
- },
27
- "peerDependencies": {
28
- "react": ">=18.0.0",
29
- "@tanstack/react-query": ">=5.0.0"
30
- },
31
- "dependencies": {
32
- "@mindmatrix/player-core": "workspace:*"
33
- },
34
- "devDependencies": {
35
- "@types/react": "^19.0.0",
36
- "react": "^19.0.0",
37
- "tsup": "^8.0.0",
38
- "typescript": "^5.4.0",
39
- "vitest": "^1.5.0"
40
- }
41
- }
package/src/Blueprint.ts DELETED
@@ -1,216 +0,0 @@
1
- /**
2
- * Blueprint — base class for declarative workflow module authoring.
3
- *
4
- * Users extend this class and override define() to declare models,
5
- * views, server actions, and sub-modules using a clean imperative API.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { Blueprint, field, state, transition } from '@mindmatrix/react';
10
- *
11
- * class AuthModule extends Blueprint {
12
- * slug = 'mod-authentication';
13
- * version = '1.0.0';
14
- * category = 'module';
15
- *
16
- * define() {
17
- * this.model('authentication', m => m
18
- * .field('email', field.email().required())
19
- * .state('unauthenticated', state.initial())
20
- * .state('authenticated')
21
- * .transition('login', transition.from('unauthenticated').to('authenticated'))
22
- * );
23
- * this.view('login', { route: '/login', component: 'LoginForm' });
24
- * this.serverAction('authenticate', { handler: 'actions/auth.server.ts' });
25
- * }
26
- * }
27
- *
28
- * export default new AuthModule().build();
29
- * ```
30
- */
31
-
32
- import {
33
- type ModelDefinition,
34
- } from './config/defineModel';
35
- import { ModelBuilder } from './builders';
36
- import { defineBlueprint, type BlueprintConfig, type BlueprintManifest } from './config/defineBlueprint';
37
-
38
- // =============================================================================
39
- // Types
40
- // =============================================================================
41
-
42
- /** A view declaration within a blueprint. */
43
- export interface BlueprintViewDeclaration {
44
- /** Route path (e.g., '/login'). */
45
- route?: string;
46
- /** Component reference (file path or component name). */
47
- component: string;
48
- /** Route guard expression. */
49
- guard?: string;
50
- /** Label for navigation. */
51
- label?: string;
52
- /** Route group. */
53
- group?: string;
54
- /** Icon name. */
55
- icon?: string;
56
- }
57
-
58
- /** A server action declaration within a blueprint. */
59
- export interface BlueprintServerActionDeclaration {
60
- /** Handler file path. */
61
- handler: string;
62
- /** Function name in the handler file. */
63
- functionName?: string;
64
- /** Description. */
65
- description?: string;
66
- }
67
-
68
- // =============================================================================
69
- // Blueprint Base Class
70
- // =============================================================================
71
-
72
- export abstract class Blueprint {
73
- /** Unique identifier (kebab-case). Must be set by subclass. */
74
- abstract slug: string;
75
- /** Semantic version. Must be set by subclass. */
76
- abstract version: string;
77
-
78
- /** Display name (defaults to slug). */
79
- name?: string;
80
- /** Description. */
81
- description?: string;
82
- /** Category or categories. */
83
- category?: string | string[];
84
- /** Author. */
85
- author?: string;
86
- /** Tags. */
87
- tags?: string[];
88
-
89
- // Internal collections populated by define()
90
- private _models = new Map<string, ModelDefinition>();
91
- private _views = new Map<string, BlueprintViewDeclaration>();
92
- private _serverActions = new Map<string, BlueprintServerActionDeclaration>();
93
- private _dependencies: { slug: string; version?: string }[] = [];
94
- private _capabilities: string[] = [];
95
-
96
- /**
97
- * Override this method to declare models, views, actions, etc.
98
- * Called automatically by build().
99
- */
100
- abstract define(): void;
101
-
102
- /**
103
- * Declare a model within this blueprint.
104
- *
105
- * @param name - Model name (will be prefixed with blueprint slug)
106
- * @param builderFn - Callback that configures a ModelBuilder
107
- */
108
- protected model(
109
- name: string,
110
- builderFn: (m: ModelBuilder) => ModelBuilder,
111
- ): void {
112
- const slug = `${this.slug}-${name}`;
113
- const builder = new ModelBuilder(slug);
114
- const configured = builderFn(builder);
115
- this._models.set(name, configured.build());
116
- }
117
-
118
- /**
119
- * Declare a view/page within this blueprint.
120
- *
121
- * @param name - View name
122
- * @param declaration - Route, component, guard, etc.
123
- */
124
- protected view(name: string, declaration: BlueprintViewDeclaration): void {
125
- this._views.set(name, declaration);
126
- }
127
-
128
- /**
129
- * Declare a server action within this blueprint.
130
- *
131
- * @param name - Action name (becomes server:{name})
132
- * @param declaration - Handler file and function
133
- */
134
- protected serverAction(name: string, declaration: BlueprintServerActionDeclaration): void {
135
- this._serverActions.set(name, declaration);
136
- }
137
-
138
- /**
139
- * Declare a dependency on another module/blueprint.
140
- */
141
- protected dependency(slug: string, version?: string): void {
142
- this._dependencies.push({ slug, version });
143
- }
144
-
145
- /**
146
- * Declare a required capability.
147
- */
148
- protected capability(...caps: string[]): void {
149
- this._capabilities.push(...caps);
150
- }
151
-
152
- /**
153
- * Build the blueprint. Calls define(), collects all declarations,
154
- * and returns a BlueprintManifest suitable for defineBlueprint().
155
- */
156
- build(): BlueprintManifest {
157
- // Call user's define() to populate collections
158
- this._models.clear();
159
- this._views.clear();
160
- this._serverActions.clear();
161
- this._dependencies = [];
162
- this._capabilities = [];
163
- this.define();
164
-
165
- // Build blueprint config
166
- const config: BlueprintConfig = {
167
- slug: this.slug,
168
- name: this.name || this.slug,
169
- version: this.version,
170
- description: this.description,
171
- author: this.author,
172
- tags: this.tags,
173
- category: this.category || 'blueprint',
174
- models: Array.from(this._models.keys()).map(k => `models/${k}`),
175
- };
176
-
177
- // Routes from views
178
- if (this._views.size > 0) {
179
- config.routes = Array.from(this._views.entries()).map(([name, v]) => ({
180
- path: v.route || `/${name}`,
181
- view: `app/${name}`,
182
- label: v.label,
183
- guard: v.guard,
184
- group: v.group,
185
- icon: v.icon,
186
- }));
187
- }
188
-
189
- // Actions from server actions
190
- if (this._serverActions.size > 0) {
191
- config.actions = Array.from(this._serverActions.entries()).map(([name, a]) => ({
192
- id: `server:${name}`,
193
- handler: a.handler,
194
- functionName: a.functionName || name,
195
- description: a.description,
196
- }));
197
- }
198
-
199
- if (this._dependencies.length > 0) {
200
- config.dependencies = this._dependencies;
201
- }
202
- if (this._capabilities.length > 0) {
203
- config.capabilities = this._capabilities;
204
- }
205
-
206
- return defineBlueprint(config);
207
- }
208
-
209
- /**
210
- * Get all model definitions declared by this blueprint.
211
- */
212
- getModels(): Map<string, ModelDefinition> {
213
- if (this._models.size === 0) this.define();
214
- return new Map(this._models);
215
- }
216
- }
@@ -1,106 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { Blueprint } from '../Blueprint';
3
- import { field, state, transition } from '../builders';
4
-
5
- class TestModule extends Blueprint {
6
- slug = 'test-module';
7
- version = '1.0.0';
8
- name = 'Test Module';
9
- category = 'module';
10
-
11
- define() {
12
- this.model('user', m => m
13
- .field('email', field.email().required())
14
- .field('name', field.string())
15
- .state('active', state.initial())
16
- .state('inactive', state.end())
17
- .transition('deactivate', transition.from('active').to('inactive'))
18
- );
19
-
20
- this.view('login', { route: '/login', component: 'LoginForm', label: 'Login' });
21
- this.view('profile', { route: '/profile', component: 'Profile', guard: 'context.actor_id != null' });
22
-
23
- this.serverAction('authenticate', { handler: 'actions/auth.server.ts', description: 'Validate credentials' });
24
-
25
- this.dependency('other-module', '>=1.0.0');
26
- this.capability('email');
27
- }
28
- }
29
-
30
- describe('Blueprint', () => {
31
- it('builds a valid BlueprintManifest', () => {
32
- const module = new TestModule();
33
- const manifest = module.build();
34
-
35
- expect(manifest.slug).toBe('test-module');
36
- expect(manifest.version).toBe('1.0.0');
37
- expect(manifest.name).toBe('Test Module');
38
- expect(manifest.category).toBe('module');
39
- });
40
-
41
- it('collects model declarations', () => {
42
- const module = new TestModule();
43
- const manifest = module.build();
44
-
45
- expect(manifest.models).toEqual(['models/user']);
46
- });
47
-
48
- it('collects view declarations as routes', () => {
49
- const module = new TestModule();
50
- const manifest = module.build();
51
-
52
- expect(manifest.routes).toHaveLength(2);
53
- expect(manifest.routes![0].path).toBe('/login');
54
- expect(manifest.routes![0].label).toBe('Login');
55
- expect(manifest.routes![1].path).toBe('/profile');
56
- expect(manifest.routes![1].guard).toBe('context.actor_id != null');
57
- });
58
-
59
- it('collects server action declarations', () => {
60
- const module = new TestModule();
61
- const manifest = module.build();
62
-
63
- expect(manifest.actions).toHaveLength(1);
64
- expect(manifest.actions![0].id).toBe('server:authenticate');
65
- expect(manifest.actions![0].handler).toBe('actions/auth.server.ts');
66
- });
67
-
68
- it('collects dependencies and capabilities', () => {
69
- const module = new TestModule();
70
- const manifest = module.build();
71
-
72
- expect(manifest.dependencies).toHaveLength(1);
73
- expect(manifest.dependencies![0].slug).toBe('other-module');
74
- expect(manifest.capabilities).toEqual(['email']);
75
- });
76
-
77
- it('provides model definitions via getModels()', () => {
78
- const module = new TestModule();
79
- const models = module.getModels();
80
-
81
- expect(models.size).toBe(1);
82
- const userModel = models.get('user')!;
83
- expect(userModel.slug).toBe('test-module-user');
84
- expect(Object.keys(userModel.fields)).toContain('email');
85
- expect(Object.keys(userModel.states)).toContain('active');
86
- expect(Object.keys(userModel.transitions)).toContain('deactivate');
87
- });
88
-
89
- it('resets state on each build() call', () => {
90
- const module = new TestModule();
91
- const m1 = module.build();
92
- const m2 = module.build();
93
-
94
- // Should not accumulate
95
- expect(m1.routes).toHaveLength(2);
96
- expect(m2.routes).toHaveLength(2);
97
- });
98
-
99
- it('applies default mode and runtime', () => {
100
- const module = new TestModule();
101
- const manifest = module.build();
102
-
103
- expect(manifest.mode).toBe('infer');
104
- expect(manifest.defaultRuntime).toBe('local');
105
- });
106
- });
@@ -1,166 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { model, state, field, transition } from '../builders';
3
- import type { ActionHandler } from '../builders';
4
- import { setField } from '../actions';
5
-
6
- describe('Inline action bodies — ActionContext handlers', () => {
7
- describe('StateBuilder.onEnter with handler', () => {
8
- it('stores handler as inline_handler action', () => {
9
- const handler: ActionHandler = async (ctx) => {
10
- ctx.setField('activatedAt', new Date().toISOString());
11
- ctx.log('User activated');
12
- };
13
-
14
- const result = model('test')
15
- .state('active', state.initial().onEnter(handler))
16
- .state('done', state.end())
17
- .transition('finish', transition.from('active').to('done'))
18
- .build();
19
-
20
- const actions = result.states.active.onEnter!;
21
- expect(actions).toHaveLength(1);
22
- expect(actions[0].type).toBe('inline_handler');
23
- expect(actions[0].id).toMatch(/^inline-on-enter-/);
24
- expect(actions[0].config!.handler).toContain('setField');
25
- expect(actions[0].config!.handler).toContain('activatedAt');
26
- });
27
-
28
- it('still accepts regular ActionDefinition array', () => {
29
- const result = model('test')
30
- .state('active', state.initial().onEnter(
31
- setField('status', '"active"'),
32
- ))
33
- .state('done', state.end())
34
- .transition('finish', transition.from('active').to('done'))
35
- .build();
36
-
37
- const actions = result.states.active.onEnter!;
38
- expect(actions).toHaveLength(1);
39
- expect(actions[0].type).toBe('set_field');
40
- });
41
- });
42
-
43
- describe('StateBuilder.onExit with handler', () => {
44
- it('stores handler as inline_handler action', () => {
45
- const handler: ActionHandler = (ctx) => {
46
- ctx.log('Leaving state');
47
- ctx.setFields({ cleanedUp: true });
48
- };
49
-
50
- const result = model('test')
51
- .state('active', state.initial().onExit(handler))
52
- .state('done', state.end())
53
- .transition('finish', transition.from('active').to('done'))
54
- .build();
55
-
56
- const actions = result.states.active.onExit!;
57
- expect(actions).toHaveLength(1);
58
- expect(actions[0].type).toBe('inline_handler');
59
- expect(actions[0].id).toMatch(/^inline-on-exit-/);
60
- expect(actions[0].config!.handler).toContain('cleanedUp');
61
- });
62
- });
63
-
64
- describe('TransitionBuilder.do with handler', () => {
65
- it('stores handler as inline_handler action on transition', () => {
66
- const handler: ActionHandler = async (ctx) => {
67
- ctx.setField('approvedAt', new Date().toISOString());
68
- await ctx.notify('requester', 'Your request was approved');
69
- await ctx.serverAction('send_email', { template: 'approved' });
70
- };
71
-
72
- const result = model('test')
73
- .state('review', state.initial())
74
- .state('approved', state.end())
75
- .transition('approve', transition.from('review').to('approved').do(handler))
76
- .build();
77
-
78
- const actions = result.transitions.approve.actions!;
79
- expect(actions).toHaveLength(1);
80
- expect(actions[0].type).toBe('inline_handler');
81
- expect(actions[0].config!.handler).toContain('notify');
82
- expect(actions[0].config!.handler).toContain('serverAction');
83
- expect(actions[0].config!.handler).toContain('send_email');
84
- });
85
-
86
- it('can mix handler with regular actions', () => {
87
- const result = model('test')
88
- .state('a', state.initial())
89
- .state('b', state.end())
90
- .transition('go', transition.from('a').to('b')
91
- .do(setField('step', '"1"'))
92
- .do(async (ctx) => { ctx.log('step 2'); })
93
- )
94
- .build();
95
-
96
- const actions = result.transitions.go.actions!;
97
- expect(actions).toHaveLength(2);
98
- expect(actions[0].type).toBe('set_field');
99
- expect(actions[1].type).toBe('inline_handler');
100
- });
101
- });
102
-
103
- describe('TypedTransitionBuilder.do with handler', () => {
104
- it('stores handler via typed callback form', () => {
105
- const result = model('test')
106
- .state('draft', state.initial())
107
- .state('published', state.end())
108
- .transition('publish', t =>
109
- t.from('draft').to('published').do(async (ctx) => {
110
- ctx.setField('publishedAt', new Date().toISOString());
111
- await ctx.spawn('notification', { type: 'published' });
112
- })
113
- )
114
- .build();
115
-
116
- const actions = result.transitions.publish.actions!;
117
- expect(actions).toHaveLength(1);
118
- expect(actions[0].type).toBe('inline_handler');
119
- expect(actions[0].config!.handler).toContain('spawn');
120
- expect(actions[0].config!.handler).toContain('publishedAt');
121
- });
122
- });
123
-
124
- describe('Handler function serialization', () => {
125
- it('serializes arrow functions', () => {
126
- const result = model('test')
127
- .state('a', state.initial().onEnter((ctx) => { ctx.log('hello'); }))
128
- .state('b', state.end())
129
- .transition('go', transition.from('a').to('b'))
130
- .build();
131
-
132
- const handler = result.states.a.onEnter![0].config!.handler as string;
133
- expect(handler).toContain('log');
134
- expect(handler).toContain('hello');
135
- });
136
-
137
- it('serializes async arrow functions', () => {
138
- const result = model('test')
139
- .state('a', state.initial().onEnter(async (ctx) => {
140
- await ctx.notify('user', 'hi');
141
- }))
142
- .state('b', state.end())
143
- .transition('go', transition.from('a').to('b'))
144
- .build();
145
-
146
- const handler = result.states.a.onEnter![0].config!.handler as string;
147
- expect(handler).toContain('notify');
148
- });
149
-
150
- it('each handler gets a unique id', () => {
151
- const result = model('test')
152
- .state('a', state.initial()
153
- .onEnter((ctx) => { ctx.log('enter'); })
154
- )
155
- .state('b', state.end())
156
- .transition('go', transition.from('a').to('b')
157
- .do((ctx) => { ctx.log('transition'); })
158
- )
159
- .build();
160
-
161
- const enterId = result.states.a.onEnter![0].id;
162
- const transId = result.transitions.go.actions![0].id;
163
- expect(enterId).not.toBe(transId);
164
- });
165
- });
166
- });