@floken-io/engine 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/CHANGELOG.md +39 -0
- package/LICENSE +153 -0
- package/README.md +138 -0
- package/dist/chunk-RMCROXES.js +376 -0
- package/dist/conformance.d.ts +162 -0
- package/dist/conformance.js +711 -0
- package/dist/index.d.ts +1948 -0
- package/dist/index.js +3645 -0
- package/dist/spi-BABH0Tcs.d.ts +635 -0
- package/package.json +45 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import { ProcessDefinition, ApproverSpec } from '@floken-io/moddle';
|
|
2
|
+
|
|
3
|
+
/** 当前快照结构版本。新增字段破坏兼容时 +1,并**必须**在 `STATE_MIGRATIONS` 登记迁移。 */
|
|
4
|
+
declare const STATE_SCHEMA_VERSION = 1;
|
|
5
|
+
type InstanceStatus = 'running' | 'suspended' | 'completed' | 'terminated' | 'cancelled';
|
|
6
|
+
declare const INSTANCE_STATUSES: readonly ["running", "suspended", "completed", "terminated", "cancelled"];
|
|
7
|
+
/** 终态三值:进入后 `submit()` / `deliver*()` **必须抛错**(INV-2) */
|
|
8
|
+
declare const TERMINAL_STATUSES: readonly ["completed", "terminated", "cancelled"];
|
|
9
|
+
declare function isTerminalStatus(status: InstanceStatus): boolean;
|
|
10
|
+
type TokenState = 'active' | 'waiting' | 'completed' | 'cancelled';
|
|
11
|
+
declare const TOKEN_STATES: readonly ["active", "waiting", "completed", "cancelled"];
|
|
12
|
+
/**
|
|
13
|
+
* ★ 令牌在汇聚组里的**表态**(T13)。
|
|
14
|
+
*
|
|
15
|
+
* 为什么必须单开一个字段、而不是从 `state` 推:
|
|
16
|
+
* `state` 只有四值,而「投了通过」与「投了驳回」在生命周期上是**同一件事**(都办完了),
|
|
17
|
+
* 在语义上是**两件事**。若用 `completed` / `cancelled` 兼表,那么「或签里被取消的那个人」
|
|
18
|
+
* 与「投了驳回的那个人」将不可区分 —— 事后审计答不出「谁驳回的」。
|
|
19
|
+
*
|
|
20
|
+
* ⇒ `state` 管**在不在途**,`vote` 管**投了什么**,两者正交。
|
|
21
|
+
* 投票后令牌一律 `state:'completed'`(他的办理结束了),方向记在本字段。
|
|
22
|
+
*/
|
|
23
|
+
type VoteOutcome = 'approved' | 'rejected';
|
|
24
|
+
/**
|
|
25
|
+
* ★ 令牌**正在等的东西**(T20 · `intermediateCatchEvent` / `receiveTask`)。
|
|
26
|
+
*
|
|
27
|
+
* 为什么必须落在令牌上、而不是"看节点类型临时推":
|
|
28
|
+
* ① 同一个节点可能被**多个令牌**同时等待(并行分支),"谁等到了"是令牌级事实;
|
|
29
|
+
* ② 投递要按 `name` 精确匹配,而 `name` 来自**定义** —— 定义可能被改版(AC-E10),
|
|
30
|
+
* 在令牌上留一份快照,投递判据才不随图纸漂移;
|
|
31
|
+
* ③ 它是唯一能回答「这个实例现在在等什么」的地方 —— 宿主做订阅表 / 超时扫描都要它。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 与 `state:'waiting'` 无关:那是**串行会签里还没轮到**(人已定、等前面的人办完)。
|
|
34
|
+
* 本字段是「等**外部世界**的某个消息 / 信号」,没人能替它办 —— 命名刻意不叫 `waiting`
|
|
35
|
+
* 就是为了避免这两件事被当成一件。
|
|
36
|
+
*/
|
|
37
|
+
interface TokenAwait {
|
|
38
|
+
readonly kind: 'message' | 'signal';
|
|
39
|
+
/** 消息名 / 信号名(`messageRef` / `signalRef`) */
|
|
40
|
+
readonly name: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 令牌 —— 审批流的**执行指针**。
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ 与「待办」不是一回事:一条待办 = `active` + 有 `assignee` 的令牌
|
|
46
|
+
* (详见 `runtime/loop.ts` 的 `tasksOf`)。没有 `assignee` 的在途令牌
|
|
47
|
+
* (刚分叉出来还没落定、停在等待外部消息的 catch 节点上)**不是**待办。
|
|
48
|
+
*/
|
|
49
|
+
interface Token {
|
|
50
|
+
id: string;
|
|
51
|
+
nodeId: string;
|
|
52
|
+
state: TokenState;
|
|
53
|
+
/** 分配层(`ApproverSource`)解析后落地的结果 */
|
|
54
|
+
assignee?: string;
|
|
55
|
+
/** 同节点多实例归组 —— 会签 / 票签的汇聚单元 */
|
|
56
|
+
instanceGroup?: string;
|
|
57
|
+
/** 委派(delegate)时的回归目标 */
|
|
58
|
+
returnTo?: string;
|
|
59
|
+
/**
|
|
60
|
+
* 汇聚组内的表态(T13)。**只在组内投票后才有值**;被取消 / 未表态的令牌没有它。
|
|
61
|
+
* 组解散(`instanceGroup` 被摘)后本字段**保留** —— 它是审计事实,不是组状态。
|
|
62
|
+
*/
|
|
63
|
+
vote?: VoteOutcome;
|
|
64
|
+
/**
|
|
65
|
+
* ★ 落到等待节点(成为一条待办)的时刻 —— `TaskView.createdAt` 由它派生。
|
|
66
|
+
*
|
|
67
|
+
* 为什么必须落在令牌上而不是"取 `state.startedAt` 兜底":
|
|
68
|
+
* 待办的创建时刻是**这条待办自己的事实**,加签 / 驳回重办产生的新待办各有各的时刻;
|
|
69
|
+
* 拿实例启动时刻顶替会让「这条待办挂了多久」永远算错(超时判定的输入)。
|
|
70
|
+
*
|
|
71
|
+
* 可选:由 `runtime/loop.ts` 在令牌首次落到等待节点时填(`primitives.ts` 是纯函数、没有时间源,故不填)。
|
|
72
|
+
*/
|
|
73
|
+
createdAt?: string;
|
|
74
|
+
/**
|
|
75
|
+
* ★ **并行分支标记**(T16 · D-47 的落点)。
|
|
76
|
+
*
|
|
77
|
+
* 网关分叉时写入:同一网关**同一批**分裂出的令牌及其后代共享一个 `branch` 值。
|
|
78
|
+
* 汇聚合流后**清除**(合流点之后又回到单干)。
|
|
79
|
+
*
|
|
80
|
+
* 为什么需要它:`rollbackTo`(拿回 / 撤销)的语义是「撤销下游」,
|
|
81
|
+
* 而"下游"在并行分支下必须**收缩到本分支** —— 否则 A 分支上点一次"撤销",
|
|
82
|
+
* 会把 B 分支上毫不相干的在途待办一起取消(**D-47** 记录的那处误伤)。
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ 它与 `instanceGroup` 正交:`instanceGroup` 是"**同一节点上的多个人**"(会签 / 或签),
|
|
85
|
+
* `branch` 是"**同一条并行分支**"。一个会签节点整体处在某条分支上,
|
|
86
|
+
* 故组内令牌的 `branch` 相同、而 `instanceGroup` 各异。
|
|
87
|
+
*
|
|
88
|
+
* ⚠️ 无分支(单干)的令牌**不写本字段**:此时"撤销下游"= 撤销全部在途,
|
|
89
|
+
* 与 T15 之前的既有行为一致(向后兼容,不需要迁移)。
|
|
90
|
+
*/
|
|
91
|
+
/**
|
|
92
|
+
* ★ **正在等外部消息 / 信号**(T20)—— 见 {@link TokenAwait}。
|
|
93
|
+
*
|
|
94
|
+
* - 令牌落到 `intermediateCatchEvent` / `receiveTask` 时写入,被投递唤醒时**摘掉**;
|
|
95
|
+
* - 有本字段的令牌是**稳定点**:`run-to-wait` 见到它就停(不会自己走过去);
|
|
96
|
+
* - 令牌换节点时由 `clearAssignment()` 一并清除(与办理人同一口径 —— 它是**节点级**属性)。
|
|
97
|
+
*/
|
|
98
|
+
awaiting?: TokenAwait;
|
|
99
|
+
branch?: string;
|
|
100
|
+
/**
|
|
101
|
+
* ★ **竞速组**(T21 · `eventBasedGateway`)。
|
|
102
|
+
*
|
|
103
|
+
* 事件网关分叉出来的令牌共享同一个 `race` 值;**其中一个被唤醒**时,同组其余在途令牌
|
|
104
|
+
* 一律**取消**(BPMN:只走第一个到达的事件)。没有本字段 = 不参与竞速。
|
|
105
|
+
*
|
|
106
|
+
* ⚠️ 为什么不能复用 `branch`:`branch` 是「并行分支」标记,`rollbackTo` 靠它把"撤销下游"
|
|
107
|
+
* 收缩到本分支(D-47)。竞速与它是**两件正交的事** —— 一个并行分支内部可以再有一次竞速,
|
|
108
|
+
* 混用会让"取消同批竞速分支"变成"取消整条并行分支"。
|
|
109
|
+
*/
|
|
110
|
+
race?: string;
|
|
111
|
+
/**
|
|
112
|
+
* ★ **已排程的超时 handle**(T21 · `Scheduler`)。
|
|
113
|
+
*
|
|
114
|
+
* 由 `runtime/engine.ts`(**不纯层**)在 `schedule()` 之后回填 ——
|
|
115
|
+
* 纯循环**拿不到** handle(那是调度方返回的外部标识)。
|
|
116
|
+
*
|
|
117
|
+
* ⚠️ 为什么不自己拼一个确定性的 handle:`Scheduler.schedule()` 的契约是「**返回**可取消的
|
|
118
|
+
* handle」,即形状由**调度方**决定(BullMQ 的 jobId 与内存实现的计数器显然不同)。
|
|
119
|
+
* 引擎自己拼一个等于反过来规定调度方的数据形状 —— 那正是 SPI 要避免的耦合。
|
|
120
|
+
*
|
|
121
|
+
* ⚠️ 令牌离开该节点(办完 / 被取消 / 被撤销)时由不纯层 `cancel()` 后**删除本字段**。
|
|
122
|
+
*/
|
|
123
|
+
timerHandles?: string[];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 审计条目 —— **合规主源**(`AGENTS.md` §6 / `03-engine` §9.1 Plan A)。
|
|
127
|
+
* 由内核在每次状态变更时生成,随状态被 `StateStore` **整块持久化**;
|
|
128
|
+
* ⚠️ 与「变量快照」不同源:这里记**谁做了什么**,变量快照记**数据怎么变**。
|
|
129
|
+
*/
|
|
130
|
+
interface AuditEntry {
|
|
131
|
+
/** 严格递增、无空洞(INV-4) */
|
|
132
|
+
seq: number;
|
|
133
|
+
at: string;
|
|
134
|
+
/** 19 项动作名 或 内核原语名 */
|
|
135
|
+
actor: string;
|
|
136
|
+
action: string;
|
|
137
|
+
nodeId?: string;
|
|
138
|
+
tokenId?: string;
|
|
139
|
+
/** 前后状态 */
|
|
140
|
+
from?: string;
|
|
141
|
+
to?: string;
|
|
142
|
+
/** 意见、表单增量等 */
|
|
143
|
+
payload?: Record<string, unknown>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 动作事实记录。
|
|
147
|
+
* ★ **同源**:一份进 `InstanceState.lastAction`(给 store 填审计列),
|
|
148
|
+
* 一份进 `TaskDelta.action`(给投影路由)—— 两处不得各造一份。
|
|
149
|
+
*/
|
|
150
|
+
interface ActionRecord {
|
|
151
|
+
/** 19 项动作名之一 */
|
|
152
|
+
name: string;
|
|
153
|
+
nodeId?: string;
|
|
154
|
+
tokenId?: string;
|
|
155
|
+
actor: string;
|
|
156
|
+
at: string;
|
|
157
|
+
comment?: string;
|
|
158
|
+
}
|
|
159
|
+
interface InstanceStateHeader {
|
|
160
|
+
/** 引擎生成、全局唯一(带前缀);`load(id)` 单参即可定位 */
|
|
161
|
+
instanceId: string;
|
|
162
|
+
processId: string;
|
|
163
|
+
/** ★ 实例绑定定义版本:改版不影响在途(`AC-E10`) */
|
|
164
|
+
definitionVersion: number;
|
|
165
|
+
/** 关联宿主业务行(`start()` 传入) */
|
|
166
|
+
businessKey?: string;
|
|
167
|
+
/** 多租户分片 */
|
|
168
|
+
tenantId?: string;
|
|
169
|
+
/** 宿主靠它做归档路由(终态 → 历史表),不用解 JSON */
|
|
170
|
+
status: InstanceStatus;
|
|
171
|
+
/** CAS 版本;**`0` = 尚未落库 = INSERT 信号**(写死,宿主据此分两条路径) */
|
|
172
|
+
rev: number;
|
|
173
|
+
/** 快照结构版本,供迁移(≠ moddle 的 `schemaVersion`) */
|
|
174
|
+
stateSchema: number;
|
|
175
|
+
/** 审计列 `updated_by` / `last_action` 直接取,不用挖数组 */
|
|
176
|
+
lastAction?: ActionRecord;
|
|
177
|
+
/** ★ 投影未追平的 rev(INV-18);补做完成后**必须删除该键** */
|
|
178
|
+
pendingProjectionRev?: number;
|
|
179
|
+
startedAt: string;
|
|
180
|
+
updatedAt: string;
|
|
181
|
+
/** 终态时间,归档表要这个 */
|
|
182
|
+
endedAt?: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* ★ `CallActivity` 子实例指回父实例的指针(T18)。
|
|
186
|
+
*
|
|
187
|
+
* 只带**定位用的三元组**,不带状态副本:子实例结束时要靠它把父实例里那条
|
|
188
|
+
* 停在 `callActivity` 上的令牌唤醒,而"父实例现在什么样"必须**现读**(读快照、CAS 写),
|
|
189
|
+
* 缓存一份下来就是典型的脏读。
|
|
190
|
+
*
|
|
191
|
+
* ⚠️ 与 `childInstanceIds` 是**两个方向**的记录:父记"我起了哪些子实例"(终止时要连坐),
|
|
192
|
+
* 子记"我该回哪里去"(结束时要唤醒)。缺任何一条,父子之间就会断。
|
|
193
|
+
*/
|
|
194
|
+
interface InstanceParent {
|
|
195
|
+
readonly instanceId: string;
|
|
196
|
+
/** 父实例上那个 `callActivity` 节点 */
|
|
197
|
+
readonly nodeId: string;
|
|
198
|
+
/** 父实例上停在那个节点的令牌(**令牌 id 在实例内唯一**,故它是可靠的定位键) */
|
|
199
|
+
readonly tokenId: string;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 实例状态 —— **分两层**(§6.1)。
|
|
203
|
+
*
|
|
204
|
+
* ⚠️ 本接口是 `Header` 与 `Body` 的交叉类型,字段**不分组**存放;
|
|
205
|
+
* 取 Header 用 `headerOf()`(逐字段列举,绝不用 rest 解构)。
|
|
206
|
+
*/
|
|
207
|
+
interface InstanceStateBody {
|
|
208
|
+
/** 引擎内部结构,宿主当不透明 JSON */
|
|
209
|
+
tokens: Token[];
|
|
210
|
+
/** 驳回目标只能从这里选(INV-6) */
|
|
211
|
+
completedNodes: string[];
|
|
212
|
+
variables: Record<string, unknown>;
|
|
213
|
+
/** ★ 合规主源:任何状态变更都追加一条 */
|
|
214
|
+
auditTrail: AuditEntry[];
|
|
215
|
+
/**
|
|
216
|
+
* 发起人(`start()` 传入)。
|
|
217
|
+
*
|
|
218
|
+
* ★ 为什么必须落在状态里而不是"从 `auditTrail[0].actor` 反推":`ApproverCtx.starter`
|
|
219
|
+
* 是**已发布的 SPI 契约**(`{type:'deptLeader', of:'starter'}` 全靠它解析),
|
|
220
|
+
* 而 `auditTrail` 会被 `maxAuditEntries` 裁剪(INV-17)—— 用一条**可能被裁掉的**记录
|
|
221
|
+
* 去支撑一个**永久需要**的契约,是典型的"省一个字段、埋一个偶发 bug"。
|
|
222
|
+
* ⚠️ `03` §9.1 尚未列出本字段 → 见 `ARCHITECTURE.md` **D-25**(待回写)。
|
|
223
|
+
*/
|
|
224
|
+
starter?: string;
|
|
225
|
+
/** `CallActivity` 子实例(不新增接口) */
|
|
226
|
+
childInstanceIds?: string[];
|
|
227
|
+
/** ★ 本实例是某个 `CallActivity` 的子实例时的回归指针;父实例**没有**本字段 */
|
|
228
|
+
parent?: InstanceParent;
|
|
229
|
+
}
|
|
230
|
+
type InstanceState = InstanceStateHeader & InstanceStateBody;
|
|
231
|
+
|
|
232
|
+
type TaskStatus = 'active' | 'delegated' | 'suspended' | 'cancelled' | 'done';
|
|
233
|
+
/**
|
|
234
|
+
* ★ **5 值**(不是 4 值):
|
|
235
|
+
* - `active` —— 待办,可办理
|
|
236
|
+
* - `delegated` —— 已委派他人,原办理人保留可见
|
|
237
|
+
* - `suspended` —— 随实例挂起而冻结(INV-5)
|
|
238
|
+
* - `cancelled` —— 被汇聚/终止取消(INV-9 属此类)
|
|
239
|
+
* - `done` —— 已办结
|
|
240
|
+
*/
|
|
241
|
+
declare const TASK_STATUSES: readonly ["active", "delegated", "suspended", "cancelled", "done"];
|
|
242
|
+
interface TaskView {
|
|
243
|
+
taskId: string;
|
|
244
|
+
instanceId: string;
|
|
245
|
+
nodeId: string;
|
|
246
|
+
nodeName?: string;
|
|
247
|
+
assignee: string;
|
|
248
|
+
status: TaskStatus;
|
|
249
|
+
createdAt: string;
|
|
250
|
+
/** 超时截止(工作日历算出),`Scheduler` 用 */
|
|
251
|
+
dueAt?: string;
|
|
252
|
+
formKey?: string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 待办视图差分。
|
|
256
|
+
*
|
|
257
|
+
* ⚠️ 头号静默错误是**只处理 `added`**:`removed` 里的 taskId 必须**真删**
|
|
258
|
+
* (INV-15 断言「apply 之后查不到」)。`removed` 是 id 列表,不是 `TaskView`。
|
|
259
|
+
*/
|
|
260
|
+
interface TaskDelta {
|
|
261
|
+
/** 对应 `InstanceState.rev` —— 投影据此判幂等与追平(INV-18) */
|
|
262
|
+
rev: number;
|
|
263
|
+
/** ★ 必做:不带动点名,宿主分不出「通过一步」与「被驳回」 */
|
|
264
|
+
action: ActionRecord;
|
|
265
|
+
added: TaskView[];
|
|
266
|
+
/** taskId 列表 */
|
|
267
|
+
removed: string[];
|
|
268
|
+
changed: TaskView[];
|
|
269
|
+
/** 供宿主取 `businessKey` / `tenantId` / `status`(不解体 body) */
|
|
270
|
+
instance: InstanceStateHeader;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* `exportTrace()` 的返回元素,**派生自 `auditTrail`**(不新增存储)。
|
|
274
|
+
*
|
|
275
|
+
* ★ **`kind` 的两档判据是「是不是 19 项审批动作之一」**,不是「谁发起的」:
|
|
276
|
+
* `start` / `callActivityReturn` / `deliverMessage` / `deliverSignal` 一律 `system`。
|
|
277
|
+
* ⚠️ 早期草案写的是 `'action' | 'primitive'`(原语级审计,**D-23**),**已否决** ——
|
|
278
|
+
* 理由见 `ARCHITECTURE.md` **D-87**:`run-to-wait` 的令牌推进**不走 `advance` 原语**
|
|
279
|
+
* (`runtime/loop.ts` 直接改 `token.nodeId`),按原语记出来的"轨迹"里**没有令牌移动**,
|
|
280
|
+
* 恰恰是"轨迹"最该有的那一半;且一次提交会炸出几十条,把 `maxAuditEntries` 的
|
|
281
|
+
* 「保留最近 N 次变更」扭曲成「保留最近两次提交」。故 `kind` 只标**审批 / 非审批**这一档。
|
|
282
|
+
*
|
|
283
|
+
* ★ `from` / `to` / `tokenId` 由 `runtime/plan.ts` 填:取**动作实际作用的那个令牌**
|
|
284
|
+
* 在推进前后的节点(`subjectTokenOf()` 是唯一口径 —— 与 `submit()` 认领令牌同一套判据,
|
|
285
|
+
* 不是另写一份"看起来差不多"的定位逻辑)。
|
|
286
|
+
*/
|
|
287
|
+
interface TraceEntry {
|
|
288
|
+
seq: number;
|
|
289
|
+
at: string;
|
|
290
|
+
actor: string;
|
|
291
|
+
action: string;
|
|
292
|
+
kind: 'approval' | 'system';
|
|
293
|
+
nodeId?: string;
|
|
294
|
+
tokenId?: string;
|
|
295
|
+
/** 推进**前**令牌所在节点(动作发生地) */
|
|
296
|
+
from?: string;
|
|
297
|
+
/** 推进**后**令牌所在节点;令牌已终结则无 */
|
|
298
|
+
to?: string;
|
|
299
|
+
payload?: Record<string, unknown>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* ★ 本次动作作用在**哪个令牌**上 —— **唯一口径**。
|
|
303
|
+
*
|
|
304
|
+
* 判据(与 `actions/compile.ts` 的 `resolveToken` 同一套,只是这里优先按 `actor` 认领):
|
|
305
|
+
* ① 办理人 == `actor` 的在途令牌恰好 1 个 → 它(会签下"我办我那条"就是靠这条);
|
|
306
|
+
* ② 否则若全局在途令牌恰好 1 个 → 它;
|
|
307
|
+
* ③ 否则 → `undefined`(交给 `compileAction` 报"无法唯一定位",或该动作本就不需要令牌)。
|
|
308
|
+
*
|
|
309
|
+
* ⚠️ ① 与 ② 都不命中时**不猜**:猜错令牌 = 改到了别人的待办,是最难查的一类误伤。
|
|
310
|
+
*
|
|
311
|
+
* ★ 为什么必须收口到这一处:`submit()` 用它认领令牌,`plan()` 用它填审计的
|
|
312
|
+
* `tokenId` / `from` / `to`(**D-88**)。两处各写一份"看起来差不多"的定位逻辑,
|
|
313
|
+
* 就会出现「审计说办的是 A 分支、实际推进的是 B 分支」—— 而两份代码单独看都对。
|
|
314
|
+
*/
|
|
315
|
+
declare function subjectTokenOf(state: InstanceState, actor: string): Token | undefined;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @floken-io/engine · 10 个业务事件(节点级 5 + 实例级 5)
|
|
319
|
+
*
|
|
320
|
+
* 契约来源:`ARCHITECTURE.md` §7.3 / ADR-006。
|
|
321
|
+
*
|
|
322
|
+
* 三条定死的口径:
|
|
323
|
+
* 1. **固定顺序**:`taskCreated` 必先于 `taskAssigned`(无 `assignee` 时只发 `created`)。
|
|
324
|
+
* —— 特意**不抄** Flowable 的「`assignment` 早于 `create`」:它那么做是因为要先定
|
|
325
|
+
* assignee 再实例化 task,语义混乱。
|
|
326
|
+
* 2. **有意不采**:连线级 `flow.take`(`auditTrail` 已记 `from`/`to`,且噪音最大)、
|
|
327
|
+
* 内核生命周期 `enter`/`leave`(暴露即锁死内核实现)→ **只发业务语义事件**。
|
|
328
|
+
* 3. 出口是 `EventSink`(异步、不阻塞、丢了不影响流程);**审计不走这里** ——
|
|
329
|
+
* 审计主源是 `InstanceState.auditTrail`。
|
|
330
|
+
*
|
|
331
|
+
* `taskUpdated` 是**刻意加入**的第 5 个节点级事件:四家引擎里只有 Camunda 8 单独给它
|
|
332
|
+
* 一个事件(`updating`),有道理 —— 变量变更也得同步到宿主的待办视图。
|
|
333
|
+
*/
|
|
334
|
+
|
|
335
|
+
type TaskEventName = 'taskCreated' | 'taskAssigned' | 'taskUpdated' | 'taskCompleted' | 'taskCancelled';
|
|
336
|
+
type InstanceEventName = 'started' | 'completed' | 'terminated' | 'suspended' | 'resumed';
|
|
337
|
+
type EngineEventName = TaskEventName | InstanceEventName;
|
|
338
|
+
declare const TASK_EVENT_NAMES: readonly ["taskCreated", "taskAssigned", "taskUpdated", "taskCompleted", "taskCancelled"];
|
|
339
|
+
declare const INSTANCE_EVENT_NAMES: readonly ["started", "completed", "terminated", "suspended", "resumed"];
|
|
340
|
+
/** 全部 10 个事件名(顺序即文档顺序,便于快照测试) */
|
|
341
|
+
declare const ENGINE_EVENT_NAMES: readonly ["taskCreated", "taskAssigned", "taskUpdated", "taskCompleted", "taskCancelled", "started", "completed", "terminated", "suspended", "resumed"];
|
|
342
|
+
interface EngineEventBase {
|
|
343
|
+
at: string;
|
|
344
|
+
/** 引擎生成、全局唯一 */
|
|
345
|
+
instanceId: string;
|
|
346
|
+
processId: string;
|
|
347
|
+
definitionVersion: number;
|
|
348
|
+
/** 触发本次事件的动作事实(与 `InstanceState.lastAction` / `delta.action` 同源) */
|
|
349
|
+
action: ActionRecord;
|
|
350
|
+
}
|
|
351
|
+
/** 节点级(待办)事件 —— 一次 `submit()` 可能发多条 */
|
|
352
|
+
interface TaskEvent extends EngineEventBase {
|
|
353
|
+
name: TaskEventName;
|
|
354
|
+
taskId: string;
|
|
355
|
+
nodeId: string;
|
|
356
|
+
nodeName?: string;
|
|
357
|
+
/** 未分配时缺省(此时只发 `taskCreated`,不发 `taskAssigned`) */
|
|
358
|
+
assignee?: string;
|
|
359
|
+
taskStatus: TaskStatus;
|
|
360
|
+
formKey?: string;
|
|
361
|
+
}
|
|
362
|
+
/** 实例级事件 —— 一次 `submit()` 至多一条 */
|
|
363
|
+
interface InstanceEvent extends EngineEventBase {
|
|
364
|
+
name: InstanceEventName;
|
|
365
|
+
status: InstanceStatus;
|
|
366
|
+
/** 实例头快照,宿主不用再 `load()` 一次 */
|
|
367
|
+
instance: InstanceStateHeader;
|
|
368
|
+
}
|
|
369
|
+
type EngineEvent = TaskEvent | InstanceEvent;
|
|
370
|
+
declare function isTaskEvent(e: EngineEvent): e is TaskEvent;
|
|
371
|
+
declare function isInstanceEvent(e: EngineEvent): e is InstanceEvent;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @floken-io/engine · 11 项 SPI(与业务的**全部**接触面)
|
|
375
|
+
*
|
|
376
|
+
* 契约来源:`ARCHITECTURE.md` §7.2 / `03-engine` §8。
|
|
377
|
+
*
|
|
378
|
+
* **本文件只声明、绝不实现** —— 引擎不 import 数据库 / 消息队列 / 组织架构,
|
|
379
|
+
* 而是反过来:低层实现这些接口,再注入进来(依赖倒置)。
|
|
380
|
+
* 判据:**任何「换个客户就要改包代码」的东西,都是解耦没做到位。**
|
|
381
|
+
*
|
|
382
|
+
* ★ 计数口径(勿再挪):**11 项 = 存储三线 3 + 业务接入 4 + 求值 2 + 出口 2**。
|
|
383
|
+
* ★ 命名红线:同一接口只允许一个名字 —— 求值 = `ConditionHandler` / `DecisionHandler`
|
|
384
|
+
* (旧名 `ExpressionEvaluator` **废弃、全项目不再使用**);存储写线 = `StateStore`。
|
|
385
|
+
*/
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* 写线 · 引擎状态的**唯一权威**。
|
|
389
|
+
*
|
|
390
|
+
* 只两个方法 —— 这一层门槛刻意压到 ~20 行,任何库 / 内存 / 浏览器都能实现。
|
|
391
|
+
* **禁止**让本接口长出事务接口:那会把内存实现的门槛抬到 100 行,
|
|
392
|
+
* 也会把「引擎内不做事务」这条底线(ADR-004)毁掉。
|
|
393
|
+
*/
|
|
394
|
+
interface StateStore {
|
|
395
|
+
/** 单参定位:`instanceId` 由引擎生成、全局唯一(**不开 tenantId 口子**,见 §9.3) */
|
|
396
|
+
load(id: string): Promise<InstanceState | null>;
|
|
397
|
+
/**
|
|
398
|
+
* 整块快照 + CAS 乐观锁。
|
|
399
|
+
*
|
|
400
|
+
* ★ **`expectedRev === 0` 就是 INSERT 信号**(写死,宿主据此分两条路径):
|
|
401
|
+
* - `expectedRev === 0` → INSERT;该 id 已存在则抛 `ENGINE_PERSIST_ALREADY_EXISTS`
|
|
402
|
+
* - `expectedRev > 0` → CAS UPDATE(`WHERE rev = expectedRev`);**影响 0 行**则抛 `ENGINE_PERSIST_CONFLICT`
|
|
403
|
+
*
|
|
404
|
+
* ⚠️ 必须靠**影响行数**判定,不得“先查后写”(那是竞态)。
|
|
405
|
+
* 行业背书:Camunda 7 的 `REV_` 乐观锁是同一设计(affected rows 0 → 抛冲突)。
|
|
406
|
+
*/
|
|
407
|
+
save(next: InstanceState, expectedRev: number): Promise<void>;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* 定义线 · **只读**、按版本取图纸。
|
|
411
|
+
*
|
|
412
|
+
* ★ 必填。理由:`AC-E10` 要求「v1.0 改版后,在途实例仍按**旧版本**定义执行」——
|
|
413
|
+
* 定义必须能被按 `(processId, version)` 取回,而不是每次 `submit()` 由宿主递进来。
|
|
414
|
+
*
|
|
415
|
+
* ═══════════════════════════════════════════════════════════════
|
|
416
|
+
* ★★ 版本语义(T19 钉死 —— 这四条就是 `AC-E10` 的全部内容)
|
|
417
|
+
* ═══════════════════════════════════════════════════════════════
|
|
418
|
+
*
|
|
419
|
+
* ① **版本精确**:`getDefinition(pid, v)` 是「第 v 版」的**等值查询**,不是「≤ v 的最新一版」,
|
|
420
|
+
* 更不是「最新版」。宿主**不得**做就近取整(`Math.round`)或"取不到就退到上一版"。
|
|
421
|
+
* 理由:一次静默回退 = 在途实例跑到了它发起时**还不存在的节点**上。
|
|
422
|
+
*
|
|
423
|
+
* ② **不存在 = `null`**:该 `(pid, v)` 不在库里就返回 `null`(不得抛错、不得返回任一其他版本)。
|
|
424
|
+
* 引擎侧统一把 `null` 翻成 `ENGINE_STATE_DEFINITION_MISSING`。
|
|
425
|
+
* ⚠️ 宿主若抛自定义错,引擎就**分不清**「这版没有」与「仓库挂了」—— 与 `StateStore`
|
|
426
|
+
* 必须用 `persistConflict()` 抛错(见 `report.ts`)同一条理由。
|
|
427
|
+
*
|
|
428
|
+
* ③ **不得改内容**:同一 `(pid, v)` 反复取回必须内容一致(发布即冻结)。
|
|
429
|
+
* 「改一版的定义而不升版本号」会让在途实例**中途变图**,这是 `AC-E10` 的头号破防方式。
|
|
430
|
+
*
|
|
431
|
+
* ④ **`version` 由引擎保证 ≥ 1 的整数**(`assertStartOptions` 与 `callTargetOf` 各自校验),
|
|
432
|
+
* 宿主不必重复校验;但对**异常入参**(0 / 负数 / 小数 / `NaN`)应返回 `null` 而非抛错。
|
|
433
|
+
*
|
|
434
|
+
* 契约自检:`runDefinitionConformance()`(`./conformance`)把这四条变成可跑的判据。
|
|
435
|
+
*/
|
|
436
|
+
interface DefinitionSource {
|
|
437
|
+
getDefinition(processId: string, version: number): Promise<ProcessDefinition | null>;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* 读线 · 待办视图。**可选注入** —— 不注入则宿主自管待办表。
|
|
441
|
+
*
|
|
442
|
+
* ⚠️ 本线是**视图**,表结构归宿主;引擎只吐 `TaskDelta`。
|
|
443
|
+
* ⚠️ **禁止在本线里做业务副作用**(扣库存、发通知、改业务主表)—— 那是门 1 `hooks`
|
|
444
|
+
* 或门 2 `plan()` 自编排的事(ADR-004)。
|
|
445
|
+
*/
|
|
446
|
+
interface TaskProjection {
|
|
447
|
+
/** 按 `taskId` 幂等;`delta.removed` 里的 taskId **必须真删**(INV-15) */
|
|
448
|
+
apply(instanceId: string, delta: TaskDelta): Promise<void>;
|
|
449
|
+
/** 全量对账:`load()` 发现 `pendingProjectionRev` 时补做(INV-18) */
|
|
450
|
+
sync(instanceId: string, tasks: TaskView[]): Promise<void>;
|
|
451
|
+
}
|
|
452
|
+
/** `ApproverSource.resolve()` 的入参上下文:引擎知道的事实,不含组织模型 */
|
|
453
|
+
interface ApproverCtx {
|
|
454
|
+
instanceId: string;
|
|
455
|
+
processId: string;
|
|
456
|
+
nodeId: string;
|
|
457
|
+
/** 发起人(`start()` 传入)—— `{type:'deptLeader', of:'starter'}` 的解析依据 */
|
|
458
|
+
starter: string;
|
|
459
|
+
/** 当前流程变量(只读快照) */
|
|
460
|
+
variables: Readonly<Record<string, unknown>>;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* ★ **最常被写死的一环**:「谁是发起人的部门负责人」是**业务数据**,不是引擎知识。
|
|
464
|
+
*
|
|
465
|
+
* 引擎只会说「我要哪一类人」(`ApproverSpec`),具体是谁由宿主回答:
|
|
466
|
+
* ```ts
|
|
467
|
+
* await source.resolve({ type: 'deptLeader', of: 'starter' }, { starter: 'u_001', … });
|
|
468
|
+
* // → ['u_1007']
|
|
469
|
+
* ```
|
|
470
|
+
* 为什么必须外置:「部门负责人」到底是正职还是副职?休假期间算谁?跨部门兼职算谁?
|
|
471
|
+
* —— **每家公司答案不同**,写进引擎就等于每换一个客户改一次包代码。
|
|
472
|
+
*/
|
|
473
|
+
interface ApproverSource {
|
|
474
|
+
resolve(spec: ApproverSpec, ctx: ApproverCtx): Promise<string[]>;
|
|
475
|
+
}
|
|
476
|
+
/** `serviceTask` 的宿主上下文 */
|
|
477
|
+
interface ServiceCtx {
|
|
478
|
+
instanceId: string;
|
|
479
|
+
processId: string;
|
|
480
|
+
definitionVersion: number;
|
|
481
|
+
nodeId: string;
|
|
482
|
+
}
|
|
483
|
+
/** 单个 `serviceTask` 实现:入参是**只读**流程变量,返回要并入 `variables` 的增量 */
|
|
484
|
+
type ServiceHandlerFn = (variables: Readonly<Record<string, unknown>>, ctx: ServiceCtx) => Promise<Record<string, unknown>>;
|
|
485
|
+
/**
|
|
486
|
+
* `serviceTask` 的实现表(引擎不知道怎么发邮件)。
|
|
487
|
+
* 按定义里的 `handlerRef` 取;缺省用 `nodeId` 取。
|
|
488
|
+
*/
|
|
489
|
+
interface ServiceHandler {
|
|
490
|
+
/** 未注册返回 `undefined` —— 引擎据此报「未配置」,**不得静默跳过** */
|
|
491
|
+
get(ref: string): ServiceHandlerFn | undefined;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* 鉴权(可选)。
|
|
495
|
+
* ★ 内核**默认零鉴权**(信任宿主),默认实现放行;信创 / 不可信直连场景才注入强鉴权。
|
|
496
|
+
* 部署契约:任务中心是唯一对外入口,内核 API 不公开。
|
|
497
|
+
*/
|
|
498
|
+
interface AuthResolver {
|
|
499
|
+
canAct(actor: string, nodeId: string, state: InstanceState): Promise<boolean>;
|
|
500
|
+
}
|
|
501
|
+
/** `FormProvider` 的宿主上下文 */
|
|
502
|
+
interface FormCtx {
|
|
503
|
+
instanceId: string;
|
|
504
|
+
nodeId: string;
|
|
505
|
+
/** 发起人 / 当前办理人 */
|
|
506
|
+
actor: string;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* 表单读取与快照(可选)。
|
|
510
|
+
* ★ 引擎**不解析表单结构**,只存 `formKey` —— 本接口是「画面之外」的那半:
|
|
511
|
+
* 读取定义 + 落快照。渲染归宿主薄渲染器(可选包 `@floken-io/form-renderer`)。
|
|
512
|
+
*/
|
|
513
|
+
interface FormProvider {
|
|
514
|
+
/** 按 `formKey` 读取表单定义;取不到返回 `null`(引擎报「未配置」) */
|
|
515
|
+
getForm(formKey: string, ctx: FormCtx): Promise<unknown | null>;
|
|
516
|
+
/** 每一步「看到的表单是什么」的快照(`03-engine` FR-8.1 表单快照);不落则返回 `null` */
|
|
517
|
+
snapshot(formKey: string, variables: Readonly<Record<string, unknown>>, ctx: FormCtx): Promise<unknown | null>;
|
|
518
|
+
}
|
|
519
|
+
/** `ConditionHandler` 的宿主上下文 */
|
|
520
|
+
interface ConditionCtx {
|
|
521
|
+
instanceId: string;
|
|
522
|
+
nodeId: string;
|
|
523
|
+
variables: Readonly<Record<string, unknown>>;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* 网关分支 / 顺序流条件求值。**不注入 = 内置 `@floken-io/feel`**(S-FEEL 子集)。
|
|
527
|
+
*
|
|
528
|
+
* ⚠️ **第 0 层要求(无豁免,`AC-E9`)**:求值失败**必须抛错**,不得静默返回 `false`。
|
|
529
|
+
* 「表达式出错却返回 false」会让流程**静默走错分支**,比抛错危险十倍。
|
|
530
|
+
* 注入自定义实现后,§7.2 的「越界语法抛错」判定权移交宿主,但本条对**任何**实现生效。
|
|
531
|
+
*/
|
|
532
|
+
interface ConditionHandler {
|
|
533
|
+
evaluate(expression: string, ctx: ConditionCtx): boolean | Promise<boolean>;
|
|
534
|
+
}
|
|
535
|
+
/** `DecisionHandler` 的宿主上下文 */
|
|
536
|
+
interface DecisionCtx {
|
|
537
|
+
instanceId: string;
|
|
538
|
+
nodeId: string;
|
|
539
|
+
input: Readonly<Record<string, unknown>>;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* `BusinessRuleTask` 的决策求值。**不注入 = 该节点报「未配置」**(无内置默认)。
|
|
543
|
+
* 官方实现在 `@floken-io/dmn`,旁挂接入、不进主链。
|
|
544
|
+
*/
|
|
545
|
+
interface DecisionHandler {
|
|
546
|
+
evaluate(input: Record<string, unknown>, ctx: DecisionCtx): Promise<Record<string, unknown>>;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* 领域事件出口(**通知 / 集成**,异步、不阻塞、丢了不影响流程)。
|
|
550
|
+
* ⚠️ **不是审计来源** —— 审计主源是状态内 `auditTrail`(`03-engine` §9.1 Plan A)。
|
|
551
|
+
*/
|
|
552
|
+
interface EventSink {
|
|
553
|
+
emit(event: EngineEvent): void | Promise<void>;
|
|
554
|
+
}
|
|
555
|
+
/** 到点后做什么 —— `03` §4 的 `timeout.actions[]` 四选(可并存,故 `kind` 是单值、可多次 schedule) */
|
|
556
|
+
type ScheduleKind = 'remind' | 'autoApprove' | 'autoReject' | 'escalate';
|
|
557
|
+
/**
|
|
558
|
+
* ★ **原始**超时配置(`03` §4 的 `TimeoutConfig` 三选一 + 工作日历)。
|
|
559
|
+
*
|
|
560
|
+
* ⚠️ 为什么这里传的是**配置**而不是算好的 `dueAt`:
|
|
561
|
+
* ① **Q33** 硬性规定引擎 `dist` 不得出现时态库 —— 内核连 `P3D` 都解不了;
|
|
562
|
+
* ② 就算能解,`03` F-1 要求「3 个工作日」**必须**跳过周末与法定节假日,
|
|
563
|
+
* 而节假日表是**业务配置**,属于宿主/调度方。
|
|
564
|
+
* ⇒ 内核只能如实交出「从什么时候开始 + 定义上写的什么」,**到期时刻由调度方算**。
|
|
565
|
+
* 把 `dueAt` 留在内核里,等于逼内核要么违反 Q33、要么静默退化成 7×24。
|
|
566
|
+
*/
|
|
567
|
+
interface TimeoutSpec {
|
|
568
|
+
/** ISO-8601 duration:'P3D' / 'PT4H' */
|
|
569
|
+
readonly duration?: string | undefined;
|
|
570
|
+
/** 绝对时间 */
|
|
571
|
+
readonly date?: string | undefined;
|
|
572
|
+
/** 周期 */
|
|
573
|
+
readonly cycle?: string | undefined;
|
|
574
|
+
/** 工作日历 id;不给 = 调度方自己的默认(`03` F-1:不得退化成 7×24) */
|
|
575
|
+
readonly workCalendar?: string | undefined;
|
|
576
|
+
}
|
|
577
|
+
interface ScheduleRequest {
|
|
578
|
+
instanceId: string;
|
|
579
|
+
nodeId: string;
|
|
580
|
+
/** ★ 待办对应的令牌(`cancel` 要能定位到"哪条待办") */
|
|
581
|
+
tokenId: string;
|
|
582
|
+
/** ★ 待办**创建**的时刻(ISO 8601)—— 调度方按工作日历**从它**推算真实到期时刻 */
|
|
583
|
+
fromAt: string;
|
|
584
|
+
/** ★ 定义上写的超时配置(内核**不**算 `dueAt`,理由见 {@link TimeoutSpec}) */
|
|
585
|
+
timeout: TimeoutSpec;
|
|
586
|
+
kind: ScheduleKind;
|
|
587
|
+
payload?: Record<string, unknown>;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* 定时(超时提醒 / 自动审批)。
|
|
591
|
+
* ★ **内核里不允许出现定时逻辑**(把定时画进内核是最常见的架构污染);
|
|
592
|
+
* 不注入 = 内置 `createMemoryScheduler()` 零依赖进程内实现。
|
|
593
|
+
*/
|
|
594
|
+
interface Scheduler {
|
|
595
|
+
/** 返回可取消的 handle(形状由**调度方**决定,引擎不得假定) */
|
|
596
|
+
schedule(req: ScheduleRequest): Promise<string>;
|
|
597
|
+
cancel(handle: string): Promise<void>;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* 11 项 SPI 的名字,**分类顺序即文档顺序**(`03-engine` §8 四张子表)。
|
|
601
|
+
* 对外报数只从这里取,不得另记一份。
|
|
602
|
+
*/
|
|
603
|
+
declare const SPI_NAMES: readonly ["StateStore", "DefinitionSource", "TaskProjection", "ApproverSource", "ServiceHandler", "AuthResolver", "FormProvider", "ConditionHandler", "DecisionHandler", "EventSink", "Scheduler"];
|
|
604
|
+
type SpiName = (typeof SPI_NAMES)[number];
|
|
605
|
+
/** 分组视图(3 + 4 + 2 + 2 = 11)—— 供测试断言,也方便对外解释「为什么是 11」 */
|
|
606
|
+
declare const SPI_GROUPS: {
|
|
607
|
+
/** 存储三线:真相 / 图纸 / 视图 */
|
|
608
|
+
readonly storage: readonly ["StateStore", "DefinitionSource", "TaskProjection"];
|
|
609
|
+
/** 业务接入:取人 / 干活 / 鉴权 / 表单 */
|
|
610
|
+
readonly business: readonly ["ApproverSource", "ServiceHandler", "AuthResolver", "FormProvider"];
|
|
611
|
+
/** 求值:网关条件 / 决策表 */
|
|
612
|
+
readonly eval: readonly ["ConditionHandler", "DecisionHandler"];
|
|
613
|
+
/** 出口:事件 / 定时 */
|
|
614
|
+
readonly exit: readonly ["EventSink", "Scheduler"];
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* 名字 → 接口的登记表。
|
|
618
|
+
* ⚠️ 不导出成运行时值(会让 tsup 无法 tree-shake、也污了 `sideEffects:false` 声明);
|
|
619
|
+
* 它只用于下面那道**编译期锁**。
|
|
620
|
+
*/
|
|
621
|
+
interface SpiInterfaces {
|
|
622
|
+
StateStore: StateStore;
|
|
623
|
+
DefinitionSource: DefinitionSource;
|
|
624
|
+
TaskProjection: TaskProjection;
|
|
625
|
+
ApproverSource: ApproverSource;
|
|
626
|
+
ServiceHandler: ServiceHandler;
|
|
627
|
+
AuthResolver: AuthResolver;
|
|
628
|
+
FormProvider: FormProvider;
|
|
629
|
+
ConditionHandler: ConditionHandler;
|
|
630
|
+
DecisionHandler: DecisionHandler;
|
|
631
|
+
EventSink: EventSink;
|
|
632
|
+
Scheduler: Scheduler;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
export { isTaskEvent as $, type ActionRecord as A, SPI_GROUPS as B, type ConditionHandler as C, type DefinitionSource as D, type EngineEvent as E, type FormProvider as F, SPI_NAMES as G, STATE_SCHEMA_VERSION as H, type InstanceStateHeader as I, type ScheduleRequest as J, type ServiceCtx as K, type ServiceHandlerFn as L, type SpiInterfaces as M, type SpiName as N, TASK_EVENT_NAMES as O, TASK_STATUSES as P, TERMINAL_STATUSES as Q, TOKEN_STATES as R, type StateStore as S, type TaskView as T, type TaskEvent as U, type VoteOutcome as V, type TaskEventName as W, type TaskStatus as X, type TokenAwait as Y, type TokenState as Z, isInstanceEvent as _, type TaskProjection as a, isTerminalStatus as a0, subjectTokenOf as a1, type TaskDelta as b, type Token as c, type InstanceState as d, type InstanceParent as e, type TimeoutSpec as f, type TraceEntry as g, type InstanceStatus as h, type EventSink as i, type InstanceEventName as j, type ApproverSource as k, type ServiceHandler as l, type AuthResolver as m, type DecisionHandler as n, type Scheduler as o, type ConditionCtx as p, type ApproverCtx as q, type AuditEntry as r, type DecisionCtx as s, ENGINE_EVENT_NAMES as t, type EngineEventName as u, type FormCtx as v, INSTANCE_EVENT_NAMES as w, INSTANCE_STATUSES as x, type InstanceEvent as y, type InstanceStateBody as z };
|