@pyreon/machine 0.11.5 → 0.11.7

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.
@@ -1,108 +1,108 @@
1
- import { signal } from "@pyreon/reactivity"
2
- import { describe, expect, it, vi } from "vitest"
3
- import { createMachine } from "../index"
1
+ import { signal } from '@pyreon/reactivity'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+ import { createMachine } from '../index'
4
4
 
5
- describe("createMachine — guard conditions", () => {
6
- it("transitions when guard returns true", () => {
5
+ describe('createMachine — guard conditions', () => {
6
+ it('transitions when guard returns true', () => {
7
7
  const m = createMachine({
8
- initial: "editing",
8
+ initial: 'editing',
9
9
  states: {
10
10
  editing: {
11
- on: { SUBMIT: { target: "submitting", guard: () => true } },
11
+ on: { SUBMIT: { target: 'submitting', guard: () => true } },
12
12
  },
13
13
  submitting: {},
14
14
  },
15
15
  })
16
- m.send("SUBMIT")
17
- expect(m()).toBe("submitting")
16
+ m.send('SUBMIT')
17
+ expect(m()).toBe('submitting')
18
18
  })
19
19
 
20
- it("blocks transition when guard returns false", () => {
20
+ it('blocks transition when guard returns false', () => {
21
21
  const m = createMachine({
22
- initial: "editing",
22
+ initial: 'editing',
23
23
  states: {
24
24
  editing: {
25
- on: { SUBMIT: { target: "submitting", guard: () => false } },
25
+ on: { SUBMIT: { target: 'submitting', guard: () => false } },
26
26
  },
27
27
  submitting: {},
28
28
  },
29
29
  })
30
- m.send("SUBMIT")
31
- expect(m()).toBe("editing")
30
+ m.send('SUBMIT')
31
+ expect(m()).toBe('editing')
32
32
  })
33
33
 
34
- it("guard receives event payload", () => {
34
+ it('guard receives event payload', () => {
35
35
  const guardFn = vi.fn((payload?: unknown) => {
36
36
  return (payload as any)?.amount > 0
37
37
  })
38
38
 
39
39
  const m = createMachine({
40
- initial: "idle",
40
+ initial: 'idle',
41
41
  states: {
42
42
  idle: {
43
- on: { PAY: { target: "processing", guard: guardFn } },
43
+ on: { PAY: { target: 'processing', guard: guardFn } },
44
44
  },
45
45
  processing: {},
46
46
  },
47
47
  })
48
48
 
49
- m.send("PAY", { amount: 0 })
50
- expect(m()).toBe("idle")
49
+ m.send('PAY', { amount: 0 })
50
+ expect(m()).toBe('idle')
51
51
  expect(guardFn).toHaveBeenCalledWith({ amount: 0 })
52
52
 
53
- m.send("PAY", { amount: 100 })
54
- expect(m()).toBe("processing")
53
+ m.send('PAY', { amount: 100 })
54
+ expect(m()).toBe('processing')
55
55
  expect(guardFn).toHaveBeenCalledWith({ amount: 100 })
56
56
  })
57
57
 
58
- it("guard without payload receives undefined", () => {
58
+ it('guard without payload receives undefined', () => {
59
59
  const guardFn = vi.fn(() => true)
60
60
 
61
61
  const m = createMachine({
62
- initial: "idle",
62
+ initial: 'idle',
63
63
  states: {
64
64
  idle: {
65
- on: { GO: { target: "active", guard: guardFn } },
65
+ on: { GO: { target: 'active', guard: guardFn } },
66
66
  },
67
67
  active: {},
68
68
  },
69
69
  })
70
70
 
71
- m.send("GO")
71
+ m.send('GO')
72
72
  expect(guardFn).toHaveBeenCalledWith(undefined)
73
73
  })
74
74
 
75
- it("guard with reactive signal dependency", () => {
75
+ it('guard with reactive signal dependency', () => {
76
76
  const isValid = signal(false)
77
77
 
78
78
  const m = createMachine({
79
- initial: "editing",
79
+ initial: 'editing',
80
80
  states: {
81
81
  editing: {
82
82
  on: {
83
- SUBMIT: { target: "submitting", guard: () => isValid.peek() },
83
+ SUBMIT: { target: 'submitting', guard: () => isValid.peek() },
84
84
  },
85
85
  },
86
86
  submitting: {},
87
87
  },
88
88
  })
89
89
 
90
- m.send("SUBMIT")
91
- expect(m()).toBe("editing") // guard blocks
90
+ m.send('SUBMIT')
91
+ expect(m()).toBe('editing') // guard blocks
92
92
 
93
93
  isValid.set(true)
94
- m.send("SUBMIT")
95
- expect(m()).toBe("submitting") // guard passes
94
+ m.send('SUBMIT')
95
+ expect(m()).toBe('submitting') // guard passes
96
96
  })
97
97
 
98
- it("guard can check multiple conditions", () => {
98
+ it('guard can check multiple conditions', () => {
99
99
  const m = createMachine({
100
- initial: "idle",
100
+ initial: 'idle',
101
101
  states: {
102
102
  idle: {
103
103
  on: {
104
104
  START: {
105
- target: "running",
105
+ target: 'running',
106
106
  guard: (payload?: unknown) => {
107
107
  const p = payload as { ready: boolean; count: number } | undefined
108
108
  return p !== undefined && p.ready && p.count > 0
@@ -114,24 +114,24 @@ describe("createMachine — guard conditions", () => {
114
114
  },
115
115
  })
116
116
 
117
- m.send("START", { ready: false, count: 5 })
118
- expect(m()).toBe("idle")
117
+ m.send('START', { ready: false, count: 5 })
118
+ expect(m()).toBe('idle')
119
119
 
120
- m.send("START", { ready: true, count: 0 })
121
- expect(m()).toBe("idle")
120
+ m.send('START', { ready: true, count: 0 })
121
+ expect(m()).toBe('idle')
122
122
 
123
- m.send("START", { ready: true, count: 5 })
124
- expect(m()).toBe("running")
123
+ m.send('START', { ready: true, count: 5 })
124
+ expect(m()).toBe('running')
125
125
  })
126
126
 
127
- it("different events on same state can have different guards", () => {
127
+ it('different events on same state can have different guards', () => {
128
128
  const m = createMachine({
129
- initial: "editing",
129
+ initial: 'editing',
130
130
  states: {
131
131
  editing: {
132
132
  on: {
133
- SAVE: { target: "saved", guard: () => true },
134
- PUBLISH: { target: "published", guard: () => false },
133
+ SAVE: { target: 'saved', guard: () => true },
134
+ PUBLISH: { target: 'published', guard: () => false },
135
135
  },
136
136
  },
137
137
  saved: {},
@@ -139,40 +139,40 @@ describe("createMachine — guard conditions", () => {
139
139
  },
140
140
  })
141
141
 
142
- m.send("PUBLISH")
143
- expect(m()).toBe("editing") // guard blocks
142
+ m.send('PUBLISH')
143
+ expect(m()).toBe('editing') // guard blocks
144
144
 
145
- m.send("SAVE")
146
- expect(m()).toBe("saved") // guard passes
145
+ m.send('SAVE')
146
+ expect(m()).toBe('saved') // guard passes
147
147
  })
148
148
 
149
- it("guard blocks do not fire onEnter or onTransition", () => {
149
+ it('guard blocks do not fire onEnter or onTransition', () => {
150
150
  const m = createMachine({
151
- initial: "a",
151
+ initial: 'a',
152
152
  states: {
153
- a: { on: { GO: { target: "b", guard: () => false } } },
153
+ a: { on: { GO: { target: 'b', guard: () => false } } },
154
154
  b: {},
155
155
  },
156
156
  })
157
157
 
158
158
  const enterFn = vi.fn()
159
159
  const transitionFn = vi.fn()
160
- m.onEnter("b", enterFn)
160
+ m.onEnter('b', enterFn)
161
161
  m.onTransition(transitionFn)
162
162
 
163
- m.send("GO")
163
+ m.send('GO')
164
164
  expect(enterFn).not.toHaveBeenCalled()
165
165
  expect(transitionFn).not.toHaveBeenCalled()
166
166
  })
167
167
 
168
- it("mixing guarded and non-guarded transitions", () => {
168
+ it('mixing guarded and non-guarded transitions', () => {
169
169
  const m = createMachine({
170
- initial: "idle",
170
+ initial: 'idle',
171
171
  states: {
172
172
  idle: {
173
173
  on: {
174
- FETCH: "loading", // no guard
175
- ADMIN: { target: "admin", guard: () => false }, // guarded
174
+ FETCH: 'loading', // no guard
175
+ ADMIN: { target: 'admin', guard: () => false }, // guarded
176
176
  },
177
177
  },
178
178
  loading: {},
@@ -180,10 +180,10 @@ describe("createMachine — guard conditions", () => {
180
180
  },
181
181
  })
182
182
 
183
- m.send("ADMIN")
184
- expect(m()).toBe("idle") // blocked
183
+ m.send('ADMIN')
184
+ expect(m()).toBe('idle') // blocked
185
185
 
186
- m.send("FETCH")
187
- expect(m()).toBe("loading") // no guard, passes
186
+ m.send('FETCH')
187
+ expect(m()).toBe('loading') // no guard, passes
188
188
  })
189
189
  })
@@ -1,105 +1,105 @@
1
- import { describe, expect, it, vi } from "vitest"
2
- import { createMachine } from "../index"
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import { createMachine } from '../index'
3
3
 
4
- describe("createMachine — onEnter callbacks", () => {
5
- it("fires callback when entering the specified state", () => {
4
+ describe('createMachine — onEnter callbacks', () => {
5
+ it('fires callback when entering the specified state', () => {
6
6
  const m = createMachine({
7
- initial: "idle",
7
+ initial: 'idle',
8
8
  states: {
9
- idle: { on: { LOAD: "loading" } },
10
- loading: { on: { DONE: "idle" } },
9
+ idle: { on: { LOAD: 'loading' } },
10
+ loading: { on: { DONE: 'idle' } },
11
11
  },
12
12
  })
13
13
  const entered: string[] = []
14
14
 
15
- m.onEnter("loading", (event) => {
15
+ m.onEnter('loading', (event) => {
16
16
  entered.push(event.type)
17
17
  })
18
18
 
19
- m.send("LOAD")
20
- expect(entered).toEqual(["LOAD"])
19
+ m.send('LOAD')
20
+ expect(entered).toEqual(['LOAD'])
21
21
  })
22
22
 
23
- it("does not fire for other state entries", () => {
23
+ it('does not fire for other state entries', () => {
24
24
  const m = createMachine({
25
- initial: "a",
25
+ initial: 'a',
26
26
  states: {
27
- a: { on: { GO: "b" } },
28
- b: { on: { GO: "c" } },
27
+ a: { on: { GO: 'b' } },
28
+ b: { on: { GO: 'c' } },
29
29
  c: {},
30
30
  },
31
31
  })
32
32
  const fn = vi.fn()
33
- m.onEnter("c", fn)
33
+ m.onEnter('c', fn)
34
34
 
35
- m.send("GO") // a -> b
35
+ m.send('GO') // a -> b
36
36
  expect(fn).not.toHaveBeenCalled()
37
37
 
38
- m.send("GO") // b -> c
38
+ m.send('GO') // b -> c
39
39
  expect(fn).toHaveBeenCalledOnce()
40
40
  })
41
41
 
42
- it("receives event payload", () => {
42
+ it('receives event payload', () => {
43
43
  const m = createMachine({
44
- initial: "idle",
44
+ initial: 'idle',
45
45
  states: {
46
- idle: { on: { SELECT: "selected" } },
46
+ idle: { on: { SELECT: 'selected' } },
47
47
  selected: {},
48
48
  },
49
49
  })
50
50
  let received: unknown = null
51
51
 
52
- m.onEnter("selected", (event) => {
52
+ m.onEnter('selected', (event) => {
53
53
  received = event.payload
54
54
  })
55
55
 
56
- m.send("SELECT", { id: 42, name: "item" })
57
- expect(received).toEqual({ id: 42, name: "item" })
56
+ m.send('SELECT', { id: 42, name: 'item' })
57
+ expect(received).toEqual({ id: 42, name: 'item' })
58
58
  })
59
59
 
60
- it("fires on self-transitions", () => {
60
+ it('fires on self-transitions', () => {
61
61
  const m = createMachine({
62
- initial: "counting",
62
+ initial: 'counting',
63
63
  states: {
64
- counting: { on: { INC: "counting" } },
64
+ counting: { on: { INC: 'counting' } },
65
65
  },
66
66
  })
67
67
  const fn = vi.fn()
68
- m.onEnter("counting", fn)
68
+ m.onEnter('counting', fn)
69
69
 
70
- m.send("INC")
71
- m.send("INC")
72
- m.send("INC")
70
+ m.send('INC')
71
+ m.send('INC')
72
+ m.send('INC')
73
73
 
74
74
  expect(fn).toHaveBeenCalledTimes(3)
75
75
  })
76
76
 
77
- it("unsubscribe function stops future callbacks", () => {
77
+ it('unsubscribe function stops future callbacks', () => {
78
78
  const m = createMachine({
79
- initial: "a",
79
+ initial: 'a',
80
80
  states: {
81
- a: { on: { GO: "b" } },
82
- b: { on: { GO: "a" } },
81
+ a: { on: { GO: 'b' } },
82
+ b: { on: { GO: 'a' } },
83
83
  },
84
84
  })
85
85
  const fn = vi.fn()
86
- const unsub = m.onEnter("b", fn)
86
+ const unsub = m.onEnter('b', fn)
87
87
 
88
- m.send("GO") // a -> b
88
+ m.send('GO') // a -> b
89
89
  expect(fn).toHaveBeenCalledOnce()
90
90
 
91
91
  unsub()
92
92
 
93
- m.send("GO") // b -> a
94
- m.send("GO") // a -> b again
93
+ m.send('GO') // b -> a
94
+ m.send('GO') // a -> b again
95
95
  expect(fn).toHaveBeenCalledOnce() // not called again
96
96
  })
97
97
 
98
- it("multiple listeners for same state all fire", () => {
98
+ it('multiple listeners for same state all fire', () => {
99
99
  const m = createMachine({
100
- initial: "idle",
100
+ initial: 'idle',
101
101
  states: {
102
- idle: { on: { GO: "active" } },
102
+ idle: { on: { GO: 'active' } },
103
103
  active: {},
104
104
  },
105
105
  })
@@ -107,40 +107,40 @@ describe("createMachine — onEnter callbacks", () => {
107
107
  const fn2 = vi.fn()
108
108
  const fn3 = vi.fn()
109
109
 
110
- m.onEnter("active", fn1)
111
- m.onEnter("active", fn2)
112
- m.onEnter("active", fn3)
110
+ m.onEnter('active', fn1)
111
+ m.onEnter('active', fn2)
112
+ m.onEnter('active', fn3)
113
113
 
114
- m.send("GO")
114
+ m.send('GO')
115
115
  expect(fn1).toHaveBeenCalledOnce()
116
116
  expect(fn2).toHaveBeenCalledOnce()
117
117
  expect(fn3).toHaveBeenCalledOnce()
118
118
  })
119
119
 
120
- it("onEnter for a state that is never entered is never called", () => {
120
+ it('onEnter for a state that is never entered is never called', () => {
121
121
  const m = createMachine({
122
- initial: "idle",
122
+ initial: 'idle',
123
123
  states: {
124
- idle: { on: { GO: "active" } },
124
+ idle: { on: { GO: 'active' } },
125
125
  active: {},
126
126
  unreachable: {},
127
127
  },
128
128
  })
129
129
  const fn = vi.fn()
130
- m.onEnter("unreachable", fn)
130
+ m.onEnter('unreachable', fn)
131
131
 
132
- m.send("GO")
132
+ m.send('GO')
133
133
  expect(fn).not.toHaveBeenCalled()
134
134
  })
135
135
  })
136
136
 
137
- describe("createMachine — onTransition callbacks", () => {
138
- it("fires on every state transition", () => {
137
+ describe('createMachine — onTransition callbacks', () => {
138
+ it('fires on every state transition', () => {
139
139
  const m = createMachine({
140
- initial: "a",
140
+ initial: 'a',
141
141
  states: {
142
- a: { on: { NEXT: "b" } },
143
- b: { on: { NEXT: "c" } },
142
+ a: { on: { NEXT: 'b' } },
143
+ b: { on: { NEXT: 'c' } },
144
144
  c: {},
145
145
  },
146
146
  })
@@ -150,36 +150,36 @@ describe("createMachine — onTransition callbacks", () => {
150
150
  transitions.push([from, to, event.type])
151
151
  })
152
152
 
153
- m.send("NEXT") // a -> b
154
- m.send("NEXT") // b -> c
153
+ m.send('NEXT') // a -> b
154
+ m.send('NEXT') // b -> c
155
155
 
156
156
  expect(transitions).toEqual([
157
- ["a", "b", "NEXT"],
158
- ["b", "c", "NEXT"],
157
+ ['a', 'b', 'NEXT'],
158
+ ['b', 'c', 'NEXT'],
159
159
  ])
160
160
  })
161
161
 
162
- it("does not fire when event is ignored (no valid transition)", () => {
162
+ it('does not fire when event is ignored (no valid transition)', () => {
163
163
  const m = createMachine({
164
- initial: "idle",
164
+ initial: 'idle',
165
165
  states: {
166
- idle: { on: { START: "running" } },
166
+ idle: { on: { START: 'running' } },
167
167
  running: {},
168
168
  },
169
169
  })
170
170
  const fn = vi.fn()
171
171
  m.onTransition(fn)
172
172
 
173
- m.send("STOP" as any) // invalid
173
+ m.send('STOP' as any) // invalid
174
174
  expect(fn).not.toHaveBeenCalled()
175
175
  })
176
176
 
177
- it("does not fire when guard blocks transition", () => {
177
+ it('does not fire when guard blocks transition', () => {
178
178
  const m = createMachine({
179
- initial: "idle",
179
+ initial: 'idle',
180
180
  states: {
181
181
  idle: {
182
- on: { GO: { target: "active", guard: () => false } },
182
+ on: { GO: { target: 'active', guard: () => false } },
183
183
  },
184
184
  active: {},
185
185
  },
@@ -187,15 +187,15 @@ describe("createMachine — onTransition callbacks", () => {
187
187
  const fn = vi.fn()
188
188
  m.onTransition(fn)
189
189
 
190
- m.send("GO")
190
+ m.send('GO')
191
191
  expect(fn).not.toHaveBeenCalled()
192
192
  })
193
193
 
194
- it("receives event payload", () => {
194
+ it('receives event payload', () => {
195
195
  const m = createMachine({
196
- initial: "idle",
196
+ initial: 'idle',
197
197
  states: {
198
- idle: { on: { LOAD: "loading" } },
198
+ idle: { on: { LOAD: 'loading' } },
199
199
  loading: {},
200
200
  },
201
201
  })
@@ -205,56 +205,56 @@ describe("createMachine — onTransition callbacks", () => {
205
205
  receivedPayload = event.payload
206
206
  })
207
207
 
208
- m.send("LOAD", { url: "/api/data" })
209
- expect(receivedPayload).toEqual({ url: "/api/data" })
208
+ m.send('LOAD', { url: '/api/data' })
209
+ expect(receivedPayload).toEqual({ url: '/api/data' })
210
210
  })
211
211
 
212
- it("fires on self-transitions", () => {
212
+ it('fires on self-transitions', () => {
213
213
  const m = createMachine({
214
- initial: "counting",
214
+ initial: 'counting',
215
215
  states: {
216
- counting: { on: { INC: "counting" } },
216
+ counting: { on: { INC: 'counting' } },
217
217
  },
218
218
  })
219
219
  const fn = vi.fn()
220
220
  m.onTransition(fn)
221
221
 
222
- m.send("INC")
223
- m.send("INC")
222
+ m.send('INC')
223
+ m.send('INC')
224
224
 
225
225
  expect(fn).toHaveBeenCalledTimes(2)
226
226
  expect(fn).toHaveBeenCalledWith(
227
- "counting",
228
- "counting",
229
- expect.objectContaining({ type: "INC" }),
227
+ 'counting',
228
+ 'counting',
229
+ expect.objectContaining({ type: 'INC' }),
230
230
  )
231
231
  })
232
232
 
233
- it("unsubscribe stops future callbacks", () => {
233
+ it('unsubscribe stops future callbacks', () => {
234
234
  const m = createMachine({
235
- initial: "a",
235
+ initial: 'a',
236
236
  states: {
237
- a: { on: { GO: "b" } },
238
- b: { on: { GO: "a" } },
237
+ a: { on: { GO: 'b' } },
238
+ b: { on: { GO: 'a' } },
239
239
  },
240
240
  })
241
241
  const fn = vi.fn()
242
242
  const unsub = m.onTransition(fn)
243
243
 
244
- m.send("GO")
244
+ m.send('GO')
245
245
  expect(fn).toHaveBeenCalledOnce()
246
246
 
247
247
  unsub()
248
- m.send("GO")
249
- m.send("GO")
248
+ m.send('GO')
249
+ m.send('GO')
250
250
  expect(fn).toHaveBeenCalledOnce()
251
251
  })
252
252
 
253
- it("multiple transition listeners all fire", () => {
253
+ it('multiple transition listeners all fire', () => {
254
254
  const m = createMachine({
255
- initial: "idle",
255
+ initial: 'idle',
256
256
  states: {
257
- idle: { on: { GO: "active" } },
257
+ idle: { on: { GO: 'active' } },
258
258
  active: {},
259
259
  },
260
260
  })
@@ -264,25 +264,25 @@ describe("createMachine — onTransition callbacks", () => {
264
264
  m.onTransition(fn1)
265
265
  m.onTransition(fn2)
266
266
 
267
- m.send("GO")
267
+ m.send('GO')
268
268
  expect(fn1).toHaveBeenCalledOnce()
269
269
  expect(fn2).toHaveBeenCalledOnce()
270
270
  })
271
271
 
272
- it("onTransition fires before onEnter", () => {
272
+ it('onTransition fires before onEnter', () => {
273
273
  const m = createMachine({
274
- initial: "idle",
274
+ initial: 'idle',
275
275
  states: {
276
- idle: { on: { GO: "active" } },
276
+ idle: { on: { GO: 'active' } },
277
277
  active: {},
278
278
  },
279
279
  })
280
280
  const order: string[] = []
281
281
 
282
- m.onTransition(() => order.push("transition"))
283
- m.onEnter("active", () => order.push("enter"))
282
+ m.onTransition(() => order.push('transition'))
283
+ m.onEnter('active', () => order.push('enter'))
284
284
 
285
- m.send("GO")
286
- expect(order).toEqual(["transition", "enter"])
285
+ m.send('GO')
286
+ expect(order).toEqual(['transition', 'enter'])
287
287
  })
288
288
  })