@longzai-intelligence-state-machine/core 0.0.0-stage → 0.0.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.
- package/dist/index.d.ts +663 -0
- package/dist/index.js +1 -0
- package/package.json +40 -4
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
import { Result } from "@longzai-intelligence-shared-kernel/core";
|
|
2
|
+
//#region src/definition.types.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* 状态机定义层类型集
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* 定义层是纯数据契约,由初始状态、状态元信息与流转规则构成,不携带任何运行时状态
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 状态守卫函数
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* 守卫在迁移提交前求值,返回 false 时本次迁移被拒绝,状态保持不变
|
|
14
|
+
* @typeParam C - 实例上下文类型
|
|
15
|
+
* @typeParam E - 事件名类型
|
|
16
|
+
* @param context - 实例上下文
|
|
17
|
+
* @param event - 触发事件,目标驱动迁移未携带事件时为 null
|
|
18
|
+
*/
|
|
19
|
+
type StateGuard<C, E> = (context: C, event: E | null) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* 状态动作函数
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* 动作在迁移提交后执行,此时状态已更新为目标状态,动作内抛出的异常向上透传
|
|
25
|
+
* @typeParam C - 实例上下文类型
|
|
26
|
+
* @typeParam E - 事件名类型
|
|
27
|
+
* @param context - 实例上下文
|
|
28
|
+
* @param event - 触发事件,目标驱动迁移未携带事件时为 null
|
|
29
|
+
*/
|
|
30
|
+
type StateAction<C, E> = (context: C, event: E | null) => void;
|
|
31
|
+
/**
|
|
32
|
+
* 流转规则
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* 描述一条从起始状态到目标状态的迁移通道,可选携带事件名、守卫与动作
|
|
36
|
+
* @typeParam S - 状态名类型
|
|
37
|
+
* @typeParam E - 事件名类型
|
|
38
|
+
* @typeParam C - 实例上下文类型
|
|
39
|
+
*/
|
|
40
|
+
type TransitionConfig<S extends string, E extends string, C> = {
|
|
41
|
+
/**
|
|
42
|
+
* 起始状态
|
|
43
|
+
*/
|
|
44
|
+
from: S;
|
|
45
|
+
/**
|
|
46
|
+
* 目标状态
|
|
47
|
+
*/
|
|
48
|
+
to: S;
|
|
49
|
+
/**
|
|
50
|
+
* 触发事件名
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* 缺省时该规则仅可经实例层的目标驱动迁移触达
|
|
54
|
+
*/
|
|
55
|
+
event?: E;
|
|
56
|
+
/**
|
|
57
|
+
* 状态守卫
|
|
58
|
+
*
|
|
59
|
+
* @default 不设守卫
|
|
60
|
+
*/
|
|
61
|
+
guard?: StateGuard<C, E>;
|
|
62
|
+
/**
|
|
63
|
+
* 迁移提交后的动作
|
|
64
|
+
*
|
|
65
|
+
* @default 不设动作
|
|
66
|
+
*/
|
|
67
|
+
action?: StateAction<C, E>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* 状态元信息
|
|
71
|
+
*/
|
|
72
|
+
type StateMeta = {
|
|
73
|
+
/**
|
|
74
|
+
* 终态标记
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* 终态不再接受任何迁移,无出边的状态自动视为终态,无需显式标记
|
|
78
|
+
* @default false
|
|
79
|
+
*/
|
|
80
|
+
terminal?: boolean;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* 状态机定义
|
|
84
|
+
*
|
|
85
|
+
* @remarks
|
|
86
|
+
* 定义为开发期静态数据,建议书写时保留字面量类型推断以便类型层校验状态名与事件名
|
|
87
|
+
* @typeParam S - 状态名类型
|
|
88
|
+
* @typeParam E - 事件名类型
|
|
89
|
+
* @typeParam C - 实例上下文类型
|
|
90
|
+
*/
|
|
91
|
+
type StateMachineDefinition<S extends string, E extends string, C> = {
|
|
92
|
+
/**
|
|
93
|
+
* 初始状态
|
|
94
|
+
*/
|
|
95
|
+
initial: S;
|
|
96
|
+
/**
|
|
97
|
+
* 状态元信息表
|
|
98
|
+
*
|
|
99
|
+
* @remarks
|
|
100
|
+
* 键为状态名,未列出的状态取默认元信息
|
|
101
|
+
* @default 空表
|
|
102
|
+
*/
|
|
103
|
+
states?: Readonly<Partial<Record<S, StateMeta>>>;
|
|
104
|
+
/**
|
|
105
|
+
* 流转规则列表
|
|
106
|
+
*/
|
|
107
|
+
transitions: readonly TransitionConfig<S, E, C>[];
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/validate.utils.d.ts
|
|
111
|
+
/**
|
|
112
|
+
* 定义问题严重级别
|
|
113
|
+
*
|
|
114
|
+
* @remarks
|
|
115
|
+
* `error` 表示定义不可用,`warning` 表示定义可用但存在可疑之处
|
|
116
|
+
*/
|
|
117
|
+
type DefinitionIssueSeverity = 'error' | 'warning';
|
|
118
|
+
/**
|
|
119
|
+
* 定义问题种类标识
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* `duplicate-route` 为同一起始状态与同一事件对应多条流转规则
|
|
123
|
+
* `unreachable-state` 为自初始状态出发经流转规则不可达的状态
|
|
124
|
+
* `no-terminal` 为全定义不存在任何终态
|
|
125
|
+
*/
|
|
126
|
+
type DefinitionIssueKind = 'duplicate-route' | 'unreachable-state' | 'no-terminal';
|
|
127
|
+
/**
|
|
128
|
+
* 定义问题条目
|
|
129
|
+
*/
|
|
130
|
+
type DefinitionIssue = {
|
|
131
|
+
/**
|
|
132
|
+
* 问题严重级别
|
|
133
|
+
*/
|
|
134
|
+
severity: DefinitionIssueSeverity;
|
|
135
|
+
/**
|
|
136
|
+
* 问题种类标识
|
|
137
|
+
*/
|
|
138
|
+
kind: DefinitionIssueKind;
|
|
139
|
+
/**
|
|
140
|
+
* 人读问题描述
|
|
141
|
+
*/
|
|
142
|
+
message: string;
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* 校验状态机定义
|
|
146
|
+
*
|
|
147
|
+
* @remarks
|
|
148
|
+
* 校验项分两类,`error` 级为事件路由歧义,`warning` 级为不可达状态与无终态
|
|
149
|
+
* 全部问题按上述顺序汇总,存在 `error` 级问题时返回失败并只携带 `error` 级清单
|
|
150
|
+
* @typeParam S - 状态名类型
|
|
151
|
+
* @typeParam E - 事件名类型
|
|
152
|
+
* @typeParam C - 实例上下文类型
|
|
153
|
+
* @param definition - 待校验的状态机定义
|
|
154
|
+
* @returns 无 `error` 级问题时返回全量问题清单,否则返回定义校验失败错误
|
|
155
|
+
*/
|
|
156
|
+
declare function validateDefinition<S extends string, E extends string, C>(definition: StateMachineDefinition<S, E, C>): Result<readonly DefinitionIssue[], DefinitionValidationError>;
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/state-machine.errors.d.ts
|
|
159
|
+
/**
|
|
160
|
+
* 状态机错误基类
|
|
161
|
+
*/
|
|
162
|
+
declare class StateMachineError extends Error {
|
|
163
|
+
/**
|
|
164
|
+
* 创建状态机错误
|
|
165
|
+
*
|
|
166
|
+
* @param message - 人读错误描述
|
|
167
|
+
*/
|
|
168
|
+
constructor(message: string);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 非法迁移错误
|
|
172
|
+
*
|
|
173
|
+
* @remarks
|
|
174
|
+
* 目标驱动迁移在流转表中找不到对应规则时产生
|
|
175
|
+
*/
|
|
176
|
+
declare class InvalidTransitionError extends StateMachineError {
|
|
177
|
+
/**
|
|
178
|
+
* 起始状态
|
|
179
|
+
*/
|
|
180
|
+
readonly from: string;
|
|
181
|
+
/**
|
|
182
|
+
* 目标状态
|
|
183
|
+
*/
|
|
184
|
+
readonly to: string;
|
|
185
|
+
/**
|
|
186
|
+
* 创建非法迁移错误
|
|
187
|
+
*
|
|
188
|
+
* @param from - 起始状态
|
|
189
|
+
* @param to - 目标状态
|
|
190
|
+
*/
|
|
191
|
+
constructor(from: string, to: string);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 未路由事件错误
|
|
195
|
+
*
|
|
196
|
+
* @remarks
|
|
197
|
+
* 事件驱动迁移在当前状态下找不到事件路由时产生
|
|
198
|
+
*/
|
|
199
|
+
declare class UnhandledEventError extends StateMachineError {
|
|
200
|
+
/**
|
|
201
|
+
* 事件发生时所处的状态
|
|
202
|
+
*/
|
|
203
|
+
readonly state: string;
|
|
204
|
+
/**
|
|
205
|
+
* 未被路由的事件
|
|
206
|
+
*/
|
|
207
|
+
readonly event: string;
|
|
208
|
+
/**
|
|
209
|
+
* 创建未路由事件错误
|
|
210
|
+
*
|
|
211
|
+
* @param state - 事件发生时所处的状态
|
|
212
|
+
* @param event - 未被路由的事件
|
|
213
|
+
*/
|
|
214
|
+
constructor(state: string, event: string);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 守卫拒绝错误
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* 流转规则存在但守卫求值未放行时产生,状态保持不变
|
|
221
|
+
*/
|
|
222
|
+
declare class GuardRejectedError extends StateMachineError {
|
|
223
|
+
/**
|
|
224
|
+
* 起始状态
|
|
225
|
+
*/
|
|
226
|
+
readonly from: string;
|
|
227
|
+
/**
|
|
228
|
+
* 目标状态
|
|
229
|
+
*/
|
|
230
|
+
readonly to: string;
|
|
231
|
+
/**
|
|
232
|
+
* 触发事件
|
|
233
|
+
*
|
|
234
|
+
* @remarks
|
|
235
|
+
* 目标驱动迁移未携带事件时为 null
|
|
236
|
+
*/
|
|
237
|
+
readonly event: string | null;
|
|
238
|
+
/**
|
|
239
|
+
* 创建守卫拒绝错误
|
|
240
|
+
*
|
|
241
|
+
* @param from - 起始状态
|
|
242
|
+
* @param to - 目标状态
|
|
243
|
+
* @param event - 触发事件,目标驱动迁移未携带事件时传 null
|
|
244
|
+
*/
|
|
245
|
+
constructor(from: string, to: string, event: string | null);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 终态封锁错误
|
|
249
|
+
*
|
|
250
|
+
* @remarks
|
|
251
|
+
* 实例已进入终态后再发起任何迁移时产生
|
|
252
|
+
*/
|
|
253
|
+
declare class TerminalStateReachedError extends StateMachineError {
|
|
254
|
+
/**
|
|
255
|
+
* 已进入的终态
|
|
256
|
+
*/
|
|
257
|
+
readonly state: string;
|
|
258
|
+
/**
|
|
259
|
+
* 创建终态封锁错误
|
|
260
|
+
*
|
|
261
|
+
* @param state - 已进入的终态
|
|
262
|
+
*/
|
|
263
|
+
constructor(state: string);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 未知状态错误
|
|
267
|
+
*
|
|
268
|
+
* @remarks
|
|
269
|
+
* 引用了定义状态集之外的状态时产生,见于快照恢复与实例创建
|
|
270
|
+
*/
|
|
271
|
+
declare class UnknownStateError extends StateMachineError {
|
|
272
|
+
/**
|
|
273
|
+
* 定义状态集之外的状态
|
|
274
|
+
*/
|
|
275
|
+
readonly state: string;
|
|
276
|
+
/**
|
|
277
|
+
* 创建未知状态错误
|
|
278
|
+
*
|
|
279
|
+
* @param state - 定义状态集之外的状态
|
|
280
|
+
*/
|
|
281
|
+
constructor(state: string);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* 定义校验失败错误
|
|
285
|
+
*
|
|
286
|
+
* @remarks
|
|
287
|
+
* 定义存在 `error` 级校验问题时产生,携带全部 `error` 级问题清单
|
|
288
|
+
*/
|
|
289
|
+
declare class DefinitionValidationError extends StateMachineError {
|
|
290
|
+
/**
|
|
291
|
+
* 令校验失败的 `error` 级问题清单
|
|
292
|
+
*/
|
|
293
|
+
readonly issues: readonly DefinitionIssue[];
|
|
294
|
+
/**
|
|
295
|
+
* 创建定义校验失败错误
|
|
296
|
+
*
|
|
297
|
+
* @param issues - 令校验失败的 `error` 级问题清单
|
|
298
|
+
*/
|
|
299
|
+
constructor(issues: readonly DefinitionIssue[]);
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region src/machine.types.d.ts
|
|
303
|
+
/**
|
|
304
|
+
* 状态机规格层类型集
|
|
305
|
+
*
|
|
306
|
+
* @remarks
|
|
307
|
+
* 规格层基于定义构建索引并提供流转查询,不持有任何实例状态
|
|
308
|
+
*/
|
|
309
|
+
/**
|
|
310
|
+
* 状态机规格
|
|
311
|
+
*
|
|
312
|
+
* @remarks
|
|
313
|
+
* 规格可安全跨实例共享,同一规格可派生任意多个相互隔离的实例
|
|
314
|
+
* @typeParam S - 状态名类型
|
|
315
|
+
* @typeParam E - 事件名类型
|
|
316
|
+
* @typeParam C - 实例上下文类型
|
|
317
|
+
*/
|
|
318
|
+
type StateMachine<S extends string, E extends string, C> = {
|
|
319
|
+
/**
|
|
320
|
+
* 构建规格所用的状态机定义
|
|
321
|
+
*/
|
|
322
|
+
readonly definition: StateMachineDefinition<S, E, C>;
|
|
323
|
+
/**
|
|
324
|
+
* 全量状态清单
|
|
325
|
+
*
|
|
326
|
+
* @remarks
|
|
327
|
+
* 初始状态置于首位,其余按定义中的出现顺序排列
|
|
328
|
+
*/
|
|
329
|
+
readonly states: readonly S[];
|
|
330
|
+
/**
|
|
331
|
+
* 判断状态迁移是否合法
|
|
332
|
+
*
|
|
333
|
+
* @param from - 起始状态
|
|
334
|
+
* @param to - 目标状态
|
|
335
|
+
* @returns 存在 `from` 到 `to` 的流转规则时为 true
|
|
336
|
+
*/
|
|
337
|
+
canTransition(from: S, to: S): boolean;
|
|
338
|
+
/**
|
|
339
|
+
* 判断状态能否响应指定事件
|
|
340
|
+
*
|
|
341
|
+
* @param state - 起始状态
|
|
342
|
+
* @param event - 触发事件
|
|
343
|
+
* @returns 存在对应事件路由时为 true
|
|
344
|
+
*/
|
|
345
|
+
canHandle(state: S, event: E): boolean;
|
|
346
|
+
/**
|
|
347
|
+
* 列出某状态可达的全部目标状态
|
|
348
|
+
*
|
|
349
|
+
* @param state - 起始状态
|
|
350
|
+
* @returns 按定义顺序去重后的目标状态列表
|
|
351
|
+
*/
|
|
352
|
+
availableTargets(state: S): readonly S[];
|
|
353
|
+
/**
|
|
354
|
+
* 列出某状态可响应的全部事件
|
|
355
|
+
*
|
|
356
|
+
* @param state - 起始状态
|
|
357
|
+
* @returns 按定义顺序去重后的事件列表
|
|
358
|
+
*/
|
|
359
|
+
availableEvents(state: S): readonly E[];
|
|
360
|
+
/**
|
|
361
|
+
* 解析事件路由到的流转规则
|
|
362
|
+
*
|
|
363
|
+
* @param state - 起始状态
|
|
364
|
+
* @param event - 触发事件
|
|
365
|
+
* @returns 命中时返回对应流转规则,未命中时返回 `UnhandledEventError`
|
|
366
|
+
*/
|
|
367
|
+
resolveTransition(state: S, event: E): Result<TransitionConfig<S, E, C>, UnhandledEventError>;
|
|
368
|
+
/**
|
|
369
|
+
* 判断是否为终态
|
|
370
|
+
*
|
|
371
|
+
* @remarks
|
|
372
|
+
* 显式标记 `terminal` 的状态与无出边状态均为终态
|
|
373
|
+
* @param state - 待判定的状态
|
|
374
|
+
* @returns 终态为 true
|
|
375
|
+
*/
|
|
376
|
+
isTerminal(state: S): boolean;
|
|
377
|
+
};
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/instance.types.d.ts
|
|
380
|
+
/**
|
|
381
|
+
* 状态机实例层类型集
|
|
382
|
+
*
|
|
383
|
+
* @remarks
|
|
384
|
+
* 实例层持有单个状态机实例的运行时状态,提供事件驱动与目标驱动两类迁移入口
|
|
385
|
+
*/
|
|
386
|
+
/**
|
|
387
|
+
* 迁移记录
|
|
388
|
+
*
|
|
389
|
+
* @remarks
|
|
390
|
+
* 一次成功迁移的完整留痕,字段均为可 JSON 序列化数据
|
|
391
|
+
* @typeParam S - 状态名类型
|
|
392
|
+
* @typeParam E - 事件名类型
|
|
393
|
+
* @typeParam C - 实例上下文类型
|
|
394
|
+
*/
|
|
395
|
+
type TransitionRecord<S extends string, E extends string, C> = {
|
|
396
|
+
/**
|
|
397
|
+
* 发生迁移的实例标识
|
|
398
|
+
*/
|
|
399
|
+
instanceId: string;
|
|
400
|
+
/**
|
|
401
|
+
* 迁移前状态
|
|
402
|
+
*/
|
|
403
|
+
from: S;
|
|
404
|
+
/**
|
|
405
|
+
* 迁移后状态
|
|
406
|
+
*/
|
|
407
|
+
to: S;
|
|
408
|
+
/**
|
|
409
|
+
* 触发事件
|
|
410
|
+
*
|
|
411
|
+
* @remarks
|
|
412
|
+
* 目标驱动迁移未携带事件时为 null
|
|
413
|
+
*/
|
|
414
|
+
event: E | null;
|
|
415
|
+
/**
|
|
416
|
+
* 迁移完成时的实例上下文
|
|
417
|
+
*/
|
|
418
|
+
context: C;
|
|
419
|
+
/**
|
|
420
|
+
* 迁移发生时刻
|
|
421
|
+
*
|
|
422
|
+
* @remarks
|
|
423
|
+
* ISO 8601 文本形态
|
|
424
|
+
*/
|
|
425
|
+
occurredAt: string;
|
|
426
|
+
};
|
|
427
|
+
/**
|
|
428
|
+
* 状态机实例快照
|
|
429
|
+
*
|
|
430
|
+
* @remarks
|
|
431
|
+
* 快照为纯 JSON 数据,用于持久化与断点续跑,恢复时经 `restore` 回填实例
|
|
432
|
+
* @typeParam S - 状态名类型
|
|
433
|
+
* @typeParam E - 事件名类型
|
|
434
|
+
* @typeParam C - 实例上下文类型
|
|
435
|
+
*/
|
|
436
|
+
type MachineSnapshot<S extends string, E extends string, C> = {
|
|
437
|
+
/**
|
|
438
|
+
* 实例标识
|
|
439
|
+
*/
|
|
440
|
+
id: string;
|
|
441
|
+
/**
|
|
442
|
+
* 当前状态
|
|
443
|
+
*/
|
|
444
|
+
state: S;
|
|
445
|
+
/**
|
|
446
|
+
* 上一状态
|
|
447
|
+
*
|
|
448
|
+
* @remarks
|
|
449
|
+
* 实例尚无迁移时为 null
|
|
450
|
+
*/
|
|
451
|
+
previousState: S | null;
|
|
452
|
+
/**
|
|
453
|
+
* 实例上下文
|
|
454
|
+
*/
|
|
455
|
+
context: C;
|
|
456
|
+
/**
|
|
457
|
+
* 迁移历史
|
|
458
|
+
*
|
|
459
|
+
* @remarks
|
|
460
|
+
* 最新在后,容量受创建时 `maxHistory` 约束
|
|
461
|
+
*/
|
|
462
|
+
history: readonly TransitionRecord<S, E, C>[];
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* 状态变更监听器
|
|
466
|
+
*
|
|
467
|
+
* @remarks
|
|
468
|
+
* 迁移提交并执行完动作后同步通知,监听器内抛出的异常向上透传
|
|
469
|
+
* @typeParam S - 状态名类型
|
|
470
|
+
* @typeParam E - 事件名类型
|
|
471
|
+
* @typeParam C - 实例上下文类型
|
|
472
|
+
* @param record - 本次迁移的留痕记录
|
|
473
|
+
*/
|
|
474
|
+
type StateChangeListener<S extends string, E extends string, C> = (record: TransitionRecord<S, E, C>) => void;
|
|
475
|
+
/**
|
|
476
|
+
* 实例创建选项
|
|
477
|
+
*
|
|
478
|
+
* @typeParam S - 状态名类型
|
|
479
|
+
* @typeParam C - 实例上下文类型
|
|
480
|
+
*/
|
|
481
|
+
type MachineInstanceOptions<S extends string, C> = {
|
|
482
|
+
/**
|
|
483
|
+
* 实例标识
|
|
484
|
+
*
|
|
485
|
+
* @remarks
|
|
486
|
+
* 同一规格下多实例以标识区分,标识随迁移记录与快照携带
|
|
487
|
+
*/
|
|
488
|
+
id: string;
|
|
489
|
+
/**
|
|
490
|
+
* 初始状态
|
|
491
|
+
*
|
|
492
|
+
* @default 取定义的 `initial`
|
|
493
|
+
*/
|
|
494
|
+
initialState?: S;
|
|
495
|
+
/**
|
|
496
|
+
* 实例上下文
|
|
497
|
+
*
|
|
498
|
+
* @remarks
|
|
499
|
+
* 供守卫与动作读写,随快照一并序列化
|
|
500
|
+
*/
|
|
501
|
+
context: C;
|
|
502
|
+
/**
|
|
503
|
+
* 迁移历史容量上限
|
|
504
|
+
*
|
|
505
|
+
* @default 0
|
|
506
|
+
*/
|
|
507
|
+
maxHistory?: number;
|
|
508
|
+
};
|
|
509
|
+
/**
|
|
510
|
+
* 状态机实例
|
|
511
|
+
*
|
|
512
|
+
* @remarks
|
|
513
|
+
* 实例之间相互隔离,全部迁移入口以 `Result` 信封返回结果
|
|
514
|
+
* @typeParam S - 状态名类型
|
|
515
|
+
* @typeParam E - 事件名类型
|
|
516
|
+
* @typeParam C - 实例上下文类型
|
|
517
|
+
*/
|
|
518
|
+
type MachineInstance<S extends string, E extends string, C> = {
|
|
519
|
+
/**
|
|
520
|
+
* 实例标识
|
|
521
|
+
*/
|
|
522
|
+
readonly id: string;
|
|
523
|
+
/**
|
|
524
|
+
* 实例所基于的规格
|
|
525
|
+
*/
|
|
526
|
+
readonly machine: StateMachine<S, E, C>;
|
|
527
|
+
/**
|
|
528
|
+
* 以事件驱动一次迁移
|
|
529
|
+
*
|
|
530
|
+
* @param event - 触发事件
|
|
531
|
+
* @returns 成功时返回迁移记录,失败时返回状态机错误
|
|
532
|
+
*/
|
|
533
|
+
send(event: E): Result<TransitionRecord<S, E, C>, StateMachineError>;
|
|
534
|
+
/**
|
|
535
|
+
* 以目标状态驱动一次迁移
|
|
536
|
+
*
|
|
537
|
+
* @remarks
|
|
538
|
+
* 从当前状态到目标状态存在多条规则时,取定义顺序上首条守卫放行的规则
|
|
539
|
+
* @param target - 目标状态
|
|
540
|
+
* @returns 成功时返回迁移记录,失败时返回状态机错误
|
|
541
|
+
*/
|
|
542
|
+
transitionTo(target: S): Result<TransitionRecord<S, E, C>, StateMachineError>;
|
|
543
|
+
/**
|
|
544
|
+
* 读取当前状态
|
|
545
|
+
*
|
|
546
|
+
* @returns 当前状态
|
|
547
|
+
*/
|
|
548
|
+
getState(): S;
|
|
549
|
+
/**
|
|
550
|
+
* 读取实例上下文
|
|
551
|
+
*
|
|
552
|
+
* @returns 实例上下文引用
|
|
553
|
+
*/
|
|
554
|
+
getContext(): C;
|
|
555
|
+
/**
|
|
556
|
+
* 判断当前状态是否为终态
|
|
557
|
+
*
|
|
558
|
+
* @returns 终态为 true
|
|
559
|
+
*/
|
|
560
|
+
isTerminal(): boolean;
|
|
561
|
+
/**
|
|
562
|
+
* 读取实例快照
|
|
563
|
+
*
|
|
564
|
+
* @returns 可 JSON 序列化的实例快照
|
|
565
|
+
*/
|
|
566
|
+
getSnapshot(): MachineSnapshot<S, E, C>;
|
|
567
|
+
/**
|
|
568
|
+
* 从快照恢复实例状态
|
|
569
|
+
*
|
|
570
|
+
* @remarks
|
|
571
|
+
* 恢复会整体覆盖当前状态、上一状态、上下文与迁移历史
|
|
572
|
+
* @param snapshot - 此前经 `getSnapshot` 取出的快照
|
|
573
|
+
* @returns 快照状态在定义状态集内时成功,否则返回 `UnknownStateError`
|
|
574
|
+
*/
|
|
575
|
+
restore(snapshot: MachineSnapshot<S, E, C>): Result<void, StateMachineError>;
|
|
576
|
+
/**
|
|
577
|
+
* 订阅状态变更
|
|
578
|
+
*
|
|
579
|
+
* @param listener - 状态变更监听器
|
|
580
|
+
* @returns 退订函数,调用后不再收到通知
|
|
581
|
+
*/
|
|
582
|
+
subscribe(listener: StateChangeListener<S, E, C>): () => void;
|
|
583
|
+
};
|
|
584
|
+
//#endregion
|
|
585
|
+
//#region src/state-name.utils.d.ts
|
|
586
|
+
/**
|
|
587
|
+
* 状态名键收窄守卫
|
|
588
|
+
*
|
|
589
|
+
* @remarks
|
|
590
|
+
* `Object.keys` 只产出字符串,而状态名的运行时形态本就是字符串
|
|
591
|
+
* 本守卫仅校验候选键为字符串,用于把映射表键收窄回状态名类型
|
|
592
|
+
* @typeParam S - 状态名类型
|
|
593
|
+
* @param key - 映射表读出的候选键
|
|
594
|
+
* @returns 候选键为字符串时为真
|
|
595
|
+
*/
|
|
596
|
+
declare function isStateName<S extends string>(key: string): key is S;
|
|
597
|
+
//#endregion
|
|
598
|
+
//#region src/adjacency.utils.d.ts
|
|
599
|
+
/**
|
|
600
|
+
* 邻接表形态的状态迁移表
|
|
601
|
+
*
|
|
602
|
+
* @typeParam S - 状态名类型
|
|
603
|
+
*/
|
|
604
|
+
type StateAdjacency<S extends string> = Readonly<Record<S, readonly S[]>>;
|
|
605
|
+
/**
|
|
606
|
+
* 邻接表转换选项
|
|
607
|
+
*
|
|
608
|
+
* @typeParam S - 状态名类型
|
|
609
|
+
*/
|
|
610
|
+
type FromAdjacencyOptions<S extends string> = {
|
|
611
|
+
/**
|
|
612
|
+
* 初始状态
|
|
613
|
+
*/
|
|
614
|
+
initial: S;
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* 邻接表转换入口
|
|
618
|
+
*
|
|
619
|
+
* @remarks
|
|
620
|
+
* 产出的流转规则不携带事件名,仅可经实例层的目标驱动迁移触达
|
|
621
|
+
* 空目标列表或缺失键的起始状态无出边,自动视为终态
|
|
622
|
+
* @typeParam S - 状态名类型
|
|
623
|
+
* @param adjacency - 邻接表形态的状态迁移表
|
|
624
|
+
* @param options - 转换选项,须提供初始状态
|
|
625
|
+
* @returns 状态机定义
|
|
626
|
+
*/
|
|
627
|
+
declare function fromAdjacency<S extends string>(adjacency: StateAdjacency<S>, options: FromAdjacencyOptions<S>): StateMachineDefinition<S, never, unknown>;
|
|
628
|
+
//#endregion
|
|
629
|
+
//#region src/machine.factory.d.ts
|
|
630
|
+
/**
|
|
631
|
+
* 创建状态机规格
|
|
632
|
+
*
|
|
633
|
+
* @remarks
|
|
634
|
+
* 创建时执行定义校验,存在 `error` 级问题时抛出 `DefinitionValidationError` 快速失败
|
|
635
|
+
* 定义属开发期静态数据,非法定义视为程序缺陷而非运行时错误
|
|
636
|
+
* `warning` 级问题不阻断创建,可另行调用 `validateDefinition` 查看
|
|
637
|
+
* @typeParam S - 状态名类型
|
|
638
|
+
* @typeParam E - 事件名类型
|
|
639
|
+
* @typeParam C - 实例上下文类型
|
|
640
|
+
* @param definition - 状态机定义
|
|
641
|
+
* @returns 状态机规格
|
|
642
|
+
* @throws {@link DefinitionValidationError} 定义存在 `error` 级校验问题时抛出
|
|
643
|
+
*/
|
|
644
|
+
declare function createMachine<S extends string, E extends string, C>(definition: StateMachineDefinition<S, E, C>): StateMachine<S, E, C>;
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/instance.factory.d.ts
|
|
647
|
+
/**
|
|
648
|
+
* 创建状态机实例
|
|
649
|
+
*
|
|
650
|
+
* @remarks
|
|
651
|
+
* 实例与规格解耦,同一规格可派生多个相互隔离的实例
|
|
652
|
+
* 迁移提交顺序为状态更新、动作执行、历史留痕、监听器通知
|
|
653
|
+
* @typeParam S - 状态名类型
|
|
654
|
+
* @typeParam E - 事件名类型
|
|
655
|
+
* @typeParam C - 实例上下文类型
|
|
656
|
+
* @param machine - 实例所基于的规格
|
|
657
|
+
* @param options - 实例创建选项
|
|
658
|
+
* @returns 状态机实例
|
|
659
|
+
* @throws {@link UnknownStateError} 显式指定的初始状态不在定义状态集内时抛出
|
|
660
|
+
*/
|
|
661
|
+
declare function createMachineInstance<S extends string, E extends string, C>(machine: StateMachine<S, E, C>, options: MachineInstanceOptions<S, C>): MachineInstance<S, E, C>;
|
|
662
|
+
//#endregion
|
|
663
|
+
export { DefinitionIssue, DefinitionIssueKind, DefinitionIssueSeverity, DefinitionValidationError, FromAdjacencyOptions, GuardRejectedError, InvalidTransitionError, MachineInstance, MachineInstanceOptions, MachineSnapshot, StateAction, StateAdjacency, StateChangeListener, StateGuard, StateMachine, StateMachineDefinition, StateMachineError, StateMeta, TerminalStateReachedError, TransitionConfig, TransitionRecord, UnhandledEventError, UnknownStateError, createMachine, createMachineInstance, fromAdjacency, isStateName, validateDefinition };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{failure as e,success as t}from"@longzai-intelligence-shared-kernel/core";var n=class extends Error{constructor(e){super(e),this.name=`StateMachineError`}},r=class extends n{from;to;constructor(e,t){super(`无效的状态迁移:${e} → ${t}`),this.name=`InvalidTransitionError`,this.from=e,this.to=t}},i=class extends n{state;event;constructor(e,t){super(`状态 ${e} 无法响应事件 ${t}`),this.name=`UnhandledEventError`,this.state=e,this.event=t}},a=class extends n{from;to;event;constructor(e,t,n){super(`迁移被守卫拒绝:${e} → ${t}`),this.name=`GuardRejectedError`,this.from=e,this.to=t,this.event=n}},o=class extends n{state;constructor(e){super(`状态 ${e} 已是终态,不再接受迁移`),this.name=`TerminalStateReachedError`,this.state=e}},s=class extends n{state;constructor(e){super(`未知状态:${e}`),this.name=`UnknownStateError`,this.state=e}},c=class extends n{issues;constructor(e){super(`状态机定义校验未通过,共 ${e.length} 项错误级问题`),this.name=`DefinitionValidationError`,this.issues=e}};function l(e){return typeof e==`string`}function u(e,t){let n=[];for(let t of Object.keys(e)){if(!l(t))continue;let r=e[t];for(let e of r)n.push({from:t,to:e})}return{initial:t.initial,transitions:n}}function d(n){let r=[],i=/* @__PURE__ */ new Map;for(let e of n.transitions){let t=i.get(e.from)??[];t.push(e),i.set(e.from,t)}let a=/* @__PURE__ */ new Map;for(let e of n.transitions){if(e.event===void 0)continue;let t=`${e.from}::${e.event}`,n=a.get(t)??{from:e.from,event:e.event,count:0};n.count+=1,a.set(t,n)}for(let e of a.values())e.count>1&&r.push({severity:`error`,kind:`duplicate-route`,message:`起始状态 ${e.from} 的事件 ${e.event} 对应 ${e.count} 条流转规则,事件路由存在歧义`});let o=/* @__PURE__ */ new Set([n.initial]),s=[n.initial];for(;s.length>0;){let e=s.shift();if(e!==void 0)for(let t of i.get(e)??[])o.has(t.to)||(o.add(t.to),s.push(t.to))}let u=/* @__PURE__ */ new Set([n.initial]);for(let e of Object.keys(n.states??{}))l(e)&&u.add(e);for(let e of n.transitions)u.add(e.from),u.add(e.to);for(let e of u)o.has(e)||r.push({severity:`warning`,kind:`unreachable-state`,message:`状态 ${e} 自初始状态 ${n.initial} 出发不可达`});let d=!1;for(let e of u){let t=n.states?.[e]?.terminal===!0,r=i.get(e)??[];if(t||r.length===0){d=!0;break}}d||r.push({severity:`warning`,kind:`no-terminal`,message:`状态机定义不存在任何终态,请确认是否为刻意设计的循环状态机`});let f=r.filter(e=>e.severity===`error`);return f.length>0?e(new c(f)):t(r)}function f(n){let r=d(n);if(r.isFailure)throw r.error;let a=/* @__PURE__ */ new Map,o=/* @__PURE__ */ new Map,s=[n.initial],c=/* @__PURE__ */ new Set([n.initial]);for(let e of Object.keys(n.states??{}))l(e)&&!c.has(e)&&(c.add(e),s.push(e));for(let e of n.transitions){for(let t of[e.from,e.to])c.has(t)||(c.add(t),s.push(t));let t=a.get(e.from)??[];t.push(e),a.set(e.from,t),e.event!==void 0&&o.set(`${e.from}::${e.event}`,e)}return{definition:n,states:s,canTransition(e,t){return(a.get(e)??[]).some(e=>e.to===t)},canHandle(e,t){return o.has(`${e}::${t}`)},availableTargets(e){let t=/* @__PURE__ */ new Set,n=[];for(let r of a.get(e)??[])t.has(r.to)||(t.add(r.to),n.push(r.to));return n},availableEvents(e){let t=/* @__PURE__ */ new Set,n=[];for(let r of a.get(e)??[])r.event!==void 0&&!t.has(r.event)&&(t.add(r.event),n.push(r.event));return n},resolveTransition(n,r){let a=o.get(`${n}::${r}`);return a===void 0?e(new i(n,r)):t(a)},isTerminal(e){return n.states?.[e]?.terminal===!0||(a.get(e)??[]).length===0}}}function p(n,i){let c=i.initialState??n.definition.initial;if(!n.states.includes(c))throw new s(c);let l=c,u=null,d=i.context,f=[],p=i.maxHistory??0,m=[];function h(e,t){u=l,l=e.to,e.action?.(d,t);let n={instanceId:i.id,from:u,to:l,event:t,context:d,occurredAt:(/* @__PURE__ */ new Date()).toISOString()};if(p>0)for(f.push(n);f.length>p;)f.shift();for(let e of m)e(n);return n}return{id:i.id,machine:n,send(r){if(n.isTerminal(l))return e(new o(l));let i=n.resolveTransition(l,r);if(i.isFailure)return e(i.error);let s=i.value;return s.guard?.(d,r)===!1?e(new a(s.from,s.to,r)):t(h(s,r))},transitionTo(i){if(n.isTerminal(l))return e(new o(l));let s=n.definition.transitions.filter(e=>e.from===l&&e.to===i);if(s.length===0)return e(new r(l,i));let c=s.find(e=>e.guard?.(d,null)!==!1);return c===void 0?e(new a(l,i,null)):t(h(c,null))},getState(){return l},getContext(){return d},isTerminal(){return n.isTerminal(l)},getSnapshot(){return{id:i.id,state:l,previousState:u,context:d,history:[...f]}},restore(r){if(!n.states.includes(r.state))return e(new s(r.state));for(l=r.state,u=r.previousState,d=r.context,f.length=0,f.push(...r.history);f.length>p;)f.shift();return t(void 0)},subscribe(e){return m.push(e),()=>{let t=m.indexOf(e);t>=0&&m.splice(t,1)}}}}export{c as DefinitionValidationError,a as GuardRejectedError,r as InvalidTransitionError,n as StateMachineError,o as TerminalStateReachedError,i as UnhandledEventError,s as UnknownStateError,f as createMachine,p as createMachineInstance,u as fromAdjacency,l as isStateName,d as validateDefinition};
|
package/package.json
CHANGED
|
@@ -1,6 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longzai-intelligence-state-machine/core",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Longzai Intelligence 状态机核心能力包",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fsm",
|
|
7
|
+
"guard",
|
|
8
|
+
"longzai-intelligence",
|
|
9
|
+
"state-machine",
|
|
10
|
+
"transition-table"
|
|
11
|
+
],
|
|
12
|
+
"license": "UNLICENSED",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "lzi-builder",
|
|
27
|
+
"build:prod": "NODE_ENV=production bun run build",
|
|
28
|
+
"prepublishOnly": "bun run build:prod",
|
|
29
|
+
"typecheck": "lzi-tsgo typecheck tsconfig/app.json && lzi-tsgo typecheck tsconfig/test.json",
|
|
30
|
+
"typecheck:app": "lzi-tsgo typecheck tsconfig/app.json",
|
|
31
|
+
"typecheck:test": "lzi-tsgo typecheck tsconfig/test.json",
|
|
32
|
+
"lint": "oxlint && oxfmt --check",
|
|
33
|
+
"lint:fix": "oxlint --fix && oxfmt",
|
|
34
|
+
"test": "lzi-bun-cli test",
|
|
35
|
+
"test:watch": "lzi-bun-cli test --watch",
|
|
36
|
+
"test:coverage": "lzi-bun-cli test --coverage",
|
|
37
|
+
"clean": "lzi-dev-cli clean"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@longzai-intelligence-shared-kernel/core": "^0.1.4"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/README.md
DELETED