@matheuspuel/state-machine 0.4.0 → 0.6.0
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/definition.js.map +1 -1
- package/dist/dts/definition.d.ts +10 -0
- package/dist/dts/definition.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/machines/Form.d.ts +80 -30
- package/dist/dts/machines/Form.d.ts.map +1 -1
- package/dist/dts/machines/QueryState.d.ts +39 -0
- package/dist/dts/machines/QueryState.d.ts.map +1 -0
- package/dist/dts/machines/index.d.ts +2 -2
- package/dist/dts/machines/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/machines/Form.js +147 -54
- package/dist/machines/Form.js.map +1 -1
- package/dist/machines/QueryState.js +28 -0
- package/dist/machines/QueryState.js.map +1 -0
- package/dist/machines/index.js +2 -2
- package/dist/machines/index.js.map +1 -1
- package/package.json +15 -14
- package/src/definition.ts +7 -0
- package/src/index.ts +2 -0
- package/src/machines/Form.test.ts +123 -6
- package/src/machines/Form.ts +453 -140
- package/src/machines/QueryState.ts +89 -0
- package/src/machines/index.ts +2 -2
- package/dist/dts/form/definition.d.ts +0 -20
- package/dist/dts/form/definition.d.ts.map +0 -1
- package/dist/dts/form/index.d.ts +0 -2
- package/dist/dts/form/index.d.ts.map +0 -1
- package/dist/dts/machines/FormValue.d.ts +0 -12
- package/dist/dts/machines/FormValue.d.ts.map +0 -1
- package/dist/form/definition.js +0 -14
- package/dist/form/definition.js.map +0 -1
- package/dist/form/index.js +0 -2
- package/dist/form/index.js.map +0 -1
- package/dist/machines/FormValue.js +0 -12
- package/dist/machines/FormValue.js.map +0 -1
- package/src/form/definition.ts +0 -33
- package/src/form/index.ts +0 -1
- package/src/machines/FormValue.test.ts +0 -18
- package/src/machines/FormValue.ts +0 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matheuspuel/state-machine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"/dist",
|
|
@@ -16,10 +16,6 @@
|
|
|
16
16
|
"types": "./dist/dts/definition.d.ts",
|
|
17
17
|
"default": "./dist/definition.js"
|
|
18
18
|
},
|
|
19
|
-
"./form": {
|
|
20
|
-
"types": "./dist/dts/form/index.d.ts",
|
|
21
|
-
"default": "./dist/form/index.js"
|
|
22
|
-
},
|
|
23
19
|
"./machines": {
|
|
24
20
|
"types": "./dist/dts/machines/index.d.ts",
|
|
25
21
|
"default": "./dist/machines/index.js"
|
|
@@ -33,15 +29,9 @@
|
|
|
33
29
|
"default": "./dist/runtime/index.js"
|
|
34
30
|
}
|
|
35
31
|
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsc -b tsconfig.build.json",
|
|
38
|
-
"lint": "eslint . --max-warnings 0 --config .eslintrc.production.js",
|
|
39
|
-
"lint:fix": "eslint . --fix --config .eslintrc.production.js",
|
|
40
|
-
"typecheck": "tsc",
|
|
41
|
-
"test": "vitest"
|
|
42
|
-
},
|
|
43
32
|
"dependencies": {
|
|
44
33
|
"@matheuspuel/optic": "^0.2.0",
|
|
34
|
+
"@matheuspuel/query-state": "^0.2.0",
|
|
45
35
|
"effect": "^3.18.1",
|
|
46
36
|
"react": "19.1.0",
|
|
47
37
|
"use-sync-external-store": "^1.5.0"
|
|
@@ -53,16 +43,27 @@
|
|
|
53
43
|
"@types/node": "^25.0.8",
|
|
54
44
|
"@types/react": "~19.1.17",
|
|
55
45
|
"@types/use-sync-external-store": "^1.5.0",
|
|
56
|
-
"@typescript-eslint/
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
|
47
|
+
"@typescript-eslint/parser": "^8.55.0",
|
|
57
48
|
"@vitest/coverage-v8": "^3.2.4",
|
|
58
49
|
"cross-env": "^7.0.3",
|
|
59
50
|
"eslint": "^8.57.1",
|
|
60
51
|
"eslint-config-prettier": "^10.1.1",
|
|
61
52
|
"eslint-plugin-prettier": "^5.2.6",
|
|
53
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
62
54
|
"prettier": "^3.6.2",
|
|
63
55
|
"tsx": "^4.20.6",
|
|
64
56
|
"typescript": "^5.9.3",
|
|
65
57
|
"vite": "^6.3.5",
|
|
66
58
|
"vitest": "^3.2.4"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsc --project tsconfig.build.json",
|
|
62
|
+
"build:watch": "tsc --project tsconfig.build.json --watch",
|
|
63
|
+
"lint": "eslint . --max-warnings 0 --config .eslintrc.production.cjs",
|
|
64
|
+
"lint:fix": "eslint . --fix --config .eslintrc.production.cjs",
|
|
65
|
+
"typecheck": "tsc",
|
|
66
|
+
"test": "vitest",
|
|
67
|
+
"check": "pnpm build && pnpm typecheck && pnpm lint && pnpm test --run"
|
|
67
68
|
}
|
|
68
|
-
}
|
|
69
|
+
}
|
package/src/definition.ts
CHANGED
|
@@ -36,6 +36,13 @@ export type StateMachine<State, Actions extends AnyStateActions> = {
|
|
|
36
36
|
onUpdate?: (state: State) => void | Promise<void>
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export type AnyStateMachineWithActions<Actions extends AnyStateActions> = {
|
|
40
|
+
initialState: any
|
|
41
|
+
actions: (machine: { Store: any }) => Actions
|
|
42
|
+
start?: (machine: { Store: any }) => undefined | Promise<unknown>
|
|
43
|
+
onUpdate?: (state: any) => void | Promise<void>
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
export const make =
|
|
40
47
|
<State>() =>
|
|
41
48
|
<Actions extends AnyStateActions>(args: StateMachine<State, Actions>) =>
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { describe, expect, it } from '@effect/vitest'
|
|
2
|
-
import { StateMachine } from '@matheuspuel/state-machine'
|
|
3
|
-
import { Form } from '@matheuspuel/state-machine/form'
|
|
2
|
+
import { Form, StateMachine } from '@matheuspuel/state-machine'
|
|
4
3
|
import { Effect } from 'effect'
|
|
4
|
+
import { NoSuchElementException } from 'effect/Cause'
|
|
5
|
+
import { ValidationError } from './Form.js'
|
|
5
6
|
|
|
6
7
|
describe('Form', () => {
|
|
7
8
|
it('should work', () => {
|
|
8
|
-
const formField = Form.field({
|
|
9
|
-
initial: 0,
|
|
9
|
+
const formField = Form.transformField(Form.field(0), {
|
|
10
10
|
validate: _ =>
|
|
11
11
|
_ > 1 ? Effect.succeed({ n: _ }) : Effect.fail('low' as const),
|
|
12
12
|
fromData: (data: { n: number }) => data.n,
|
|
13
13
|
})
|
|
14
|
-
const
|
|
14
|
+
const machine = Form.Struct({
|
|
15
15
|
a: formField,
|
|
16
16
|
b: Form.Struct({ c: formField }),
|
|
17
17
|
})
|
|
18
|
-
const machine = StateMachine.Form(form)
|
|
19
18
|
const instance = StateMachine.run(machine)
|
|
20
19
|
const getState = () => instance.ref.get.pipe(Effect.runSync)
|
|
21
20
|
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
@@ -64,4 +63,122 @@ describe('Form', () => {
|
|
|
64
63
|
b: { c: { value: 3, error: null } },
|
|
65
64
|
})
|
|
66
65
|
})
|
|
66
|
+
|
|
67
|
+
it('TaggedUnion', () => {
|
|
68
|
+
const stateMachine = Form.TaggedUnion('_tag', {
|
|
69
|
+
A: Form.Struct({ a: Form.field(0), x: Form.field('') }),
|
|
70
|
+
B: Form.Struct({ b: Form.field(0), x: Form.field('') }),
|
|
71
|
+
})
|
|
72
|
+
const form = StateMachine.run(stateMachine)
|
|
73
|
+
const getState = () => form.ref.get.pipe(Effect.runSync)
|
|
74
|
+
|
|
75
|
+
form.actions.A.a.set(1)
|
|
76
|
+
form.actions.A.x.set('a')
|
|
77
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
78
|
+
_tag: { value: 'A', error: null },
|
|
79
|
+
A: { a: { value: 1, error: null }, x: { value: 'a', error: null } },
|
|
80
|
+
B: { b: { value: 0, error: null }, x: { value: '', error: null } },
|
|
81
|
+
})
|
|
82
|
+
const dataA = form.actions.validate().pipe(Effect.runSync)
|
|
83
|
+
expect(dataA).toStrictEqual<typeof dataA>({
|
|
84
|
+
_tag: 'A',
|
|
85
|
+
a: 1,
|
|
86
|
+
x: 'a',
|
|
87
|
+
})
|
|
88
|
+
form.actions._tag.set('B')
|
|
89
|
+
const dataB = form.actions.validate().pipe(Effect.runSync)
|
|
90
|
+
expect(dataB).toStrictEqual<typeof dataB>({
|
|
91
|
+
_tag: 'B',
|
|
92
|
+
b: 0,
|
|
93
|
+
x: '',
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('Array', () => {
|
|
98
|
+
const stateMachine = Form.Struct({
|
|
99
|
+
list: Form.Array(Form.Struct({ a: Form.NonEmptyString })),
|
|
100
|
+
})
|
|
101
|
+
const form = StateMachine.run(stateMachine)
|
|
102
|
+
const getState = () => form.ref.get.pipe(Effect.runSync)
|
|
103
|
+
|
|
104
|
+
form.actions.validate().pipe(Effect.runSyncExit)
|
|
105
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
106
|
+
list: [],
|
|
107
|
+
})
|
|
108
|
+
form.actions.list.addItem()
|
|
109
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
110
|
+
list: [{ a: { value: '', error: null } }],
|
|
111
|
+
})
|
|
112
|
+
form.actions.validate().pipe(Effect.runSyncExit)
|
|
113
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
114
|
+
list: [{ a: { value: '', error: new NoSuchElementException() } }],
|
|
115
|
+
})
|
|
116
|
+
form.actions.list.index(0).a.set('aaa')
|
|
117
|
+
const data = form.actions.validate().pipe(Effect.runSync)
|
|
118
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
119
|
+
list: [{ a: { value: 'aaa', error: null } }],
|
|
120
|
+
})
|
|
121
|
+
expect(data).toStrictEqual<typeof data>({
|
|
122
|
+
list: [{ a: 'aaa' }],
|
|
123
|
+
})
|
|
124
|
+
form.actions.list.addItem()
|
|
125
|
+
form.actions.list.removeItem(0)
|
|
126
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
127
|
+
list: [{ a: { value: '', error: null } }],
|
|
128
|
+
})
|
|
129
|
+
form.actions.setStateFromData({ list: [{ a: '1' }, { a: '2' }] })
|
|
130
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
131
|
+
list: [
|
|
132
|
+
{ a: { value: '1', error: null } },
|
|
133
|
+
{ a: { value: '2', error: null } },
|
|
134
|
+
],
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('change password example', () => {
|
|
139
|
+
const stateMachine = Form.Struct({
|
|
140
|
+
oldPassword: Form.field(''),
|
|
141
|
+
newPassword: StateMachine.mapActions(
|
|
142
|
+
Form.Struct({
|
|
143
|
+
password: Form.field(''),
|
|
144
|
+
confirmation: Form.fieldWithError<'not-match'>()(Form.field('')),
|
|
145
|
+
}),
|
|
146
|
+
actions => ({
|
|
147
|
+
...actions,
|
|
148
|
+
validate: () =>
|
|
149
|
+
Effect.gen(function* () {
|
|
150
|
+
const result = yield* actions.validate()
|
|
151
|
+
if (result.password !== result.confirmation) {
|
|
152
|
+
actions.confirmation.error.set('not-match' as const)
|
|
153
|
+
return yield* new ValidationError({
|
|
154
|
+
error: 'not-match' as const,
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
return result.password
|
|
158
|
+
}),
|
|
159
|
+
setStateFromData: (password: string) =>
|
|
160
|
+
actions.setStateFromData({ password, confirmation: password }),
|
|
161
|
+
}),
|
|
162
|
+
),
|
|
163
|
+
})
|
|
164
|
+
const form = StateMachine.run(stateMachine)
|
|
165
|
+
const getState = () => form.ref.get.pipe(Effect.runSync)
|
|
166
|
+
|
|
167
|
+
form.actions.newPassword.password.set('123456')
|
|
168
|
+
form.actions.newPassword.confirmation.set('1234567')
|
|
169
|
+
form.actions.validate().pipe(Effect.runSyncExit)
|
|
170
|
+
expect(getState()).toStrictEqual<ReturnType<typeof getState>>({
|
|
171
|
+
oldPassword: { value: '', error: null },
|
|
172
|
+
newPassword: {
|
|
173
|
+
password: { value: '123456', error: null },
|
|
174
|
+
confirmation: { value: '1234567', error: 'not-match' },
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
form.actions.newPassword.confirmation.set('123456')
|
|
178
|
+
const data = form.actions.validate().pipe(Effect.runSync)
|
|
179
|
+
expect(data).toStrictEqual<typeof data>({
|
|
180
|
+
oldPassword: '',
|
|
181
|
+
newPassword: '123456',
|
|
182
|
+
})
|
|
183
|
+
})
|
|
67
184
|
})
|