@uuxxx/fsm 1.2.3 → 1.2.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.
package/README.md CHANGED
@@ -21,33 +21,33 @@ import { makeFsm } from '@uuxxx/fsm';
21
21
 
22
22
  type State = 'idle' | 'loading' | 'success' | 'error';
23
23
 
24
- const STATES: State[] = ['idle', 'loading', 'success', 'error']
24
+ const STATES: State[] = ['idle', 'loading', 'success', 'error'];
25
25
 
26
26
  const fsm = makeFsm({
27
- init: 'idle',
28
- states: STATES,
29
- transitions: {
30
- start: {
31
- from: 'idle',
32
- to: 'loading',
33
- },
34
- succeed: {
35
- from: 'loading',
36
- to: 'success',
37
- },
38
- fail: {
39
- from: 'loading',
40
- to: 'error',
41
- },
42
- reset: {
43
- from: ['success', 'error'],
44
- to: 'idle',
45
- },
46
- goto: {
47
- from: '*',
48
- to: (state: State) => state
49
- }
50
- },
27
+ init: 'idle',
28
+ states: STATES,
29
+ transitions: {
30
+ start: {
31
+ from: 'idle',
32
+ to: 'loading',
33
+ },
34
+ succeed: {
35
+ from: 'loading',
36
+ to: 'success',
37
+ },
38
+ fail: {
39
+ from: 'loading',
40
+ to: 'error',
41
+ },
42
+ reset: {
43
+ from: ['success', 'error'],
44
+ to: 'idle',
45
+ },
46
+ goto: {
47
+ from: '*',
48
+ to: (state: State) => state,
49
+ },
50
+ },
51
51
  });
52
52
 
53
53
  // Check current state
@@ -60,11 +60,11 @@ console.log(fsm.state()); // 'loading'
60
60
  fsm.succeed();
61
61
  console.log(fsm.state()); // 'success'
62
62
 
63
- fsm.reset()
64
- console.log(fsm.state()) // 'idle'
63
+ fsm.reset();
64
+ console.log(fsm.state()); // 'idle'
65
65
 
66
- fsm.goto('error')
67
- console.log(fsm.state()) // 'error'
66
+ fsm.goto('error');
67
+ console.log(fsm.state()); // 'error'
68
68
  ```
69
69
 
70
70
  ## API Reference
@@ -110,8 +110,8 @@ Transitions are defined as objects with `from` and `to` properties:
110
110
 
111
111
  ```typescript
112
112
  type Transition<TState> = {
113
- from: '*' | TState | TState[];
114
- to: TState | ((...args: any[]) => TState | Promise<TState>);
113
+ from: '*' | TState | TState[];
114
+ to: TState | ((...args: any[]) => TState | Promise<TState>);
115
115
  };
116
116
  ```
117
117
 
@@ -121,39 +121,39 @@ type Transition<TState> = {
121
121
  - Any state: `'*'`
122
122
  - `to`: The target state or a function returning the target state
123
123
  - Static: `'loading'`
124
- - Dynamic: `(userId: string) => \`user_\${userId}\``
124
+ - Dynamic: `(userId: string) => \`user\_\${userId}\``
125
125
  - Async: `async (data) => await apiCall(data)`
126
126
 
127
127
  #### Examples
128
128
 
129
129
  ```typescript
130
130
  const transitions = {
131
- // Simple transition
132
- 'idle -> loading': {
133
- from: 'idle',
134
- to: 'loading',
135
- },
136
-
137
- // Multiple source states
138
- reset: {
139
- from: ['success', 'error'],
140
- to: 'idle',
141
- },
142
-
143
- // Wildcard (from any state)
144
- goto: {
145
- from: '*',
146
- to: (targetState: State) => targetState,
147
- },
148
-
149
- // Async transition
150
- 'async fetch': {
151
- from: 'idle',
152
- to: async () => {
153
- const result = await fetchData();
154
- return result.success ? 'success' : 'error';
155
- },
156
- },
131
+ // Simple transition
132
+ 'idle -> loading': {
133
+ from: 'idle',
134
+ to: 'loading',
135
+ },
136
+
137
+ // Multiple source states
138
+ reset: {
139
+ from: ['success', 'error'],
140
+ to: 'idle',
141
+ },
142
+
143
+ // Wildcard (from any state)
144
+ goto: {
145
+ from: '*',
146
+ to: (targetState: State) => targetState,
147
+ },
148
+
149
+ // Async transition
150
+ 'async fetch': {
151
+ from: 'idle',
152
+ to: async () => {
153
+ const result = await fetchData();
154
+ return result.success ? 'success' : 'error';
155
+ },
156
+ },
157
157
  };
158
158
  ```
159
159
 
@@ -163,17 +163,17 @@ Lifecycle methods can be attached to the FSM configuration:
163
163
 
164
164
  ```typescript
165
165
  const config = {
166
- // ... other config
167
- methods: {
168
- onBeforeTransition: (event) => {
169
- console.log('About to transition:', event);
170
- // Return false to cancel the transition
171
- return true;
172
- },
173
- onAfterTransition: (event) => {
174
- console.log('Transition completed:', event);
175
- },
176
- },
166
+ // ... other config
167
+ methods: {
168
+ onBeforeTransition: (event) => {
169
+ console.log('About to transition:', event);
170
+ // Return false to cancel the transition
171
+ return true;
172
+ },
173
+ onAfterTransition: (event) => {
174
+ console.log('Transition completed:', event);
175
+ },
176
+ },
177
177
  };
178
178
  ```
179
179
 
@@ -182,6 +182,7 @@ const config = {
182
182
  Called before a transition occurs. Return `false` to cancel the transition.
183
183
 
184
184
  **Parameters:**
185
+
185
186
  - `event`: Object with `transition`, `from`, `to`, and optional `args`
186
187
 
187
188
  #### `onAfterTransition(event)`
@@ -189,6 +190,7 @@ Called before a transition occurs. Return `false` to cancel the transition.
189
190
  Called after a successful transition.
190
191
 
191
192
  **Parameters:**
193
+
192
194
  - `event`: Object with `transition`, `from`, `to`, and optional `args`
193
195
 
194
196
  ## Plugins
@@ -208,42 +210,34 @@ Plugins have access to:
208
210
  ### Creating a Plugin
209
211
 
210
212
  ```typescript
211
- const myPlugin = (options) => (api) => {
212
- // Plugin initialization
213
- api.init((initialState) => {
214
- console.log('FSM initialized with state:', initialState);
215
- });
216
-
217
- // Listen to transitions
218
- api.onBeforeTransition((event) => {
219
- console.log('Transition starting:', event);
220
- });
221
-
222
- // Return plugin definition
223
- return {
224
- name: 'my-plugin',
225
- api: {
226
- // Custom methods exposed on fsm['my-plugin']
227
- doSomething: () => {
228
- return api.state();
229
- },
230
- },
231
- };
232
- };
213
+ import type { FsmLabel, FsmPlugin, FsmTransition } from '@uuxxx/fsm';
214
+
215
+ export const somePlugin = <TState extends FsmLabel, TTransitions extends Record<string, FsmTransition<TState>>>() =>
216
+ ((api) => {
217
+ // ... plugin code
218
+
219
+ return {
220
+ // replace with your plugin name
221
+ name: 'plugin-name' as const,
222
+ api: {
223
+ // ... plugin methods
224
+ },
225
+ };
226
+ }) satisfies FsmPlugin<TState, TTransitions>;
233
227
  ```
234
228
 
235
229
  ### Using Plugins
236
230
 
237
231
  ```typescript
238
232
  const config = {
239
- // ... other config
240
- plugins: [myPlugin({ someOption: true })],
233
+ // ... other config
234
+ plugins: [somePlugin({ someOption: true })],
241
235
  };
242
236
 
243
237
  const fsm = makeFsm(config);
244
238
 
245
239
  // Access plugin API
246
- const currentState = fsm['my-plugin'].doSomething();
240
+ const currentState = fsm['plugin-name'].doSomething();
247
241
  ```
248
242
 
249
243
  ## Built-in Plugins
@@ -253,11 +247,12 @@ const currentState = fsm['my-plugin'].doSomething();
253
247
  Tracks state history and provides navigation methods.
254
248
 
255
249
  ```typescript
256
- import { makeFsm, historyPlugin } from '@uuxxx/fsm';
250
+ import { makeFsm } from '@uuxxx/fsm';
251
+ import { fsmHistoryPlugin } from '@uuxxx/fsm/history-plugin';
257
252
 
258
253
  const config = {
259
- // ... config
260
- plugins: [historyPlugin()],
254
+ // ... config
255
+ plugins: [fsmHistoryPlugin()],
261
256
  };
262
257
 
263
258
  const fsm = makeFsm(config);
@@ -267,34 +262,17 @@ fsm.goto('state1');
267
262
  fsm.goto('state2');
268
263
 
269
264
  // History API
270
- console.log(fsm.history.get()); // ['initial', 'state1', 'state2']
265
+ console.log(fsm.history.get()); // Get all history
271
266
 
272
- fsm.history.back(1); // Go back 1 step
273
- fsm.history.forward(1); // Go forward 1 step
267
+ fsm.history.back(1); // Get 1 step back
268
+ fsm.history.forward(1); // Get 1 step forward
274
269
  ```
275
270
 
276
271
  #### History API Methods
277
272
 
278
273
  - `fsm.history.get()`: Get the full history array
279
- - `fsm.history.back(steps?)`: Go back N steps (default: 1)
280
- - `fsm.history.forward(steps?)`: Go forward N steps (default: 1)
281
-
282
- ## Error Handling
283
-
284
- The FSM throws errors in the following cases:
285
-
286
- - Invalid transition (current state doesn't match `from`)
287
- - Pending async transition when starting a new sync transition
288
- - Invalid target state
289
- - Duplicate plugin names
290
-
291
- ```typescript
292
- try {
293
- fsm.invalidTransition();
294
- } catch (error) {
295
- console.error(error.message); // [FSM]: Transition: "invalidTransition" is forbidden
296
- }
297
- ```
274
+ - `fsm.history.back(steps?)`: Get N step back (default: 1)
275
+ - `fsm.history.forward(steps?)`: Get N step forward (default: 1)
298
276
 
299
277
  ## TypeScript Support
300
278
 
@@ -302,12 +280,12 @@ The library is fully typed. Type inference works automatically:
302
280
 
303
281
  ```typescript
304
282
  const fsm = makeFsm({
305
- init: 'idle',
306
- states: ['idle', 'running', 'stopped'],
307
- transitions: {
308
- start: { from: 'idle', to: 'running' },
309
- stop: { from: 'running', to: 'stopped' },
310
- },
283
+ init: 'idle',
284
+ states: ['idle', 'running', 'stopped'],
285
+ transitions: {
286
+ start: { from: 'idle', to: 'running' },
287
+ stop: { from: 'running', to: 'stopped' },
288
+ },
311
289
  });
312
290
  // fsm is fully typed - autocomplete works for transitions and states
313
291
  ```
@@ -29,6 +29,7 @@ type StateMethods<TState extends Label> = {
29
29
  };
30
30
  type ApiForPlugin<TState extends Label, TTransitions extends Rec<Transition<TState>>> = {
31
31
  init: (listener: (state: TState) => void) => void;
32
+ onError: (listener: (msg: string) => void) => Noop;
32
33
  } & StateMethods<TState> & { [K in KeyOf<LifecycleMethods<TState, TTransitions>>]-?: (listener: LifecycleMethods<TState, TTransitions>[K]) => Noop };
33
34
  type PluginApi = {
34
35
  name: string;
@@ -1,4 +1,4 @@
1
- import { a as Transition, l as Rec, o as Label, t as ApiForPlugin } from "./Plugin-DGULTFg-.js";
1
+ import { a as Transition, l as Rec, o as Label, t as ApiForPlugin } from "./Plugin-Dx6AAZEC.js";
2
2
  declare const historyPlugin: <TState extends Label, TTransitions extends Rec<Transition<TState>>>() => (api: ApiForPlugin<TState, TTransitions>) => {
3
3
  name: "history";
4
4
  api: {
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { a as Transition, c as KeyOf, i as LifecycleMethods, l as Rec, n as Plugin, o as Label, r as StateMethods, s as EmptyArray } from "./Plugin-DGULTFg-.js";
1
+ import { a as Transition, c as KeyOf, i as LifecycleMethods, l as Rec, n as Plugin, o as Label, r as StateMethods, s as EmptyArray } from "./Plugin-Dx6AAZEC.js";
2
2
  type Config<TState extends Label, TTransitions extends Rec<Transition<TState>>, TPlugins extends Array<Plugin<TState, TTransitions>> = EmptyArray> = {
3
3
  init: TState;
4
4
  states: TState[];
5
5
  transitions: TTransitions;
6
6
  methods?: LifecycleMethods<TState, TTransitions>;
7
7
  plugins?: TPlugins;
8
+ onError?: (msg: string) => void;
8
9
  };
9
10
  type TransitionMethods<TTransitions extends Rec<Transition<Label>>> = { [K in KeyOf<TTransitions>]: TTransitions[K]['to'] extends Label ? () => TTransitions[K]['to'] : TTransitions[K]['to'] };
10
11
  type PluginsMethods<TState extends Label, TTransitions extends Rec<Transition<TState>>, TPlugins extends Array<Plugin<TState, TTransitions>>> = { [K in ReturnType<TPlugins[number]>['name']]: Extract<ReturnType<TPlugins[number]>, {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- let t={nlx:t=>null===t,ulx:t=>void 0===t,nil:e=>t.nlx(e)||t.ulx(e),not:{nlx:t=>null!==t,ulx:t=>void 0!==t,nil:e=>t.not.nlx(e)&&t.not.ulx(e)},array:t=>Array.isArray(t),string:t=>"string"==typeof t,function:t=>"function"==typeof t,promise:t=>t instanceof Promise,boolean:t=>"boolean"==typeof t,false:t=>!1===t,true:t=>!0===t};function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,n)}return r}function n(t){for(var n=1;n<arguments.length;n++){var i=null!=arguments[n]?arguments[n]:{};n%2?r(Object(i),!0).forEach(function(r){!function(t,r,n){var i;(i=function(t,r){if("object"!=e(t)||!t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var i=n.call(t,r||"default");if("object"!=e(i))return i;throw TypeError("@@toPrimitive must return a primitive value.")}return("string"===r?String:Number)(t)}(r,"string"),(r="symbol"==e(i)?i:i+"")in t)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n}(t,r,i[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):r(Object(i)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))})}return t}let i=e=>{var r,i,o;let l,s,a,u,f,c,m=e.init,b=e.states.includes(e.init)?[...e.states]:[...e.states,e.init],p=(l=new Map,{listen(t,e){var r;return l.has(t)||l.set(t,[]),null==(r=l.get(t))||r.push(e),()=>{this.unlisten(t,e)}},unlisten(t,e){let r=l.get(t);if(!r)return;let n=r.filter(t=>t!==e);n.length?l.set(t,n):l.delete(t)},emit(e,...r){var n,i;return null!=(n=null==(i=l.get(e))?void 0:i.map(t=>t(...r)).filter(t.not.ulx))?n:[]},unlistenAll(t){l.delete(t)}});Object.entries(null!=(r=e.methods)?r:{}).forEach(([t,e])=>{p.listen(t,e)}),p.listen("onAfterTransition",({to:t})=>{m=t}),p.listen("error",t=>{throw Error(`[FSM]: ${t}`)});let y={state:()=>m,allStates:()=>b},g=(i=e.transitions,s=((e,{state:r,allStates:n})=>{let i,o={},l={register(s,a){let u=r=>{n().includes(r.to)?r.to===r.from?e.emit("warn",`
1
+ let t={nlx:t=>null===t,ulx:t=>void 0===t,nil:e=>t.nlx(e)||t.ulx(e),not:{nlx:t=>null!==t,ulx:t=>void 0!==t,nil:e=>t.not.nlx(e)&&t.not.ulx(e)},array:t=>Array.isArray(t),string:t=>"string"==typeof t,function:t=>"function"==typeof t,promise:t=>t instanceof Promise,boolean:t=>"boolean"==typeof t,false:t=>!1===t,true:t=>!0===t};function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),r.push.apply(r,n)}return r}function n(t){for(var n=1;n<arguments.length;n++){var i=null!=arguments[n]?arguments[n]:{};n%2?r(Object(i),!0).forEach(function(r){!function(t,r,n){var i;(i=function(t,r){if("object"!=e(t)||!t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var i=n.call(t,r||"default");if("object"!=e(i))return i;throw TypeError("@@toPrimitive must return a primitive value.")}return("string"===r?String:Number)(t)}(r,"string"),(r="symbol"==e(i)?i:i+"")in t)?Object.defineProperty(t,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[r]=n}(t,r,i[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):r(Object(i)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))})}return t}let i=e=>{var r,i,o,l;let s,a,u,f,c,m,b=e.init,p=e.states.includes(e.init)?[...e.states]:[...e.states,e.init],y=(s=new Map,{listen(t,e){var r;return s.has(t)||s.set(t,[]),null==(r=s.get(t))||r.push(e),()=>{this.unlisten(t,e)}},unlisten(t,e){let r=s.get(t);if(!r)return;let n=r.filter(t=>t!==e);n.length?s.set(t,n):s.delete(t)},emit(e,...r){var n,i;return null!=(n=null==(i=s.get(e))?void 0:i.map(t=>t(...r)).filter(t.not.ulx))?n:[]},unlistenAll(t){s.delete(t)}});Object.entries(null!=(r=e.methods)?r:{}).forEach(([t,e])=>{y.listen(t,e)}),y.listen("onAfterTransition",({to:t})=>{b=t}),y.listen("error",null!=(i=e.onError)?i:t=>{throw Error(`[FSM]: ${t}`)});let g={state:()=>b,allStates:()=>p},d=(o=e.transitions,a=((e,{state:r,allStates:n})=>{let i,o={},l={register(s,a){let u=r=>{n().includes(r.to)?r.to===r.from?e.emit("warn",`
2
2
  Transition: "${s}" is canceled because it's circular.
3
3
  Current state is ${r.from}. Transition target state is ${r.to}
4
- `):e.emit("onBeforeTransition",r).filter(t.boolean).every(t.true)&&e.emit("onAfterTransition",r):e.emit("error",`Transition: "${s}" can't be executed. It has invalid "to": "${r.to}"`)};return o[s]=(...n)=>{if(t.array(a.from)?!a.from.includes(r()):"*"!==a.from&&a.from!==r())return e.emit("error",`Transition: "${s}" is forbidden`),r();if(i)return e.emit("error",`Transition: "${s}" can't be made. Has pending transtion: "${i}"`),r();if(!t.function(a.to))return u({transition:s,from:r(),to:a.to}),r();let o=a.to(...n);return t.promise(o)?(i=s,o.then(t=>(i=void 0,u({transition:s,from:r(),to:t,args:n}),r()))):(u({transition:s,from:r(),to:o,args:n}),r())},l},make:()=>o};return l})(p,y),Object.entries(i).forEach(([t,e])=>s.register(t,e)),s.make()),d=(o=e.plugins,a={},u=n({init(t){p.listen("init",t)},onBeforeTransition:t=>p.listen("onBeforeTransition",t),onAfterTransition:t=>p.listen("onAfterTransition",t)},y),c=f={register(t){let{name:e,api:r}=t(u);return e in a&&p.emit("error",`There are at least two plugins with the same name: "${e}"`),a[e]=r,f},make:()=>a},(null!=o?o:[]).forEach(c.register),c.make());return p.emit("init",m),n(n(n({},y),d),g)};export{i as makeFsm};
4
+ `):e.emit("onBeforeTransition",r).filter(t.boolean).every(t.true)&&e.emit("onAfterTransition",r):e.emit("error",`Transition: "${s}" can't be executed. It has invalid "to": "${r.to}"`)};return o[s]=(...n)=>{if(t.array(a.from)?!a.from.includes(r()):"*"!==a.from&&a.from!==r())return e.emit("error",`Transition: "${s}" is forbidden`),r();if(i)return e.emit("error",`Transition: "${s}" can't be made. Has pending transtion: "${i}"`),r();if(!t.function(a.to))return u({transition:s,from:r(),to:a.to}),r();let o=a.to(...n);return t.promise(o)?(i=s,o.then(t=>(i=void 0,u({transition:s,from:r(),to:t,args:n}),r()))):(u({transition:s,from:r(),to:o,args:n}),r())},l},make:()=>o};return l})(y,g),Object.entries(o).forEach(([t,e])=>a.register(t,e)),a.make()),v=(l=e.plugins,u={},f=n({init(t){y.listen("init",t)},onError:t=>y.listen("error",t),onBeforeTransition:t=>y.listen("onBeforeTransition",t),onAfterTransition:t=>y.listen("onAfterTransition",t)},g),m=c={register(t){let{name:e,api:r}=t(f);return e in u&&y.emit("error",`There are at least two plugins with the same name: "${e}"`),u[e]=r,c},make:()=>u},(null!=l?l:[]).forEach(m.register),m.make());return y.emit("init",b),n(n(n({},g),v),d)};export{i as makeFsm};
package/package.json CHANGED
@@ -1,31 +1,24 @@
1
1
  {
2
2
  "name": "@uuxxx/fsm",
3
- "version": "1.2.3",
4
- "author": "Artem Tryapichnikov <golysheeet@gmail.com>",
5
- "license": "MIT",
3
+ "version": "1.2.4",
6
4
  "description": "Javascript library for creating finite state machine",
7
- "homepage": "https://github.com/uuxxx/fsm/blob/main/README.md",
8
- "type": "module",
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/uuxxx/fsm.git"
15
- },
16
5
  "keywords": [
17
6
  "finite state machine",
18
7
  "fsm"
19
8
  ],
20
- "engines": {
21
- "node": ">= 20",
22
- "pnpm": ">= 10"
9
+ "homepage": "https://github.com/uuxxx/fsm/blob/main/README.md",
10
+ "license": "MIT",
11
+ "author": "Artem Tryapichnikov <golysheeet@gmail.com>",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/uuxxx/fsm.git"
23
15
  },
24
16
  "files": [
25
17
  "dist",
26
18
  "README.md",
27
19
  "LICENSE"
28
20
  ],
21
+ "type": "module",
29
22
  "exports": {
30
23
  ".": {
31
24
  "types": "./dist/index.d.ts",
@@ -36,15 +29,22 @@
36
29
  "default": "./dist/history-plugin.js"
37
30
  }
38
31
  },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@uuxxx/utils": "^0.0.3"
37
+ },
39
38
  "devDependencies": {
40
39
  "@babel/core": "^7.28.4",
41
40
  "@babel/preset-env": "^7.28.3",
42
41
  "@babel/preset-typescript": "^7.27.1",
43
42
  "@changesets/cli": "^2.29.7",
44
43
  "@swc/core": "^1.13.5",
45
- "eslint": "^9.37.0",
46
- "eslint-config-xo-typescript": "^9.0.0",
47
44
  "lefthook": "^2.0.2",
45
+ "oxfmt": "^0.41.0",
46
+ "oxlint": "^1.0.0",
47
+ "oxlint-tsgolint": "^0.17.0",
48
48
  "rolldown": "^1.0.0-beta.41",
49
49
  "rolldown-plugin-dts": "^0.22.1",
50
50
  "rollup-plugin-swc3": "^0.12.1",
@@ -53,16 +53,19 @@
53
53
  "typescript": "^5.9.3",
54
54
  "vitest": "^4.0.18"
55
55
  },
56
- "dependencies": {
57
- "@uuxxx/utils": "^0.0.3"
56
+ "engines": {
57
+ "node": ">= 20",
58
+ "pnpm": ">= 10"
58
59
  },
59
60
  "scripts": {
60
61
  "build": "rolldown -c rolldown.config.ts",
61
62
  "changeset:version": "changeset version && git add --all && git commit -m 'chore: bump'",
62
63
  "changeset:publish": "changeset publish",
63
64
  "test": "vitest run",
64
- "lint": "eslint",
65
- "lint:fix": "eslint --fix",
65
+ "lint": "oxlint",
66
+ "lint:fix": "oxlint --fix",
67
+ "fmt": "oxfmt --write .",
68
+ "fmt:check": "oxfmt --check .",
66
69
  "check:types": "tsc --noEmit"
67
70
  }
68
71
  }