@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/dist/index.js ADDED
@@ -0,0 +1,3645 @@
1
+ import { isTerminalStatus, stateTerminal, cloneState, optionInvalid, stateShapeInvalid, engineDiagnostic, ENGINE_DIAGNOSTIC_CODES, assertRoundTrip, headerOf, definitionMissing, tokenOrphan, approverEmpty, deliverNoTarget, assertSerializable, persistAlreadyExists, persistConflict, conditionInvalid, STATE_SCHEMA_VERSION, stateNotFound, actionVetoed, stateSuspended, voteConfigInvalid, actionTargetInvalid, optionUnknown, actionUnknown, addSignLimit, actionNotAllowed, commentRequired } from './chunk-RMCROXES.js';
2
+ export { ENGINE_DIAGNOSTIC_CODES, ENGINE_ERROR_CODES, EngineActionError, EngineError, EngineOptionError, EnginePersistError, EngineStateError, INSTANCE_STATUSES, STATE_SCHEMA_VERSION, TERMINAL_STATUSES, TOKEN_STATES, engineDiagnostic, isTerminalStatus, persistAlreadyExists, persistConflict } from './chunk-RMCROXES.js';
3
+ import { requiredVotes, shouldTerminate as shouldTerminate$1, normalizeApproval } from '@floken-io/moddle';
4
+ import { evaluate } from '@floken-io/feel';
5
+
6
+ // src/core/primitives.ts
7
+ var PRIMITIVE_NAMES = [
8
+ // —— 令牌级 8 ——
9
+ "advance",
10
+ "jumpTo",
11
+ "rollbackTo",
12
+ "spawnInstances",
13
+ "cancelInstances",
14
+ "transfer",
15
+ "delegate",
16
+ "halt",
17
+ // —— 实例级 2 ——
18
+ "suspend",
19
+ "resume"
20
+ ];
21
+ var PRIMITIVE_GROUPS = {
22
+ token: ["advance", "jumpTo", "rollbackTo", "spawnInstances", "cancelInstances", "transfer", "delegate", "halt"],
23
+ instance: ["suspend", "resume"]
24
+ };
25
+ var LIVE_TOKEN_STATES = ["active", "waiting"];
26
+ function transform(state, mutate) {
27
+ const next = cloneState(state);
28
+ mutate(next);
29
+ assertSerializable(next, "next");
30
+ return next;
31
+ }
32
+ function requireLiveInstance(state, primitive) {
33
+ if (isTerminalStatus(state.status)) {
34
+ throw stateTerminal(state.instanceId, state.status, primitive);
35
+ }
36
+ if (state.status === "suspended") {
37
+ throw stateSuspended(state.instanceId, primitive);
38
+ }
39
+ }
40
+ function requireLiveToken(state, tokenId, primitive) {
41
+ const token = state.tokens.find((t) => t.id === tokenId);
42
+ if (token === void 0) {
43
+ throw stateShapeInvalid(`primitive '${primitive}' targets unknown token '${tokenId}'`, {
44
+ primitive,
45
+ tokenId
46
+ });
47
+ }
48
+ if (!LIVE_TOKEN_STATES.includes(token.state)) {
49
+ throw stateShapeInvalid(
50
+ `primitive '${primitive}' targets a token that is already '${token.state}'`,
51
+ { primitive, tokenId, tokenState: token.state }
52
+ );
53
+ }
54
+ return token;
55
+ }
56
+ function requireCompletedTarget(state, to, primitive) {
57
+ const at = state.completedNodes.indexOf(to);
58
+ if (at < 0) {
59
+ throw stateShapeInvalid(`primitive '${primitive}' target '${to}' is not a completed node`, {
60
+ primitive,
61
+ target: to,
62
+ completedNodes: [...state.completedNodes]
63
+ });
64
+ }
65
+ return at;
66
+ }
67
+ function requireNonEmpty(value, what, primitive) {
68
+ if (typeof value !== "string" || value.length === 0) {
69
+ throw stateShapeInvalid(`primitive '${primitive}' requires a non-empty ${what}`, {
70
+ primitive,
71
+ field: what
72
+ });
73
+ }
74
+ }
75
+ function markCompleted(next, nodeId) {
76
+ if (!next.completedNodes.includes(nodeId)) next.completedNodes.push(nodeId);
77
+ }
78
+ function clearAssignment(t) {
79
+ delete t.assignee;
80
+ delete t.returnTo;
81
+ delete t.createdAt;
82
+ delete t.awaiting;
83
+ delete t.race;
84
+ }
85
+ var isLive = (t) => LIVE_TOKEN_STATES.includes(t.state);
86
+ function advance(state, input) {
87
+ requireLiveInstance(state, "advance");
88
+ const token = requireLiveToken(state, input.tokenId, "advance");
89
+ requireNonEmpty(input.to, "to", "advance");
90
+ const from = token.nodeId;
91
+ return transform(state, (next) => {
92
+ const t = next.tokens.find((x) => x.id === input.tokenId);
93
+ if (t === void 0) return;
94
+ t.nodeId = input.to;
95
+ clearAssignment(t);
96
+ markCompleted(next, from);
97
+ });
98
+ }
99
+ function jumpTo(state, input) {
100
+ requireLiveInstance(state, "jumpTo");
101
+ const token = requireLiveToken(state, input.tokenId, "jumpTo");
102
+ requireCompletedTarget(state, input.to, "jumpTo");
103
+ const from = token.nodeId;
104
+ return transform(state, (next) => {
105
+ const t = next.tokens.find((x) => x.id === input.tokenId);
106
+ if (t === void 0) return;
107
+ t.nodeId = input.to;
108
+ clearAssignment(t);
109
+ next.completedNodes = next.completedNodes.filter((n) => n !== input.to);
110
+ markCompleted(next, from);
111
+ });
112
+ }
113
+ function rollbackTo(state, input) {
114
+ requireLiveInstance(state, "rollbackTo");
115
+ requireLiveToken(state, input.tokenId, "rollbackTo");
116
+ const at = requireCompletedTarget(state, input.to, "rollbackTo");
117
+ return transform(state, (next) => {
118
+ next.completedNodes = next.completedNodes.slice(0, at);
119
+ for (const t of next.tokens) {
120
+ if (t.id === input.tokenId) {
121
+ t.nodeId = input.to;
122
+ clearAssignment(t);
123
+ } else if (isLive(t)) {
124
+ if (input.branch !== void 0 && t.branch !== input.branch) continue;
125
+ t.state = "cancelled";
126
+ }
127
+ }
128
+ });
129
+ }
130
+ function spawnInstances(state, input) {
131
+ requireLiveInstance(state, "spawnInstances");
132
+ requireNonEmpty(input.nodeId, "nodeId", "spawnInstances");
133
+ requireNonEmpty(input.groupId, "groupId", "spawnInstances");
134
+ if (!Array.isArray(input.assignees) || input.assignees.length === 0) {
135
+ throw stateShapeInvalid("primitive 'spawnInstances' requires at least one assignee", {
136
+ primitive: "spawnInstances",
137
+ nodeId: input.nodeId
138
+ });
139
+ }
140
+ return transform(state, (next) => {
141
+ if (input.replaceTokenId !== void 0) {
142
+ next.tokens = next.tokens.filter((t) => t.id !== input.replaceTokenId);
143
+ }
144
+ const grouped = input.grouped !== false;
145
+ const created = input.assignees.map((assignee, i) => ({
146
+ id: `${input.groupId}#${i}`,
147
+ nodeId: input.nodeId,
148
+ state: "active",
149
+ assignee,
150
+ ...grouped ? { instanceGroup: input.groupId } : {}
151
+ }));
152
+ next.tokens = [...next.tokens, ...created];
153
+ next.completedNodes = next.completedNodes.filter((n) => n !== input.nodeId);
154
+ });
155
+ }
156
+ function cancelInstances(state, input) {
157
+ requireLiveInstance(state, "cancelInstances");
158
+ const hasGroup = typeof input.groupId === "string" && input.groupId.length > 0;
159
+ const hasIds = Array.isArray(input.tokenIds) && input.tokenIds.length > 0;
160
+ if (!hasGroup && !hasIds) {
161
+ throw stateShapeInvalid(
162
+ "primitive 'cancelInstances' requires groupId or tokenIds (use 'halt' to cancel everything)",
163
+ { primitive: "cancelInstances" }
164
+ );
165
+ }
166
+ const ids = new Set(input.tokenIds ?? []);
167
+ return transform(state, (next) => {
168
+ for (const t of next.tokens) {
169
+ const inGroup = hasGroup && t.instanceGroup === input.groupId;
170
+ if (isLive(t) && (inGroup || ids.has(t.id))) t.state = "cancelled";
171
+ }
172
+ });
173
+ }
174
+ function transfer(state, input) {
175
+ requireLiveInstance(state, "transfer");
176
+ requireLiveToken(state, input.tokenId, "transfer");
177
+ requireNonEmpty(input.assignee, "assignee", "transfer");
178
+ return transform(state, (next) => {
179
+ const t = next.tokens.find((x) => x.id === input.tokenId);
180
+ if (t === void 0) return;
181
+ t.assignee = input.assignee;
182
+ });
183
+ }
184
+ function delegate(state, input) {
185
+ requireLiveInstance(state, "delegate");
186
+ const token = requireLiveToken(state, input.tokenId, "delegate");
187
+ requireNonEmpty(input.assignee, "assignee", "delegate");
188
+ const previous = token.assignee;
189
+ return transform(state, (next) => {
190
+ const t = next.tokens.find((x) => x.id === input.tokenId);
191
+ if (t === void 0) return;
192
+ if (previous !== void 0) t.returnTo = previous;
193
+ t.assignee = input.assignee;
194
+ });
195
+ }
196
+ function halt(state, _input = {}) {
197
+ if (isTerminalStatus(state.status)) {
198
+ throw stateTerminal(state.instanceId, state.status, "halt");
199
+ }
200
+ return transform(state, (next) => {
201
+ for (const t of next.tokens) {
202
+ if (isLive(t)) t.state = "cancelled";
203
+ }
204
+ next.status = "terminated";
205
+ });
206
+ }
207
+ function suspend(state, _input = {}) {
208
+ if (isTerminalStatus(state.status)) {
209
+ throw stateTerminal(state.instanceId, state.status, "suspend");
210
+ }
211
+ if (state.status === "suspended") return transform(state, () => void 0);
212
+ return transform(state, (next) => {
213
+ next.status = "suspended";
214
+ });
215
+ }
216
+ function resume(state, _input = {}) {
217
+ if (isTerminalStatus(state.status)) {
218
+ throw stateTerminal(state.instanceId, state.status, "resume");
219
+ }
220
+ if (state.status !== "suspended") {
221
+ throw stateShapeInvalid(
222
+ `primitive 'resume' requires a suspended instance, got '${state.status}'`,
223
+ { primitive: "resume", status: state.status }
224
+ );
225
+ }
226
+ return transform(state, (next) => {
227
+ next.status = "running";
228
+ });
229
+ }
230
+ var primitives = {
231
+ advance,
232
+ jumpTo,
233
+ rollbackTo,
234
+ spawnInstances,
235
+ cancelInstances,
236
+ transfer,
237
+ delegate,
238
+ halt,
239
+ suspend,
240
+ resume
241
+ };
242
+
243
+ // src/core/task.ts
244
+ var TASK_STATUSES = [
245
+ "active",
246
+ "delegated",
247
+ "suspended",
248
+ "cancelled",
249
+ "done"
250
+ ];
251
+ function diffTasks(before, after) {
252
+ const b = new Map(before.map((t) => [t.taskId, t]));
253
+ const a = new Map(after.map((t) => [t.taskId, t]));
254
+ const added = after.filter((t) => !b.has(t.taskId));
255
+ const removed = [...b.keys()].filter((id) => !a.has(id));
256
+ const changed = after.filter((t) => {
257
+ const prev = b.get(t.taskId);
258
+ return prev !== void 0 && JSON.stringify(prev) !== JSON.stringify(t);
259
+ });
260
+ return { added: [...added], removed, changed: [...changed] };
261
+ }
262
+ function subjectTokenOf(state, actor) {
263
+ const live = state.tokens.filter((t) => LIVE_TOKEN_STATES.includes(t.state));
264
+ if (live.length === 0) return void 0;
265
+ const mine = live.filter((t) => t.assignee !== void 0 && t.assignee === actor);
266
+ if (mine.length === 1) return mine[0];
267
+ if (live.length === 1) return live[0];
268
+ return void 0;
269
+ }
270
+
271
+ // src/core/spi.ts
272
+ var SPI_NAMES = [
273
+ // 8.1 存储三线
274
+ "StateStore",
275
+ "DefinitionSource",
276
+ "TaskProjection",
277
+ // 8.2 业务接入
278
+ "ApproverSource",
279
+ "ServiceHandler",
280
+ "AuthResolver",
281
+ "FormProvider",
282
+ // 8.3 求值
283
+ "ConditionHandler",
284
+ "DecisionHandler",
285
+ // 8.4 出口
286
+ "EventSink",
287
+ "Scheduler"
288
+ ];
289
+ var SPI_GROUPS = {
290
+ /** 存储三线:真相 / 图纸 / 视图 */
291
+ storage: ["StateStore", "DefinitionSource", "TaskProjection"],
292
+ /** 业务接入:取人 / 干活 / 鉴权 / 表单 */
293
+ business: ["ApproverSource", "ServiceHandler", "AuthResolver", "FormProvider"],
294
+ /** 求值:网关条件 / 决策表 */
295
+ eval: ["ConditionHandler", "DecisionHandler"],
296
+ /** 出口:事件 / 定时 */
297
+ exit: ["EventSink", "Scheduler"]
298
+ };
299
+
300
+ // src/core/events.ts
301
+ var TASK_EVENT_NAMES = [
302
+ "taskCreated",
303
+ "taskAssigned",
304
+ "taskUpdated",
305
+ "taskCompleted",
306
+ "taskCancelled"
307
+ ];
308
+ var INSTANCE_EVENT_NAMES = [
309
+ "started",
310
+ "completed",
311
+ "terminated",
312
+ "suspended",
313
+ "resumed"
314
+ ];
315
+ var ENGINE_EVENT_NAMES = [
316
+ ...TASK_EVENT_NAMES,
317
+ ...INSTANCE_EVENT_NAMES
318
+ ];
319
+ function isTaskEvent(e) {
320
+ return TASK_EVENT_NAMES.includes(e.name);
321
+ }
322
+ function isInstanceEvent(e) {
323
+ return INSTANCE_EVENT_NAMES.includes(e.name);
324
+ }
325
+
326
+ // src/actions/catalog.ts
327
+ var ACTION_SPECS = Object.freeze([
328
+ // —— 流转 ——
329
+ {
330
+ names: ["approve"],
331
+ label: "\u901A\u8FC7",
332
+ primitiveExpr: "advance",
333
+ primitives: ["advance"],
334
+ gate: null,
335
+ needsTarget: false,
336
+ native: true
337
+ },
338
+ {
339
+ names: ["reject"],
340
+ label: "\u9A73\u56DE",
341
+ primitiveExpr: "jumpTo",
342
+ primitives: ["jumpTo"],
343
+ gate: "reject",
344
+ needsTarget: true,
345
+ native: true
346
+ },
347
+ {
348
+ names: ["rejectToPrev"],
349
+ label: "\u9A73\u56DE\u5230\u4E0A\u4E00\u8282\u70B9",
350
+ primitiveExpr: "jumpTo",
351
+ primitives: ["jumpTo"],
352
+ gate: "reject",
353
+ needsTarget: true,
354
+ native: true
355
+ },
356
+ {
357
+ names: ["jumpTo"],
358
+ label: "\u4EFB\u610F\u8DF3\u8F6C",
359
+ primitiveExpr: "jumpTo",
360
+ primitives: ["jumpTo"],
361
+ gate: "reject",
362
+ needsTarget: true,
363
+ requiresArbitrary: true,
364
+ native: true
365
+ },
366
+ {
367
+ names: ["returnTo"],
368
+ label: "\u4EFB\u610F\u9000\u56DE",
369
+ primitiveExpr: "jumpTo",
370
+ primitives: ["jumpTo"],
371
+ gate: "reject",
372
+ needsTarget: true,
373
+ requiresArbitrary: true,
374
+ native: true
375
+ },
376
+ {
377
+ names: ["takeBack"],
378
+ label: "\u62FF\u56DE",
379
+ primitiveExpr: "rollbackTo",
380
+ primitives: ["rollbackTo"],
381
+ gate: "withdraw",
382
+ needsTarget: true,
383
+ native: true
384
+ },
385
+ {
386
+ names: ["revoke"],
387
+ label: "\u64A4\u9500",
388
+ primitiveExpr: "rollbackTo",
389
+ primitives: ["rollbackTo"],
390
+ gate: "revoke",
391
+ needsTarget: true,
392
+ native: true
393
+ },
394
+ {
395
+ names: ["terminate"],
396
+ label: "\u7EC8\u6B62",
397
+ primitiveExpr: "halt",
398
+ primitives: ["halt"],
399
+ gate: null,
400
+ needsTarget: false,
401
+ native: true
402
+ },
403
+ // —— 换人 ——
404
+ {
405
+ names: ["transfer"],
406
+ label: "\u8F6C\u529E",
407
+ primitiveExpr: "transfer",
408
+ primitives: ["transfer"],
409
+ gate: "transfer",
410
+ needsTarget: false,
411
+ native: true
412
+ },
413
+ {
414
+ names: ["delegate"],
415
+ label: "\u59D4\u6D3E",
416
+ primitiveExpr: "delegate",
417
+ primitives: ["delegate"],
418
+ gate: "delegate",
419
+ needsTarget: false,
420
+ native: true
421
+ },
422
+ // —— 改待办集合 ——
423
+ {
424
+ names: ["addSignBefore"],
425
+ label: "\u524D\u52A0\u7B7E",
426
+ primitiveExpr: "spawnInstances",
427
+ primitives: ["spawnInstances"],
428
+ gate: "addSign.before",
429
+ needsTarget: false,
430
+ native: true
431
+ },
432
+ {
433
+ names: ["addSignAfter"],
434
+ label: "\u540E\u52A0\u7B7E",
435
+ primitiveExpr: "spawnInstances",
436
+ primitives: ["spawnInstances"],
437
+ gate: "addSign.after",
438
+ needsTarget: false,
439
+ native: true
440
+ },
441
+ {
442
+ names: ["reduceSign"],
443
+ label: "\u51CF\u7B7E",
444
+ primitiveExpr: "cancelInstances",
445
+ primitives: ["cancelInstances"],
446
+ gate: "reduceSign",
447
+ needsTarget: false,
448
+ native: true
449
+ },
450
+ // —— 多实例(汇聚模式由 `mode` 承载,不是动作开关)——
451
+ {
452
+ names: ["countersign"],
453
+ label: "\u4F1A\u7B7E",
454
+ primitiveExpr: "spawnInstances",
455
+ primitives: ["spawnInstances"],
456
+ gate: null,
457
+ needsTarget: false,
458
+ native: true,
459
+ mode: "all"
460
+ },
461
+ {
462
+ names: ["orSign"],
463
+ label: "\u6216\u7B7E",
464
+ primitiveExpr: "spawnInstances+cancelInstances",
465
+ // ★ `cancelInstances` 不在提交时执行 —— 它在**汇聚时**由 T10 `actions/convergence.ts` 触发
466
+ // (一人通过 → 取消其余)。`primitiveExpr` 保留文档原文以便对账。
467
+ primitives: ["spawnInstances"],
468
+ gate: null,
469
+ needsTarget: false,
470
+ native: true,
471
+ mode: "any"
472
+ },
473
+ {
474
+ names: ["voteSign"],
475
+ label: "\u7968\u7B7E",
476
+ primitiveExpr: "spawnInstances+cancelInstances",
477
+ primitives: ["spawnInstances"],
478
+ gate: null,
479
+ needsTarget: false,
480
+ native: true,
481
+ mode: "vote"
482
+ },
483
+ // —— 内核外 ——
484
+ {
485
+ names: ["timeoutAction"],
486
+ label: "\u8D85\u65F6\u81EA\u52A8\u5904\u7406",
487
+ primitiveExpr: "advance",
488
+ primitives: ["advance"],
489
+ gate: "timeout",
490
+ needsTarget: false,
491
+ native: false
492
+ },
493
+ {
494
+ names: ["suspend", "resume"],
495
+ label: "\u6302\u8D77 / \u6062\u590D",
496
+ primitiveExpr: "suspend + resume",
497
+ primitives: ["suspend", "resume"],
498
+ gate: null,
499
+ needsTarget: false,
500
+ native: true
501
+ },
502
+ {
503
+ names: ["saveDraft"],
504
+ label: "\u6682\u5B58",
505
+ primitiveExpr: "\uFF08\u7A7A\uFF09",
506
+ primitives: [],
507
+ gate: null,
508
+ needsTarget: false,
509
+ native: false
510
+ }
511
+ ]);
512
+ var ACTION_NAMES = Object.freeze(
513
+ ACTION_SPECS.flatMap((s) => s.names)
514
+ );
515
+ var ACTION_SPEC_BY_NAME = Object.freeze(
516
+ Object.fromEntries(ACTION_SPECS.flatMap((s) => s.names.map((n) => [n, s])))
517
+ );
518
+ Object.freeze(
519
+ ACTION_SPECS.filter((s) => s.needsTarget).flatMap((s) => s.names)
520
+ );
521
+ var HANDOVER_ACTIONS = Object.freeze(["transfer", "delegate"]);
522
+ var SPAWN_ACTIONS = Object.freeze(
523
+ ACTION_SPECS.filter((s) => s.primitives.includes("spawnInstances")).flatMap((s) => s.names)
524
+ );
525
+ Object.freeze(
526
+ ACTION_SPECS.filter((s) => s.gate === null && s.mode === void 0).flatMap((s) => s.names)
527
+ );
528
+
529
+ // src/actions/gates.ts
530
+ function readGate(approval, path) {
531
+ switch (path) {
532
+ case "reject":
533
+ return {
534
+ allowed: approval.reject.allowed,
535
+ requireComment: approval.reject.requireComment
536
+ };
537
+ case "withdraw":
538
+ return {
539
+ allowed: approval.withdraw.allowed,
540
+ requireComment: approval.withdraw.requireComment
541
+ };
542
+ case "revoke":
543
+ return { allowed: approval.revoke.allowed, requireComment: approval.revoke.requireComment };
544
+ case "transfer":
545
+ return {
546
+ allowed: approval.transfer.allowed,
547
+ requireComment: approval.transfer.requireComment
548
+ };
549
+ case "delegate":
550
+ return {
551
+ allowed: approval.delegate.allowed,
552
+ requireComment: approval.delegate.requireComment
553
+ };
554
+ case "reduceSign":
555
+ return {
556
+ allowed: approval.reduceSign.allowed,
557
+ requireComment: approval.reduceSign.requireComment ?? false
558
+ };
559
+ case "addSign.before":
560
+ return { allowed: approval.addSign.before, requireComment: false };
561
+ case "addSign.after":
562
+ return { allowed: approval.addSign.after, requireComment: false };
563
+ case "timeout":
564
+ return {
565
+ allowed: approval.timeout !== void 0 && approval.timeout.actions.length > 0,
566
+ requireComment: false
567
+ };
568
+ default: {
569
+ const _exhaustive = path;
570
+ throw new Error(`unknown gate path: ${String(_exhaustive)}`);
571
+ }
572
+ }
573
+ }
574
+ function isActionEnabled(spec, approval) {
575
+ if (spec.gate === null) return true;
576
+ if (approval === void 0) return false;
577
+ if (!readGate(approval, spec.gate).allowed) return false;
578
+ if (spec.requiresArbitrary === true && !approval.reject.allowArbitrary) return false;
579
+ return true;
580
+ }
581
+ function enabledActionNames(approval) {
582
+ return ACTION_SPECS.filter((s) => s.native && isActionEnabled(s, approval)).flatMap(
583
+ (s) => s.names
584
+ );
585
+ }
586
+ function assertActionEnabled(name, approval) {
587
+ const spec = ACTION_SPECS.find((s) => s.names.includes(name));
588
+ if (spec === void 0) {
589
+ throw actionNotAllowed(name, []);
590
+ }
591
+ if (!isActionEnabled(spec, approval)) {
592
+ throw actionNotAllowed(name, approval === void 0 ? [] : enabledActionNames(approval));
593
+ }
594
+ return spec;
595
+ }
596
+ function assertComment(name, spec, approval, comment) {
597
+ if (spec.gate === null) return;
598
+ if (approval === void 0) return;
599
+ if (!readGate(approval, spec.gate).requireComment) return;
600
+ if (typeof comment === "string" && comment.trim().length > 0) return;
601
+ throw commentRequired(name);
602
+ }
603
+ function assertTarget(params) {
604
+ const { name, approval, completedNodes, target, startNodeId } = params;
605
+ if (approval === void 0) {
606
+ throw actionTargetInvalid(name, target ?? "", completedNodes, []);
607
+ }
608
+ const allowedTargets = approval.reject.allowedTargets;
609
+ const reject = () => {
610
+ throw actionTargetInvalid(name, target ?? "", completedNodes, allowedTargets);
611
+ };
612
+ if (typeof target !== "string" || target.length === 0) reject();
613
+ const t = target;
614
+ if (!completedNodes.includes(t)) reject();
615
+ const last = completedNodes[completedNodes.length - 1];
616
+ const ok = allowedTargets.some((kind) => {
617
+ switch (kind) {
618
+ case "previous":
619
+ return t === last;
620
+ case "nodeId":
621
+ return true;
622
+ // ① 已保证 ∈ completedNodes
623
+ case "starter":
624
+ return startNodeId !== void 0 && t === startNodeId;
625
+ default:
626
+ return false;
627
+ }
628
+ });
629
+ if (!ok) reject();
630
+ return t;
631
+ }
632
+ function resolveRejectTarget(params) {
633
+ const { name, approval, completedNodes, startNodeId } = params;
634
+ if (params.target !== void 0 && params.target.length > 0) {
635
+ return assertTarget({ name, approval, completedNodes, target: params.target, startNodeId });
636
+ }
637
+ const allowed = approval.reject.allowedTargets;
638
+ for (const kind of allowed) {
639
+ if (kind === "previous") {
640
+ const last = completedNodes[completedNodes.length - 1];
641
+ if (last !== void 0) return last;
642
+ }
643
+ if (kind === "starter" && startNodeId !== void 0 && completedNodes.includes(startNodeId)) {
644
+ return startNodeId;
645
+ }
646
+ }
647
+ throw actionTargetInvalid(name, "", completedNodes, allowed);
648
+ }
649
+ function isNonNegativeInt(n) {
650
+ return typeof n === "number" && Number.isInteger(n) && n >= 0;
651
+ }
652
+ function assertConvergeCtx(ctx) {
653
+ if (ctx === null || typeof ctx !== "object") {
654
+ throw stateShapeInvalid("ConvergeCtx must be an object", { ctx });
655
+ }
656
+ if (ctx.mode !== "all" && ctx.mode !== "any" && ctx.mode !== "vote") {
657
+ throw stateShapeInvalid(`unknown converge mode '${String(ctx.mode)}'`, {
658
+ mode: ctx.mode,
659
+ allowed: ["all", "any", "vote"]
660
+ });
661
+ }
662
+ if (!isNonNegativeInt(ctx.total) || ctx.total < 1) {
663
+ throw stateShapeInvalid(`total must be an integer >= 1, got ${String(ctx.total)}`, {
664
+ total: ctx.total
665
+ });
666
+ }
667
+ for (const key of ["approved", "rejected", "pending"]) {
668
+ if (!isNonNegativeInt(ctx[key])) {
669
+ throw stateShapeInvalid(`${key} must be a non-negative integer, got ${String(ctx[key])}`, {
670
+ [key]: ctx[key]
671
+ });
672
+ }
673
+ }
674
+ const expectedPending = ctx.total - ctx.approved - ctx.rejected;
675
+ if (ctx.pending !== expectedPending) {
676
+ throw stateShapeInvalid(
677
+ `pending (${ctx.pending}) !== total \u2212 approved \u2212 rejected (${expectedPending})`,
678
+ { total: ctx.total, approved: ctx.approved, rejected: ctx.rejected, pending: ctx.pending }
679
+ );
680
+ }
681
+ if (ctx.approved + ctx.rejected > ctx.total) {
682
+ throw stateShapeInvalid("approved + rejected exceeds total", {
683
+ total: ctx.total,
684
+ approved: ctx.approved,
685
+ rejected: ctx.rejected
686
+ });
687
+ }
688
+ if (ctx.onReject !== "abort" && ctx.onReject !== "wait") {
689
+ throw stateShapeInvalid(`unknown onReject '${String(ctx.onReject)}'`, {
690
+ onReject: ctx.onReject,
691
+ allowed: ["abort", "wait"]
692
+ });
693
+ }
694
+ }
695
+ function voteSpecOf(ctx) {
696
+ const hasCount = ctx.count !== void 0;
697
+ const hasThreshold = ctx.threshold !== void 0;
698
+ if (ctx.mode !== "vote") return void 0;
699
+ if (hasCount === hasThreshold) {
700
+ throw voteConfigInvalid(
701
+ hasCount ? "mode:'vote' requires exactly one of vote.count / vote.threshold, got both" : "mode:'vote' requires vote.count or vote.threshold, got neither",
702
+ { mode: ctx.mode, count: ctx.count, threshold: ctx.threshold }
703
+ );
704
+ }
705
+ if (hasCount) {
706
+ if (!Number.isInteger(ctx.count) || ctx.count < 1) {
707
+ throw voteConfigInvalid("vote.count must be a positive integer", { count: ctx.count });
708
+ }
709
+ return { count: ctx.count };
710
+ }
711
+ const t = ctx.threshold;
712
+ if (typeof t !== "number" || !Number.isFinite(t) || t <= 0 || t > 1) {
713
+ throw voteConfigInvalid("vote.threshold must be a ratio in (0, 1]", { threshold: t });
714
+ }
715
+ return { threshold: t };
716
+ }
717
+ function requiredOf(ctx) {
718
+ assertConvergeCtx(ctx);
719
+ return requiredVotes(ctx.total, voteSpecOf(ctx));
720
+ }
721
+ function evaluateConvergence(ctx) {
722
+ assertConvergeCtx(ctx);
723
+ const vote = voteSpecOf(ctx);
724
+ const required = requiredVotes(ctx.total, vote);
725
+ const r = shouldTerminate$1(ctx.mode, ctx.total, ctx.approved, ctx.rejected, {
726
+ onReject: ctx.onReject,
727
+ ...vote === void 0 ? {} : { vote }
728
+ });
729
+ return {
730
+ outcome: r.outcome,
731
+ cancelRest: r.cancelRest,
732
+ reason: r.reason,
733
+ required
734
+ };
735
+ }
736
+ function shouldConverge(ctx) {
737
+ return evaluateConvergence(ctx).outcome === "approved";
738
+ }
739
+ function shouldTerminate(ctx) {
740
+ return evaluateConvergence(ctx).outcome === "rejected";
741
+ }
742
+ function groupTallies(tokens) {
743
+ const order = [];
744
+ const byGroup = /* @__PURE__ */ new Map();
745
+ for (const t of tokens) {
746
+ const g = t.instanceGroup;
747
+ if (g === void 0) continue;
748
+ const list = byGroup.get(g);
749
+ if (list === void 0) {
750
+ byGroup.set(g, [t]);
751
+ order.push(g);
752
+ } else {
753
+ list.push(t);
754
+ }
755
+ }
756
+ const out = [];
757
+ for (const groupId of order) {
758
+ const members = byGroup.get(groupId) ?? [];
759
+ const approved = members.filter((t) => t.vote === "approved").length;
760
+ const rejected = members.filter((t) => t.vote === "rejected").length;
761
+ const pending = members.filter((t) => LIVE_TOKEN_STATES.includes(t.state)).length;
762
+ const total = approved + rejected + pending;
763
+ if (total < 1) continue;
764
+ out.push({ groupId, nodeId: members[0]?.nodeId ?? "", total, approved, rejected, pending });
765
+ }
766
+ return out;
767
+ }
768
+ function convergeCtxOf(tally, approval) {
769
+ const v = approval.vote;
770
+ return {
771
+ mode: approval.mode,
772
+ total: tally.total,
773
+ approved: tally.approved,
774
+ rejected: tally.rejected,
775
+ pending: tally.pending,
776
+ onReject: approval.onReject,
777
+ ...v !== void 0 && "count" in v && typeof v.count === "number" ? { count: v.count } : {},
778
+ ...v !== void 0 && "threshold" in v && typeof v.threshold === "number" ? { threshold: v.threshold } : {}
779
+ };
780
+ }
781
+ function restTokenIds(tokens, groupId, keep = []) {
782
+ const live = LIVE_TOKEN_STATES;
783
+ return tokens.filter((t) => t.instanceGroup === groupId && live.includes(t.state) && !keep.includes(t.id)).map((t) => t.id);
784
+ }
785
+
786
+ // src/core/action.ts
787
+ function assertActionInput(action, path = "$") {
788
+ if (typeof action !== "object" || action === null || Array.isArray(action)) {
789
+ throw optionInvalid(path, "must be a plain object", action);
790
+ }
791
+ const a = action;
792
+ const fail = (field, reason2) => {
793
+ throw optionInvalid(`${path}.${field}`, reason2, a[field]);
794
+ };
795
+ if (typeof a.action !== "string" || a.action.length === 0) {
796
+ fail("action", "must be a non-empty string");
797
+ }
798
+ if (typeof a.actor !== "string" || a.actor.length === 0) {
799
+ fail("actor", "must be a non-empty string");
800
+ }
801
+ if (a.comment !== void 0 && typeof a.comment !== "string") {
802
+ fail("comment", "must be a string when present");
803
+ }
804
+ if (a.target !== void 0 && typeof a.target !== "string") {
805
+ fail("target", "must be a string when present");
806
+ }
807
+ if (a.at !== void 0 && (typeof a.at !== "string" || a.at.length === 0)) {
808
+ fail("at", "must be a non-empty ISO 8601 string when present");
809
+ }
810
+ if (a.payload !== void 0 && (typeof a.payload !== "object" || a.payload === null)) {
811
+ fail("payload", "must be a plain object when present");
812
+ }
813
+ }
814
+
815
+ // src/runtime/plan.ts
816
+ function plan(state, action, options = {}) {
817
+ assertActionInput(action, "action");
818
+ assertSerializableInput(state);
819
+ if (isTerminalStatus(state.status)) {
820
+ throw stateTerminal(state.instanceId, state.status, action.action);
821
+ }
822
+ const at = resolveAt(action, options);
823
+ const diagnostics = [];
824
+ let next = cloneState(state);
825
+ if (action.payload !== void 0) {
826
+ next.variables = { ...next.variables, ...action.payload };
827
+ }
828
+ if (options.apply !== void 0) {
829
+ const applied = options.apply(next);
830
+ if (typeof applied !== "object" || applied === null) {
831
+ throw optionInvalid("options.apply", "must return an InstanceState object", applied);
832
+ }
833
+ next = applied;
834
+ if (next.rev !== state.rev) {
835
+ throw stateShapeInvalid("options.apply must not touch rev (plan() owns it)", {
836
+ expected: state.rev,
837
+ got: next.rev
838
+ });
839
+ }
840
+ if (next.definitionVersion !== state.definitionVersion) {
841
+ throw stateShapeInvalid(
842
+ "options.apply must not touch definitionVersion (INV-19: an instance is bound to its version for life)",
843
+ { expected: state.definitionVersion, got: next.definitionVersion }
844
+ );
845
+ }
846
+ }
847
+ next.rev = state.rev + 1;
848
+ next.updatedAt = at;
849
+ const record = {
850
+ name: action.action,
851
+ actor: action.actor,
852
+ at
853
+ };
854
+ if (action.comment !== void 0) record.comment = action.comment;
855
+ if (action.target !== void 0) record.nodeId = action.target;
856
+ next.lastAction = record;
857
+ const entry = {
858
+ seq: nextAuditSeq(state.auditTrail),
859
+ at,
860
+ actor: action.actor,
861
+ action: action.action
862
+ };
863
+ if (action.target !== void 0) entry.nodeId = action.target;
864
+ if (action.comment !== void 0) entry.payload = { comment: action.comment };
865
+ if (action.payload !== void 0) {
866
+ entry.payload = { ...entry.payload ?? {}, ...action.payload };
867
+ }
868
+ const subject = subjectTokenOf(state, action.actor);
869
+ if (subject !== void 0) {
870
+ entry.tokenId = subject.id;
871
+ entry.from = subject.nodeId;
872
+ const after = next.tokens.find((t) => t.id === subject.id);
873
+ if (after !== void 0) entry.to = after.nodeId;
874
+ }
875
+ next.auditTrail = [...state.auditTrail, entry];
876
+ const maxEntries = options.maxAuditEntries;
877
+ if (maxEntries !== void 0) {
878
+ if (!Number.isInteger(maxEntries) || maxEntries < 1) {
879
+ throw optionInvalid("maxAuditEntries", "must be a positive integer", maxEntries);
880
+ }
881
+ if (next.auditTrail.length > maxEntries) {
882
+ const dropped = next.auditTrail.slice(0, next.auditTrail.length - maxEntries);
883
+ next.auditTrail = next.auditTrail.slice(next.auditTrail.length - maxEntries);
884
+ diagnostics.push(
885
+ engineDiagnostic({
886
+ code: ENGINE_DIAGNOSTIC_CODES.AUDIT_TRUNCATED,
887
+ message: `auditTrail exceeded maxAuditEntries and was truncated`,
888
+ instanceId: state.instanceId,
889
+ details: {
890
+ maxAuditEntries: maxEntries,
891
+ droppedCount: dropped.length,
892
+ droppedFromSeq: dropped[0]?.seq,
893
+ droppedToSeq: dropped[dropped.length - 1]?.seq
894
+ }
895
+ })
896
+ );
897
+ }
898
+ }
899
+ if (isTerminalStatus(next.status) && next.endedAt === void 0) {
900
+ next.endedAt = at;
901
+ }
902
+ assertRoundTrip(next, "next");
903
+ const tasks = options.tasks;
904
+ const diff = tasks === void 0 ? { added: [], removed: [], changed: [] } : diffTasks(tasks(state), tasks(next));
905
+ const delta = {
906
+ rev: next.rev,
907
+ action: record,
908
+ added: diff.added,
909
+ removed: diff.removed,
910
+ changed: diff.changed,
911
+ instance: headerOf(next)
912
+ };
913
+ return { next, delta, diagnostics };
914
+ }
915
+ function assertSerializableInput(state) {
916
+ if (typeof state !== "object" || state === null) {
917
+ throw optionInvalid("state", "must be a plain object", state);
918
+ }
919
+ if (typeof state.instanceId !== "string" || state.instanceId.length === 0) {
920
+ throw optionInvalid("state.instanceId", "must be a non-empty string", state.instanceId);
921
+ }
922
+ if (!Number.isInteger(state.rev) || state.rev < 0) {
923
+ throw optionInvalid("state.rev", "must be a non-negative integer", state.rev);
924
+ }
925
+ }
926
+ function resolveAt(action, options) {
927
+ if (action.at !== void 0) return action.at;
928
+ const clocked = options.clock?.();
929
+ if (typeof clocked === "string" && clocked.length > 0) return clocked;
930
+ throw optionInvalid(
931
+ "at",
932
+ "plan() needs a time source: pass action.at, or pass PlanOptions.clock (EngineConfig.clock in submit())",
933
+ void 0
934
+ );
935
+ }
936
+ function nextAuditSeq(trail) {
937
+ let max = 0;
938
+ for (const e of trail) {
939
+ if (typeof e.seq === "number" && e.seq > max) max = e.seq;
940
+ }
941
+ return max + 1;
942
+ }
943
+
944
+ // src/runtime/queue.ts
945
+ function createInstanceQueue() {
946
+ const tails = /* @__PURE__ */ new Map();
947
+ const depths = /* @__PURE__ */ new Map();
948
+ function run(instanceId, fn) {
949
+ const prev = tails.get(instanceId) ?? Promise.resolve();
950
+ depths.set(instanceId, (depths.get(instanceId) ?? 0) + 1);
951
+ const task = async () => fn();
952
+ const result = prev.then(task, task);
953
+ const cleanup = result.then(
954
+ () => void 0,
955
+ () => void 0
956
+ );
957
+ tails.set(instanceId, cleanup);
958
+ void cleanup.then(() => {
959
+ const left = (depths.get(instanceId) ?? 1) - 1;
960
+ if (left <= 0) {
961
+ depths.delete(instanceId);
962
+ tails.delete(instanceId);
963
+ } else {
964
+ depths.set(instanceId, left);
965
+ }
966
+ });
967
+ return result;
968
+ }
969
+ async function drain() {
970
+ await Promise.all([...tails.values()]);
971
+ }
972
+ return {
973
+ run,
974
+ drain,
975
+ size: () => depths.size,
976
+ depth: (instanceId) => depths.get(instanceId) ?? 0
977
+ };
978
+ }
979
+
980
+ // src/nodes/events.ts
981
+ function eventBehaviorOf(type) {
982
+ switch (type) {
983
+ case "startEvent":
984
+ return "start";
985
+ case "endEvent":
986
+ return "terminal";
987
+ case "intermediateCatchEvent":
988
+ return "catch";
989
+ case "boundaryEvent":
990
+ return "pass";
991
+ case "intermediateThrowEvent":
992
+ case "implicitThrowEvent":
993
+ return "unsupported";
994
+ default:
995
+ return void 0;
996
+ }
997
+ }
998
+ function assertEventSupported(type, nodeId, behavior) {
999
+ if (behavior !== "unsupported") return;
1000
+ const owner = {
1001
+ intermediateThrowEvent: "FR-E14 / T20\uFF08\u5411\u5916\u629B\u51FA\uFF1A\u65E0 MessageSink \u51FA\u53E3\uFF0C\u987B\u5148\u6539 ADR-006 \u4E8B\u4EF6\u96C6\uFF09",
1002
+ implicitThrowEvent: "FR-E24 / T18\uFF08\u4E8B\u4EF6\u5B50\u6D41\u7A0B\u5185\u7684\u9690\u5F0F\u629B\u51FA\uFF09"
1003
+ };
1004
+ throw stateShapeInvalid(`node '${nodeId}' is a '${type}', which is not executable yet`, {
1005
+ nodeId,
1006
+ type,
1007
+ owner: owner[type] ?? "unknown",
1008
+ behavior,
1009
+ hint: '\u8BE5\u4E8B\u4EF6\u7C7B\u578B\u5DF2\u77E5\u4F46\u5C1A\u672A\u5B9E\u73B0\uFF1B\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u964D\u7EA7\u6210\u81EA\u52A8\u76F4\u901A\uFF08\u90A3\u4F1A\u8BA9"\u4E8B\u4EF6\u6CA1\u53D1\u751F"\u53D8\u6210\u9759\u9ED8\u4E8B\u5B9E\uFF09'
1010
+ });
1011
+ }
1012
+
1013
+ // src/nodes/activities.ts
1014
+ function activityBehaviorOf(type) {
1015
+ switch (type) {
1016
+ case "callActivity":
1017
+ return "call";
1018
+ case "subProcess":
1019
+ case "adHocSubProcess":
1020
+ case "transaction":
1021
+ return "unsupported";
1022
+ default:
1023
+ return void 0;
1024
+ }
1025
+ }
1026
+ function assertActivitySupported(type, nodeId, behavior) {
1027
+ if (behavior !== "unsupported") return;
1028
+ const owner = {
1029
+ subProcess: "FR-E24 / T21\uFF08\u4E8B\u4EF6\u5B50\u6D41\u7A0B triggeredByEvent\uFF09",
1030
+ adHocSubProcess: "FR-E18\uFF08\u8FD0\u884C\u65F6\u7F16\u6392\uFF09",
1031
+ transaction: "FR-E13 / T21\uFF08transaction \u5FC5\u987B\u542B\u5185\u5D4C\u8282\u70B9\u624D\u80FD\u5C55\u5F00\uFF1B\u8865\u507F\u5904\u7406\u5668 compensate \u5F52 v1.x\uFF09"
1032
+ };
1033
+ throw stateShapeInvalid(`node '${nodeId}' is a '${type}', which is not executable yet`, {
1034
+ nodeId,
1035
+ type,
1036
+ owner: owner[type] ?? "unknown",
1037
+ behavior,
1038
+ hint: '\u8BE5\u6D3B\u52A8\u7C7B\u578B\u5DF2\u77E5\u4F46\u5C1A\u672A\u5B9E\u73B0\uFF1B\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u964D\u7EA7\u6210\u81EA\u52A8\u76F4\u901A\uFF08\u90A3\u4F1A\u8BA9"\u8FD9\u6BB5\u5B50\u6D41\u7A0B\u4ECE\u6765\u6CA1\u6267\u884C\u8FC7"\u53D8\u6210\u9759\u9ED8\u4E8B\u5B9E\uFF09'
1039
+ });
1040
+ }
1041
+ var SUBPROCESS_PATH_SEP = "/";
1042
+ var SUBPROCESS_EXIT_TYPE = "subProcessExit";
1043
+ function expandSubProcesses(nodes, flows) {
1044
+ return flatten(nodes, flows, "");
1045
+ }
1046
+ function isExpandable(n) {
1047
+ return (n.type === "subProcess" || n.type === "transaction") && n.triggeredByEvent !== true && Array.isArray(n.nodes) && n.nodes.length > 0;
1048
+ }
1049
+ function flatten(nodes, flows, prefix) {
1050
+ const outNodes = [];
1051
+ const outFlows = [];
1052
+ const subs = /* @__PURE__ */ new Map();
1053
+ for (const n of nodes) {
1054
+ if (n === null || typeof n !== "object" || typeof n.id !== "string") continue;
1055
+ if (!isExpandable(n)) {
1056
+ outNodes.push(rename(n, prefix));
1057
+ continue;
1058
+ }
1059
+ const children = (n.nodes ?? []).filter(
1060
+ (c) => c !== null && typeof c === "object" && typeof c.id === "string"
1061
+ );
1062
+ const childPrefix = `${prefix}${n.id}${SUBPROCESS_PATH_SEP}`;
1063
+ const starts = children.filter((c) => c.type === "startEvent");
1064
+ const ends = children.filter((c) => c.type === "endEvent");
1065
+ if (starts.length !== 1) {
1066
+ throw stateShapeInvalid(
1067
+ `embedded subProcess '${prefix}${n.id}' must have exactly one startEvent (found ${starts.length})`,
1068
+ { nodeId: `${prefix}${n.id}`, found: starts.length }
1069
+ );
1070
+ }
1071
+ if (ends.length === 0) {
1072
+ throw stateShapeInvalid(
1073
+ `embedded subProcess '${prefix}${n.id}' has no endEvent \u2014\u2014 \u4EE4\u724C\u8FDB\u53BB\u5C31\u51FA\u4E0D\u6765\u4E86`,
1074
+ { nodeId: `${prefix}${n.id}` }
1075
+ );
1076
+ }
1077
+ const inner = flatten(children, n.flows ?? [], childPrefix);
1078
+ const endIds = new Set(ends.map((e) => e.id));
1079
+ for (const c of inner.nodes) {
1080
+ const local = c.id.startsWith(childPrefix) ? c.id.slice(childPrefix.length) : c.id;
1081
+ outNodes.push(endIds.has(local) ? { ...c, type: SUBPROCESS_EXIT_TYPE } : c);
1082
+ }
1083
+ outFlows.push(...inner.flows);
1084
+ const entry = `${childPrefix}${starts[0].id}`;
1085
+ const exits = ends.map((e) => `${childPrefix}${e.id}`);
1086
+ subs.set(n.id, { entry, exits });
1087
+ }
1088
+ for (const f of flows) {
1089
+ if (f === null || typeof f !== "object") continue;
1090
+ if (typeof f.from !== "string" || typeof f.to !== "string") continue;
1091
+ const fromSub = subs.get(f.from);
1092
+ const toSub = subs.get(f.to);
1093
+ const fromIds = fromSub === void 0 ? [`${prefix}${f.from}`] : fromSub.exits;
1094
+ const to = toSub === void 0 ? `${prefix}${f.to}` : toSub.entry;
1095
+ for (const from of fromIds) {
1096
+ const base = typeof f.id === "string" ? `${prefix}${f.id}` : void 0;
1097
+ const id = base === void 0 ? void 0 : fromIds.length > 1 ? `${base}@${from}` : base;
1098
+ outFlows.push({ ...f, ...id === void 0 ? {} : { id }, from, to });
1099
+ }
1100
+ }
1101
+ return { nodes: outNodes, flows: outFlows };
1102
+ }
1103
+ function rename(n, prefix) {
1104
+ if (prefix === "") return n;
1105
+ const out = { ...n, id: `${prefix}${n.id}` };
1106
+ if (typeof n.defaultFlow === "string" && n.defaultFlow.length > 0) {
1107
+ out.defaultFlow = `${prefix}${n.defaultFlow}`;
1108
+ }
1109
+ if (typeof n.attachedTo === "string" && n.attachedTo.length > 0) {
1110
+ out.attachedTo = `${prefix}${n.attachedTo}`;
1111
+ }
1112
+ return out;
1113
+ }
1114
+ var CALL_EXT_KEY = "floken:call";
1115
+ function callTargetOf(node) {
1116
+ if (node === void 0 || node.type !== "callActivity") return void 0;
1117
+ const raw = node.extension?.[CALL_EXT_KEY];
1118
+ const version = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw["version"] : void 0;
1119
+ if (typeof version !== "number" || !Number.isInteger(version) || version < 1) {
1120
+ throw stateShapeInvalid(
1121
+ `callActivity '${node.id}' must bind an explicit definition version (INV-16)`,
1122
+ {
1123
+ nodeId: node.id,
1124
+ // 诊断文案**刻意不逐字写**禁用 API 名(有一道源码扫描门禁),语义照样讲清楚
1125
+ hint: `\u5728\u8BE5\u8282\u70B9\u7684 extension['${CALL_EXT_KEY}'] \u4E0A\u5199 { version: <\u6B63\u6574\u6570> }\uFF1B\u5F15\u64CE\u4E0D\u66FF\u5BBF\u4E3B\u53D6\u6700\u65B0\u7248\uFF08AC-E10\uFF09`,
1126
+ got: version === void 0 ? "undefined" : String(version)
1127
+ }
1128
+ );
1129
+ }
1130
+ const processId = typeof node.calledElement === "string" ? node.calledElement.trim() : "";
1131
+ if (processId === "") {
1132
+ throw stateShapeInvalid(`callActivity '${node.id}' has no calledElement (the process to call)`, {
1133
+ nodeId: node.id,
1134
+ hint: "\u7ED9\u8BE5\u8282\u70B9\u5199 calledElement = \u88AB\u8C03\u7528\u6D41\u7A0B\u7684 processId"
1135
+ });
1136
+ }
1137
+ return { processId, definitionVersion: version };
1138
+ }
1139
+ function callInstanceIdOf(state, nodeId, tokenId) {
1140
+ const base = `${state.instanceId}::${nodeId}::${tokenId}`;
1141
+ const taken = (state.childInstanceIds ?? []).filter(
1142
+ (id) => id === base || id.startsWith(`${base}#`)
1143
+ );
1144
+ return taken.length === 0 ? base : `${base}#${taken.length}`;
1145
+ }
1146
+ function parkForCall(next, token, target) {
1147
+ const instanceId = callInstanceIdOf(next, token.nodeId, token.id);
1148
+ token.state = "waiting";
1149
+ const known = next.childInstanceIds ?? [];
1150
+ if (!known.includes(instanceId)) next.childInstanceIds = [...known, instanceId];
1151
+ return {
1152
+ nodeId: token.nodeId,
1153
+ tokenId: token.id,
1154
+ instanceId,
1155
+ processId: target.processId,
1156
+ definitionVersion: target.definitionVersion,
1157
+ variables: { ...next.variables }
1158
+ };
1159
+ }
1160
+ var CALL_RETURN_ACTION = "callActivityReturn";
1161
+ function callReturnOf(state, parent, nextNodeId) {
1162
+ const next = cloneState(state);
1163
+ const token = next.tokens.find((t) => t.id === parent.tokenId);
1164
+ if (token === void 0) {
1165
+ throw stateShapeInvalid(
1166
+ `callActivity return: parent instance '${state.instanceId}' has no token '${parent.tokenId}'`,
1167
+ { instanceId: state.instanceId, tokenId: parent.tokenId }
1168
+ );
1169
+ }
1170
+ if (token.state !== "waiting") {
1171
+ throw stateShapeInvalid(
1172
+ `callActivity return: token '${token.id}' is '${token.state}', expected 'waiting'`,
1173
+ { instanceId: state.instanceId, tokenId: token.id, tokenState: token.state }
1174
+ );
1175
+ }
1176
+ markCompleted(next, parent.nodeId);
1177
+ token.state = "active";
1178
+ token.nodeId = nextNodeId;
1179
+ clearAssignment(token);
1180
+ return next;
1181
+ }
1182
+
1183
+ // src/nodes/flows.ts
1184
+ var DATA_NODE_TYPES = [
1185
+ "dataObject",
1186
+ "dataObjectReference",
1187
+ "dataStoreReference"
1188
+ ];
1189
+ var DATA_ACCESS = "readOnly";
1190
+ function isDataNode(type) {
1191
+ return DATA_NODE_TYPES.includes(type ?? "");
1192
+ }
1193
+ function assertNotDataNode(type, nodeId) {
1194
+ if (!isDataNode(type)) return;
1195
+ throw stateShapeInvalid(
1196
+ `token arrived at data node '${nodeId}' (${type}); data elements are not executable`,
1197
+ {
1198
+ nodeId,
1199
+ type,
1200
+ access: DATA_ACCESS,
1201
+ hint: "\u6570\u636E\u5143\u7D20\uFF08dataObject / dataObjectReference / dataStoreReference\uFF09\u7531\u4E1A\u52A1\u65B9\u7BA1\u7406\uFF0C\u5F15\u64CE\u53EA\u8BFB\u4E0D\u5199\uFF1B\u4EE4\u724C\u4E0D\u8BE5\u7ECF sequenceFlow \u843D\u5230\u5B83\u4E0A\u9762"
1202
+ }
1203
+ );
1204
+ }
1205
+ function flowPasses(flow, isTrue) {
1206
+ if (flow.expression === void 0) return true;
1207
+ return isTrue(flow.expression);
1208
+ }
1209
+
1210
+ // src/nodes/catch.ts
1211
+ var CATCH_KINDS = ["message", "signal"];
1212
+ var MESSAGE_DELIVER_ACTION = "deliverMessage";
1213
+ var SIGNAL_DELIVER_ACTION = "deliverSignal";
1214
+ var DELIVER_ACTIONS = [MESSAGE_DELIVER_ACTION, SIGNAL_DELIVER_ACTION];
1215
+ var WAIT_OWNER = {
1216
+ timer: "FR-E14 / T21\uFF08\u5B9A\u65F6\u5668\uFF1A\u7ECF Scheduler SPI \u6392\u7A0B\uFF09",
1217
+ error: "FR-E13 / T21\uFF08\u5F02\u5E38\u4E0E\u8865\u507F\uFF09",
1218
+ escalation: "FR-E13 / T21\uFF08\u5347\u7EA7\u4E8B\u4EF6\uFF09",
1219
+ cancel: "FR-E13 / T21\uFF08\u4E8B\u52A1\u53D6\u6D88\uFF09",
1220
+ compensate: "FR-E13 / T21\uFF08\u8865\u507F\uFF09",
1221
+ conditional: "FR-E14 / T21\uFF08\u6761\u4EF6\u4E8B\u4EF6\uFF1A\u9700\u8981\u53D8\u91CF\u7EA7\u53D8\u66F4\u8BA2\u9605\uFF09"
1222
+ };
1223
+ function catchBindingOf(node) {
1224
+ if (node === void 0) return void 0;
1225
+ if (node.type === "receiveTask") {
1226
+ return { kind: "message", name: requiredName(node, node.messageRef, "messageRef") };
1227
+ }
1228
+ if (node.type !== "intermediateCatchEvent") return void 0;
1229
+ const def = node.eventDefinition;
1230
+ const type = typeof def?.type === "string" ? def.type : void 0;
1231
+ if (type === "message") return { kind: "message", name: requiredName(node, def?.messageRef, "messageRef") };
1232
+ if (type === "signal") return { kind: "signal", name: requiredName(node, def?.signalRef, "signalRef") };
1233
+ throw stateShapeInvalid(
1234
+ `node '${node.id}' is an intermediateCatchEvent waiting on '${type ?? "(no eventDefinition)"}', which is not deliverable yet`,
1235
+ {
1236
+ nodeId: node.id,
1237
+ type: node.type,
1238
+ ...type !== void 0 ? { eventDefinition: type } : {},
1239
+ owner: type === void 0 ? "FR-E14 / T20" : WAIT_OWNER[type] ?? "FR-E13 / T21",
1240
+ hint: "\u76EE\u524D\u53EA\u6709 message / signal \u4E24\u7C7B\u7B49\u5F85\u53EF\u88AB\u6295\u9012\u5524\u9192\uFF08T20\uFF09\uFF1B\u5176\u4F59\u7B49\u5F85\u79CD\u7C7B\u5F52 T21\uFF0C\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u4EEC\u964D\u7EA7\u6210\u81EA\u52A8\u76F4\u901A"
1241
+ }
1242
+ );
1243
+ }
1244
+ function requiredName(node, value, field) {
1245
+ if (typeof value !== "string" || value.trim() === "") {
1246
+ throw stateShapeInvalid(
1247
+ `node '${node.id}' (${node.type}) has no ${field}; it would wait forever with no way to wake it`,
1248
+ {
1249
+ nodeId: node.id,
1250
+ type: node.type,
1251
+ field,
1252
+ owner: "FR-E14 / T20",
1253
+ hint: `\u7ED9\u8BE5\u8282\u70B9\u8865 ${field}\uFF08\u6D88\u606F / \u4FE1\u53F7\u540D\uFF09\uFF1B\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u964D\u7EA7\u6210\u81EA\u52A8\u76F4\u901A \u2014\u2014 \u90A3\u4F1A\u53D8\u6210"\u6C38\u8FDC\u7B49\u4E0D\u5230\u3001\u4E14\u4E0D\u62A5\u9519"`
1254
+ }
1255
+ );
1256
+ }
1257
+ return value;
1258
+ }
1259
+ function parkForCatch(token, binding) {
1260
+ const awaiting = { kind: binding.kind, name: binding.name };
1261
+ token.awaiting = awaiting;
1262
+ }
1263
+ function matchingTokens(state, match) {
1264
+ return state.tokens.filter(
1265
+ (t) => LIVE_TOKEN_STATES.includes(t.state) && t.awaiting !== void 0 && t.awaiting.kind === match.kind && t.awaiting.name === match.name && (match.tokenIds === void 0 || match.tokenIds.includes(t.id))
1266
+ );
1267
+ }
1268
+ function waitingNamesOf(state) {
1269
+ const out = [];
1270
+ for (const t of state.tokens) {
1271
+ if (!LIVE_TOKEN_STATES.includes(t.state) || t.awaiting === void 0) continue;
1272
+ const label = `${t.awaiting.kind}:${t.awaiting.name}`;
1273
+ if (!out.includes(label)) out.push(label);
1274
+ }
1275
+ return out;
1276
+ }
1277
+ function wakeTokens(state, tokenIds) {
1278
+ const next = cloneState(state);
1279
+ for (const id of tokenIds) {
1280
+ const t = next.tokens.find((x) => x.id === id);
1281
+ if (t === void 0) {
1282
+ throw stateShapeInvalid(`wakeTokens targets unknown token '${id}'`, {
1283
+ tokenId: id,
1284
+ tokenIds: next.tokens.map((x) => x.id)
1285
+ });
1286
+ }
1287
+ delete t.awaiting;
1288
+ }
1289
+ return next;
1290
+ }
1291
+
1292
+ // src/nodes/boundary.ts
1293
+ var BOUNDARY_TYPE = "boundaryEvent";
1294
+ function boundaryBindingOf(node) {
1295
+ if (node === void 0 || node.type !== BOUNDARY_TYPE) return void 0;
1296
+ const attachedTo = node.attachedTo;
1297
+ if (typeof attachedTo !== "string" || attachedTo.trim() === "") {
1298
+ throw stateShapeInvalid(
1299
+ `boundaryEvent '${node.id}' has no attachedTo; it would listen to nothing`,
1300
+ {
1301
+ nodeId: node.id,
1302
+ field: "attachedTo",
1303
+ owner: "FR-E13 / T21",
1304
+ hint: "\u8FB9\u754C\u4E8B\u4EF6\u5FC5\u987B\u6302\u5728\u67D0\u4E2A\u6D3B\u52A8\u4E0A\uFF08attachedTo = \u5BBF\u4E3B\u6D3B\u52A8 id\uFF09\uFF1B\u60AC\u7A7A\u7684\u8FB9\u754C\u4E8B\u4EF6\u6C38\u8FDC\u4E0D\u4F1A\u89E6\u53D1\uFF0C\u5F15\u64CE\u523B\u610F\u4E0D\u653E\u884C"
1305
+ }
1306
+ );
1307
+ }
1308
+ const trigger = catchBindingOf({
1309
+ id: node.id,
1310
+ type: "intermediateCatchEvent",
1311
+ ...node.eventDefinition === void 0 ? {} : { eventDefinition: node.eventDefinition }
1312
+ });
1313
+ if (trigger === void 0) {
1314
+ throw stateShapeInvalid(`boundaryEvent '${node.id}' has no deliverable eventDefinition`, {
1315
+ nodeId: node.id,
1316
+ owner: "FR-E13 / T21"
1317
+ });
1318
+ }
1319
+ return {
1320
+ nodeId: node.id,
1321
+ attachedTo,
1322
+ cancelActivity: node.cancelActivity !== false,
1323
+ trigger
1324
+ };
1325
+ }
1326
+ function armedBoundaries(state, graph, match) {
1327
+ const out = [];
1328
+ const seen = /* @__PURE__ */ new Set();
1329
+ for (const t of state.tokens) {
1330
+ if (!LIVE_TOKEN_STATES.includes(t.state)) continue;
1331
+ for (const b of graph.boundaryOf(t.nodeId)) {
1332
+ if (b.trigger.kind !== match.kind || b.trigger.name !== match.name) continue;
1333
+ const key = `${b.nodeId}::${b.attachedTo}`;
1334
+ if (seen.has(key)) continue;
1335
+ seen.add(key);
1336
+ out.push({ boundary: b, hostTokenId: t.id });
1337
+ }
1338
+ }
1339
+ return out;
1340
+ }
1341
+ function armedNamesOf(state, graph) {
1342
+ const out = [];
1343
+ for (const t of state.tokens) {
1344
+ if (!LIVE_TOKEN_STATES.includes(t.state)) continue;
1345
+ for (const b of graph.boundaryOf(t.nodeId)) {
1346
+ const label = `boundary:${b.trigger.kind}:${b.trigger.name}`;
1347
+ if (!out.includes(label)) out.push(label);
1348
+ }
1349
+ }
1350
+ return out;
1351
+ }
1352
+ var SCOPE_SEP = "/";
1353
+ function inScopeOf(tokenNodeId, hostId) {
1354
+ return tokenNodeId === hostId || tokenNodeId.startsWith(`${hostId}${SCOPE_SEP}`);
1355
+ }
1356
+ function cancelTargetsOf(state, hostId) {
1357
+ return state.tokens.filter((t) => LIVE_TOKEN_STATES.includes(t.state) && inScopeOf(t.nodeId, hostId)).map((t) => t.id);
1358
+ }
1359
+ function boundaryTokenIdOf(state, hostTokenId, boundaryId) {
1360
+ const base = `${hostTokenId}#${boundaryId}`;
1361
+ if (!state.tokens.some((t) => t.id === base)) return base;
1362
+ for (let n = 2; ; n += 1) {
1363
+ const candidate = `${base}#${n}`;
1364
+ if (!state.tokens.some((t) => t.id === candidate)) return candidate;
1365
+ }
1366
+ }
1367
+ function boundaryTokenOf(state, fire, host) {
1368
+ const out = {
1369
+ id: boundaryTokenIdOf(state, fire.hostTokenId, fire.boundary.nodeId),
1370
+ nodeId: fire.boundary.nodeId,
1371
+ state: "active"
1372
+ };
1373
+ if (host.branch !== void 0) out.branch = host.branch;
1374
+ return out;
1375
+ }
1376
+
1377
+ // src/nodes/graph.ts
1378
+ var WAITING_NODE_TYPES = ["userTask"];
1379
+ var APPROVAL_EXT_KEY = "floken:approval";
1380
+ function isWaitingNode(type) {
1381
+ return WAITING_NODE_TYPES.includes(type);
1382
+ }
1383
+ function expressionOf(condition) {
1384
+ if (condition === void 0 || condition === null) return void 0;
1385
+ const text = typeof condition === "string" ? condition : typeof condition === "object" && typeof condition.body === "string" ? condition.body : void 0;
1386
+ if (text === void 0) return void 0;
1387
+ const trimmed = text.trim();
1388
+ return trimmed === "" ? void 0 : trimmed;
1389
+ }
1390
+ function createProcessGraph(definition, processId, definitionVersion) {
1391
+ if (definition === null || typeof definition !== "object") {
1392
+ throw definitionMissing(processId, definitionVersion);
1393
+ }
1394
+ const processes = Array.isArray(definition.processes) ? definition.processes : [];
1395
+ const process = processes.find((p) => p?.id === processId);
1396
+ if (process === void 0) {
1397
+ throw definitionMissing(processId, definitionVersion);
1398
+ }
1399
+ const flat = expandSubProcesses(process.nodes ?? [], process.flows ?? []);
1400
+ const flatNodes = flat.nodes;
1401
+ const flatFlows = flat.flows;
1402
+ const nodes = /* @__PURE__ */ new Map();
1403
+ for (const n of flatNodes) {
1404
+ if (n === null || typeof n !== "object" || typeof n.id !== "string") continue;
1405
+ if (nodes.has(n.id)) {
1406
+ throw stateShapeInvalid(`duplicate node id '${n.id}' in process '${processId}'`, {
1407
+ processId,
1408
+ nodeId: n.id
1409
+ });
1410
+ }
1411
+ nodes.set(n.id, n);
1412
+ }
1413
+ const out = /* @__PURE__ */ new Map();
1414
+ const inn = /* @__PURE__ */ new Map();
1415
+ for (const f of flatFlows) {
1416
+ if (f === null || typeof f !== "object") continue;
1417
+ if (typeof f.from !== "string" || typeof f.to !== "string") continue;
1418
+ const flowId = typeof f.id === "string" ? f.id : `flow:${f.from}->${f.to}`;
1419
+ const expr = expressionOf(f.condition);
1420
+ const outList = out.get(f.from);
1421
+ if (outList === void 0) out.set(f.from, [{ id: flowId, to: f.to, ...expr === void 0 ? {} : { expression: expr } }]);
1422
+ else outList.push({ id: flowId, to: f.to, ...expr === void 0 ? {} : { expression: expr } });
1423
+ const inList = inn.get(f.to);
1424
+ if (inList === void 0) inn.set(f.to, [{ id: flowId, from: f.from }]);
1425
+ else inList.push({ id: flowId, from: f.from });
1426
+ }
1427
+ const startNode = flatNodes.find(
1428
+ (n) => n !== null && typeof n === "object" && n.type === "startEvent"
1429
+ );
1430
+ if (startNode === void 0) {
1431
+ throw definitionMissing(processId, definitionVersion);
1432
+ }
1433
+ const approvalCache = /* @__PURE__ */ new Map();
1434
+ const boundaries = /* @__PURE__ */ new Map();
1435
+ for (const n of flatNodes) {
1436
+ if (n === null || typeof n !== "object" || n.type !== "boundaryEvent") continue;
1437
+ const b = boundaryBindingOf(n);
1438
+ if (b === void 0) continue;
1439
+ const scopeSep = `${b.attachedTo}${SUBPROCESS_PATH_SEP}`;
1440
+ const inScope = [...nodes.keys()].some((id) => id === b.attachedTo || id.startsWith(scopeSep));
1441
+ if (!inScope) {
1442
+ throw stateShapeInvalid(
1443
+ `boundaryEvent '${b.nodeId}' is attached to unknown node '${b.attachedTo}'`,
1444
+ { nodeId: b.nodeId, attachedTo: b.attachedTo, processId }
1445
+ );
1446
+ }
1447
+ const outCount = (out.get(b.nodeId) ?? []).length;
1448
+ if (outCount !== 1) {
1449
+ throw stateShapeInvalid(
1450
+ `boundaryEvent '${b.nodeId}' must have exactly one outgoing flow (found ${outCount})`,
1451
+ { nodeId: b.nodeId, found: outCount, processId }
1452
+ );
1453
+ }
1454
+ const list = boundaries.get(b.attachedTo);
1455
+ if (list === void 0) boundaries.set(b.attachedTo, [b]);
1456
+ else list.push(b);
1457
+ }
1458
+ const reachCache = /* @__PURE__ */ new Map();
1459
+ const reachable = (from, to) => {
1460
+ if (from === to) return false;
1461
+ const key = `${from}\u2192${to}`;
1462
+ const cached = reachCache.get(key);
1463
+ if (cached !== void 0) return cached;
1464
+ const seen = /* @__PURE__ */ new Set([from]);
1465
+ const queue = [from];
1466
+ let found = false;
1467
+ while (queue.length > 0) {
1468
+ const cur = queue.shift();
1469
+ for (const e of out.get(cur) ?? []) {
1470
+ if (e.to === to) {
1471
+ found = true;
1472
+ break;
1473
+ }
1474
+ if (seen.has(e.to)) continue;
1475
+ seen.add(e.to);
1476
+ queue.push(e.to);
1477
+ }
1478
+ if (found) break;
1479
+ }
1480
+ reachCache.set(key, found);
1481
+ return found;
1482
+ };
1483
+ return {
1484
+ processId,
1485
+ definitionVersion,
1486
+ startNodeId: startNode.id,
1487
+ nodeIds: () => [...nodes.keys()],
1488
+ has: (nodeId) => nodes.has(nodeId),
1489
+ typeOf: (nodeId) => nodes.get(nodeId)?.type,
1490
+ nameOf: (nodeId) => nodes.get(nodeId)?.name,
1491
+ formKeyOf: (nodeId) => nodes.get(nodeId)?.formKey,
1492
+ outFlowsOf: (nodeId) => [...out.get(nodeId) ?? []],
1493
+ inFlowsOf: (nodeId) => [...inn.get(nodeId) ?? []],
1494
+ defaultFlowIdOf: (nodeId) => {
1495
+ const id = nodes.get(nodeId)?.defaultFlow;
1496
+ return typeof id === "string" && id.length > 0 ? id : void 0;
1497
+ },
1498
+ reachable,
1499
+ nextOf(nodeId) {
1500
+ const list = out.get(nodeId) ?? [];
1501
+ if (list.length === 0) return void 0;
1502
+ if (list.length === 1) return list[0].to;
1503
+ throw stateShapeInvalid(
1504
+ `node '${nodeId}' has ${list.length} outgoing flows; only gateways may route multiple branches (D-22)`,
1505
+ { nodeId, outgoing: list.map((f) => f.to) }
1506
+ );
1507
+ },
1508
+ approvalOf(nodeId) {
1509
+ const cached = approvalCache.get(nodeId);
1510
+ if (cached !== void 0 || approvalCache.has(nodeId)) return cached;
1511
+ const node = nodes.get(nodeId);
1512
+ const raw = node?.extension?.[APPROVAL_EXT_KEY];
1513
+ const value = raw === void 0 ? void 0 : normalizeApproval(raw, { nodeId });
1514
+ approvalCache.set(nodeId, value);
1515
+ return value;
1516
+ },
1517
+ hasApproval: (nodeId) => nodes.get(nodeId)?.extension?.[APPROVAL_EXT_KEY] !== void 0,
1518
+ scriptOf: (nodeId) => {
1519
+ const s = nodes.get(nodeId)?.script;
1520
+ if (typeof s !== "string") return void 0;
1521
+ const trimmed = s.trim();
1522
+ return trimmed === "" ? void 0 : trimmed;
1523
+ },
1524
+ scriptFormatOf: (nodeId) => {
1525
+ const f = nodes.get(nodeId)?.scriptFormat;
1526
+ return typeof f === "string" && f.trim() !== "" ? f : void 0;
1527
+ },
1528
+ handlerRefOf(nodeId) {
1529
+ const node = nodes.get(nodeId);
1530
+ const impl = node?.implementation;
1531
+ if (typeof impl === "string" && impl.length > 0 && !impl.startsWith("##")) return impl;
1532
+ const op = node?.operationRef;
1533
+ if (typeof op === "string" && op.length > 0) return op;
1534
+ return nodeId;
1535
+ },
1536
+ callTargetOf: (nodeId) => callTargetOf(nodes.get(nodeId)),
1537
+ catchOf: (nodeId) => catchBindingOf(nodes.get(nodeId)),
1538
+ /*
1539
+ * ★ 为什么要**沿内嵌作用域向上找**:`transaction` / `subProcess` 拍平后自身不在图里,
1540
+ * 令牌停的是 `Tx_1/Task_a`。挂在 `Tx_1` 上的边界事件必须能被**它里面的令牌**看见 ——
1541
+ * 否则事务边界事件永远不会被触发(而它唯一的意义就是管这一整片作用域)。
1542
+ * 逐段去掉 `/` 后缀向上找,嵌套几层都成立。
1543
+ */
1544
+ boundaryOf: (nodeId) => {
1545
+ const out2 = [];
1546
+ const push = (key) => {
1547
+ for (const b of boundaries.get(key) ?? []) out2.push(b);
1548
+ };
1549
+ push(nodeId);
1550
+ let cut = nodeId.lastIndexOf(SUBPROCESS_PATH_SEP);
1551
+ while (cut > 0) {
1552
+ const scope = nodeId.slice(0, cut);
1553
+ push(scope);
1554
+ cut = scope.lastIndexOf(SUBPROCESS_PATH_SEP);
1555
+ }
1556
+ return out2;
1557
+ }
1558
+ };
1559
+ }
1560
+ function assertTokensInGraph(state, graph) {
1561
+ for (const t of state.tokens) {
1562
+ if (!graph.has(t.nodeId)) {
1563
+ throw tokenOrphan(state.instanceId, t.id, t.nodeId);
1564
+ }
1565
+ }
1566
+ }
1567
+
1568
+ // src/nodes/gateways.ts
1569
+ var GATEWAY_TYPES = [
1570
+ "exclusiveGateway",
1571
+ "parallelGateway",
1572
+ "inclusiveGateway",
1573
+ "complexGateway",
1574
+ "eventBasedGateway"
1575
+ ];
1576
+ function isGatewayType(type) {
1577
+ return GATEWAY_TYPES.includes(type ?? "");
1578
+ }
1579
+ function isConverging(type) {
1580
+ return type === "parallelGateway" || type === "inclusiveGateway";
1581
+ }
1582
+ function routeGateway(input) {
1583
+ const { type, nodeId, outFlows, defaultFlowId, isTrue } = input;
1584
+ if (!isGatewayType(type)) {
1585
+ throw stateShapeInvalid(`node '${nodeId}' is not a gateway (type '${type}')`, {
1586
+ nodeId,
1587
+ type,
1588
+ gateways: [...GATEWAY_TYPES]
1589
+ });
1590
+ }
1591
+ if (outFlows.length === 0) {
1592
+ throw definitionMissing(nodeId, 0);
1593
+ }
1594
+ if (type === "complexGateway") {
1595
+ throw stateShapeInvalid(`gateway '${nodeId}' ('${type}') is not executable yet`, {
1596
+ nodeId,
1597
+ type,
1598
+ owner: "FR-E17\uFF08\u81EA\u5B9A\u4E49\u8868\u8FBE\u5F0F\uFF0CC \u7EA7\uFF09",
1599
+ hint: "\u8BE5\u7F51\u5173\u5DF2\u77E5\u4F46\u5F52 v1.x\uFF1B\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u964D\u7EA7\u6210\u300C\u53D6\u7B2C\u4E00\u6761\u51FA\u5411\u300D\uFF08\u90A3\u4F1A\u9759\u9ED8\u8D70\u9519\u5206\u652F\uFF09"
1600
+ });
1601
+ }
1602
+ if (type === "parallelGateway" || type === "eventBasedGateway") {
1603
+ return outFlows.map((f) => ({ flowId: f.id, to: f.to }));
1604
+ }
1605
+ const isDefault = (f) => f.id === defaultFlowId;
1606
+ const candidates = outFlows.filter((f) => !isDefault(f));
1607
+ const taken = (f) => flowPasses(f, () => isTrue(f));
1608
+ if (type === "exclusiveGateway") {
1609
+ for (const f of candidates) {
1610
+ if (taken(f)) return [{ flowId: f.id, to: f.to }];
1611
+ }
1612
+ return fallbackOf(input);
1613
+ }
1614
+ const chosen = candidates.filter((f) => taken(f));
1615
+ if (chosen.length > 0) return chosen.map((f) => ({ flowId: f.id, to: f.to }));
1616
+ return fallbackOf(input);
1617
+ }
1618
+ function fallbackOf(input) {
1619
+ const { nodeId, outFlows, defaultFlowId } = input;
1620
+ const fallback = outFlows.find((f) => f.id === defaultFlowId);
1621
+ if (fallback !== void 0) return [{ flowId: fallback.id, to: fallback.to }];
1622
+ throw stateShapeInvalid(
1623
+ `gateway '${nodeId}' ('${input.type}') has no branch to take: no condition matched and no default flow`,
1624
+ {
1625
+ nodeId,
1626
+ type: input.type,
1627
+ outgoing: outFlows.map((f) => f.id),
1628
+ defaultFlowId: defaultFlowId ?? null,
1629
+ hint: "\u7ED9\u8BE5\u7F51\u5173\u914D\u4E00\u6761 default \u6D41\uFF0C\u6216\u8BA9\u81F3\u5C11\u4E00\u6761\u51FA\u5411\u7684\u6761\u4EF6\u4E3A\u771F\uFF08BPMN \u89C4\u8303\u8981\u6C42\u81F3\u5C11\u8D70\u4E00\u6761\uFF09"
1630
+ }
1631
+ );
1632
+ }
1633
+ function canJoin(state, nodeId, graph) {
1634
+ for (const t of state.tokens) {
1635
+ if (!LIVE_TOKEN_STATES.includes(t.state)) continue;
1636
+ if (t.nodeId === nodeId) continue;
1637
+ if (graph.reachable(t.nodeId, nodeId)) return false;
1638
+ }
1639
+ return true;
1640
+ }
1641
+ function waitingAt(state, nodeId) {
1642
+ const out = [];
1643
+ state.tokens.forEach((t, i) => {
1644
+ if (t.nodeId === nodeId && LIVE_TOKEN_STATES.includes(t.state)) out.push(i);
1645
+ });
1646
+ return out;
1647
+ }
1648
+
1649
+ // src/nodes/tasks.ts
1650
+ function taskBehaviorOf(type) {
1651
+ switch (type) {
1652
+ case "userTask":
1653
+ return "wait";
1654
+ case "serviceTask":
1655
+ case "scriptTask":
1656
+ case "businessRuleTask":
1657
+ case "manualTask":
1658
+ return "effect";
1659
+ case "receiveTask":
1660
+ return "catch";
1661
+ case "task":
1662
+ return "pass";
1663
+ case "sendTask":
1664
+ return "unsupported";
1665
+ default:
1666
+ return void 0;
1667
+ }
1668
+ }
1669
+ function assertTaskSupported(type, nodeId, behavior) {
1670
+ if (behavior !== "unsupported") return;
1671
+ const owner = {
1672
+ sendTask: "FR-E14 / T20\uFF08\u5411\u5916\u629B\u51FA\uFF1AADR-006 \u4E8B\u4EF6\u96C6\u5B9A\u6B7B 10 \u4E2A\uFF0C\u987B\u5148\u6539 ADR \u518D\u52A0\uFF09"
1673
+ };
1674
+ throw stateShapeInvalid(`node '${nodeId}' is a '${type}', which is not executable yet`, {
1675
+ nodeId,
1676
+ type,
1677
+ owner: owner[type] ?? "unknown",
1678
+ behavior,
1679
+ hint: '\u8BE5\u4EFB\u52A1\u7C7B\u578B\u5DF2\u77E5\u4F46\u5C1A\u672A\u5B9E\u73B0\uFF1B\u5F15\u64CE\u523B\u610F\u4E0D\u628A\u5B83\u964D\u7EA7\u6210\u81EA\u52A8\u76F4\u901A\uFF08\u90A3\u4F1A\u8BA9"\u6D88\u606F\u6CA1\u53D1\u751F"\u53D8\u6210\u9759\u9ED8\u4E8B\u5B9E\uFF09'
1680
+ });
1681
+ }
1682
+ function effectKindOf(type) {
1683
+ switch (type) {
1684
+ case "serviceTask":
1685
+ return "service";
1686
+ case "scriptTask":
1687
+ return "script";
1688
+ case "businessRuleTask":
1689
+ return "decision";
1690
+ case "manualTask":
1691
+ return "manual";
1692
+ default:
1693
+ return void 0;
1694
+ }
1695
+ }
1696
+ var FEEL_SCRIPT_FORMATS = [
1697
+ "feel",
1698
+ "text/feel",
1699
+ "application/feel",
1700
+ "http://www.omg.org/spec/feel/20140401"
1701
+ ];
1702
+ function isFeelScriptFormat(format) {
1703
+ if (typeof format !== "string") return false;
1704
+ const norm = format.trim().toLowerCase().replace(/\/+$/, "");
1705
+ if (norm === "") return false;
1706
+ if (FEEL_SCRIPT_FORMATS.includes(norm)) return true;
1707
+ return norm.endsWith("/feel");
1708
+ }
1709
+ var NodeEffectUnresolved = class extends Error {
1710
+ name = "NodeEffectUnresolved";
1711
+ /** 内部信号:便于与其它错误一眼区分(不依赖 instanceof,跨包 / 跨产物都稳) */
1712
+ unresolved = true;
1713
+ key;
1714
+ nodeId;
1715
+ tokenId;
1716
+ kind;
1717
+ /** ★ 到达该节点**此刻**的变量快照 —— 解析要用它,不能拿提交前的旧值 */
1718
+ variables;
1719
+ constructor(args) {
1720
+ super(`effect of node '${args.nodeId}' on token '${args.tokenId}' is not resolved yet`);
1721
+ this.key = args.key;
1722
+ this.nodeId = args.nodeId;
1723
+ this.tokenId = args.tokenId;
1724
+ this.kind = args.kind;
1725
+ this.variables = args.variables;
1726
+ }
1727
+ };
1728
+ function unresolvedEffect(args) {
1729
+ return new NodeEffectUnresolved({
1730
+ key: effectKeyOf(args.nodeId, args.tokenId),
1731
+ nodeId: args.nodeId,
1732
+ tokenId: args.tokenId,
1733
+ kind: args.kind,
1734
+ variables: args.variables
1735
+ });
1736
+ }
1737
+ function effectKeyOf(nodeId, tokenId) {
1738
+ return `${nodeId}::${tokenId}`;
1739
+ }
1740
+ function asUnresolvedEffect(e) {
1741
+ if (e instanceof NodeEffectUnresolved) return e;
1742
+ if (typeof e === "object" && e !== null && e.unresolved === true && typeof e.key === "string" && typeof e.kind === "string") {
1743
+ return e;
1744
+ }
1745
+ return void 0;
1746
+ }
1747
+ function assertVariablePatch(nodeId, kind, value) {
1748
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1749
+ throw stateShapeInvalid(
1750
+ `${kind} handler of node '${nodeId}' must return a plain object of variables`,
1751
+ { nodeId, kind, returned: value === void 0 ? "undefined" : Array.isArray(value) ? "array" : typeof value }
1752
+ );
1753
+ }
1754
+ return { ...value };
1755
+ }
1756
+
1757
+ // src/runtime/loop.ts
1758
+ var PROBE_ASSIGNEE = "__probe__";
1759
+ var MAX_STEPS = 1e4;
1760
+ function applyPrimitiveCalls(state, calls) {
1761
+ let current = state;
1762
+ for (const call of calls) {
1763
+ const fn = primitives[call.primitive];
1764
+ current = fn(current, call.input);
1765
+ }
1766
+ return current;
1767
+ }
1768
+ function step(state, ctx, input) {
1769
+ let cur = applyPrimitiveCalls(state, input.calls);
1770
+ if (input.post !== void 0) cur = applyPost(cur, input.post);
1771
+ if (input.vote !== void 0) cur = castVote(cur, input.vote);
1772
+ cur = promoteSequential(cur, ctx);
1773
+ cur = settleGroups(cur, ctx, input.rejectTarget);
1774
+ return runToWait(cur, ctx);
1775
+ }
1776
+ function applyPost(state, post) {
1777
+ const next = cloneState(state);
1778
+ if (post.returnFromTokenId !== void 0) {
1779
+ const t = next.tokens.find((x) => x.id === post.returnFromTokenId);
1780
+ if (t === void 0) {
1781
+ throw stateShapeInvalid(`applyPost targets unknown token '${post.returnFromTokenId}'`, {
1782
+ tokenId: post.returnFromTokenId,
1783
+ tokenIds: next.tokens.map((x) => x.id)
1784
+ });
1785
+ }
1786
+ if (t.returnTo === void 0) {
1787
+ throw stateShapeInvalid(`token '${t.id}' has no delegation return path (returnTo)`, {
1788
+ tokenId: t.id
1789
+ });
1790
+ }
1791
+ t.assignee = t.returnTo;
1792
+ delete t.returnTo;
1793
+ }
1794
+ for (const id of post.dissolveTokenIds ?? []) {
1795
+ const t = next.tokens.find((x) => x.id === id);
1796
+ if (t !== void 0) delete t.instanceGroup;
1797
+ }
1798
+ return next;
1799
+ }
1800
+ function castVote(state, vote) {
1801
+ const next = cloneState(state);
1802
+ const token = next.tokens.find((t) => t.id === vote.tokenId);
1803
+ if (token === void 0) {
1804
+ throw stateShapeInvalid(`castVote targets unknown token '${vote.tokenId}'`, {
1805
+ tokenId: vote.tokenId,
1806
+ tokenIds: next.tokens.map((t) => t.id)
1807
+ });
1808
+ }
1809
+ if (!LIVE_TOKEN_STATES.includes(token.state)) {
1810
+ throw stateShapeInvalid(`castVote targets a token that is already '${token.state}'`, {
1811
+ tokenId: vote.tokenId,
1812
+ tokenState: token.state
1813
+ });
1814
+ }
1815
+ token.vote = vote.vote;
1816
+ token.state = "completed";
1817
+ return next;
1818
+ }
1819
+ function promoteSequential(state, ctx) {
1820
+ const next = cloneState(state);
1821
+ let changed = false;
1822
+ for (const tally of groupTallies(next.tokens)) {
1823
+ const approval = ctx.graph.approvalOf(tally.nodeId);
1824
+ if (approval === void 0 || approval.sequential !== true) continue;
1825
+ const members = next.tokens.filter((t) => t.instanceGroup === tally.groupId);
1826
+ if (members.some((t) => t.state === "active")) continue;
1827
+ const firstWaiting = members.find((t) => t.state === "waiting");
1828
+ if (firstWaiting === void 0) continue;
1829
+ firstWaiting.state = "active";
1830
+ firstWaiting.createdAt = ctx.at;
1831
+ changed = true;
1832
+ }
1833
+ return changed ? next : state;
1834
+ }
1835
+ function settleGroups(state, ctx, rejectTarget) {
1836
+ let cur = state;
1837
+ for (let i = 0; i < MAX_STEPS; i += 1) {
1838
+ const found = nextSettledGroup(cur, ctx);
1839
+ if (found === void 0) return cur;
1840
+ cur = applySettlement(cur, ctx, found, rejectTarget);
1841
+ }
1842
+ throw stateShapeInvalid("convergence did not settle within the step budget", {
1843
+ budget: MAX_STEPS,
1844
+ instanceId: cur.instanceId
1845
+ });
1846
+ }
1847
+ function nextSettledGroup(state, ctx) {
1848
+ for (const tally of groupTallies(state.tokens)) {
1849
+ const approval = ctx.graph.approvalOf(tally.nodeId);
1850
+ if (approval === void 0) {
1851
+ throw stateShapeInvalid(
1852
+ `instance group '${tally.groupId}' on node '${tally.nodeId}' has no floken:approval config; convergence needs mode / onReject / vote`,
1853
+ { groupId: tally.groupId, nodeId: tally.nodeId }
1854
+ );
1855
+ }
1856
+ const result = evaluateConvergence(convergeCtxOf(tally, approval));
1857
+ if (result.outcome !== "pending") return { tally, result, approval };
1858
+ }
1859
+ return void 0;
1860
+ }
1861
+ function applySettlement(state, ctx, g, rejectTarget) {
1862
+ const next = cloneState(state);
1863
+ const { groupId, nodeId } = g.tally;
1864
+ const approved = g.result.outcome === "approved";
1865
+ for (const id of restIdsOf(next, groupId)) {
1866
+ const t = next.tokens.find((x) => x.id === id);
1867
+ if (t !== void 0) t.state = "cancelled";
1868
+ }
1869
+ let to;
1870
+ if (approved) {
1871
+ const nxt = ctx.graph.nextOf(nodeId);
1872
+ if (nxt === void 0) {
1873
+ throw definitionMissing(ctx.graph.processId, ctx.graph.definitionVersion);
1874
+ }
1875
+ to = nxt;
1876
+ } else {
1877
+ to = resolveRejectTarget({
1878
+ name: "reject",
1879
+ approval: g.approval,
1880
+ completedNodes: next.completedNodes,
1881
+ ...rejectTarget !== void 0 ? { target: rejectTarget } : {},
1882
+ startNodeId: ctx.graph.startNodeId
1883
+ });
1884
+ }
1885
+ markCompleted(next, nodeId);
1886
+ if (!approved) next.completedNodes = next.completedNodes.filter((n) => n !== to);
1887
+ for (const t of next.tokens) {
1888
+ if (t.instanceGroup === groupId) delete t.instanceGroup;
1889
+ }
1890
+ next.tokens.push({
1891
+ id: `${groupId}#${approved ? "out" : "back"}`,
1892
+ nodeId: to,
1893
+ state: "active"
1894
+ });
1895
+ return next;
1896
+ }
1897
+ function restIdsOf(state, groupId) {
1898
+ return state.tokens.filter((t) => t.instanceGroup === groupId && LIVE_TOKEN_STATES.includes(t.state)).map((t) => t.id);
1899
+ }
1900
+ function runToWait(state, ctx) {
1901
+ let cur = cloneState(state);
1902
+ const landings = [];
1903
+ const events = [];
1904
+ const pendingCalls = [];
1905
+ const budget = { steps: 0 };
1906
+ for (let round = 0; round < MAX_STEPS; round += 1) {
1907
+ const merged = joinPass(cur, ctx);
1908
+ cur = advanceTokens(cur, ctx, landings, budget, events, pendingCalls);
1909
+ if (!merged) break;
1910
+ }
1911
+ const liveLeft = cur.tokens.filter((t) => LIVE_TOKEN_STATES.includes(t.state)).length;
1912
+ if (liveLeft === 0 && !isTerminalStatus(cur.status)) {
1913
+ cur.status = "completed";
1914
+ }
1915
+ return { next: cur, landings, events, pendingCalls };
1916
+ }
1917
+ function advanceTokens(state, ctx, landings, budget, events, pendingCalls) {
1918
+ const next = cloneState(state);
1919
+ for (let i = 0; i < next.tokens.length; i += 1) {
1920
+ const token = next.tokens[i];
1921
+ if (token === void 0) continue;
1922
+ if (!LIVE_TOKEN_STATES.includes(token.state)) continue;
1923
+ if (token.state === "waiting") continue;
1924
+ if (token.awaiting !== void 0) continue;
1925
+ for (; ; ) {
1926
+ budget.steps += 1;
1927
+ if (budget.steps > MAX_STEPS) {
1928
+ throw stateShapeInvalid("run-to-wait exceeded the step budget (cycle in the definition graph?)", {
1929
+ budget: MAX_STEPS,
1930
+ instanceId: next.instanceId
1931
+ });
1932
+ }
1933
+ const type = ctx.graph.typeOf(token.nodeId);
1934
+ if (type === void 0) {
1935
+ throw tokenOrphan(next.instanceId, token.id, token.nodeId);
1936
+ }
1937
+ assertNotDataNode(type, token.nodeId);
1938
+ const behavior = eventBehaviorOf(type);
1939
+ if (behavior !== void 0) {
1940
+ assertEventSupported(type, token.nodeId, behavior);
1941
+ if (behavior === "terminal") {
1942
+ token.state = "completed";
1943
+ break;
1944
+ }
1945
+ if (behavior === "catch") {
1946
+ parkCatch(token, ctx);
1947
+ break;
1948
+ }
1949
+ }
1950
+ if (isGatewayType(type)) {
1951
+ if (isConverging(type) && !canJoin(next, token.nodeId, ctx.graph)) break;
1952
+ const routed = routeGateway({
1953
+ type,
1954
+ nodeId: token.nodeId,
1955
+ outFlows: ctx.graph.outFlowsOf(token.nodeId),
1956
+ defaultFlowId: ctx.graph.defaultFlowIdOf(token.nodeId),
1957
+ // ★ 条件上下文取**此刻**的变量(T17:脚本 / 服务可能在本次推进里改过它)
1958
+ isTrue: (f) => ctx.conditionsOf(f, token.nodeId, next.variables)
1959
+ });
1960
+ markCompleted(next, token.nodeId);
1961
+ const race = type === "eventBasedGateway" ? `${token.nodeId}#${token.id}` : void 0;
1962
+ if (routed.length === 1) {
1963
+ token.nodeId = routed[0].to;
1964
+ clearAssignment(token);
1965
+ if (race !== void 0) token.race = race;
1966
+ continue;
1967
+ }
1968
+ forkToken(next, token, i, routed, race);
1969
+ continue;
1970
+ }
1971
+ const taskBehavior = taskBehaviorOf(type);
1972
+ if (taskBehavior !== void 0) {
1973
+ assertTaskSupported(type, token.nodeId, taskBehavior);
1974
+ if (taskBehavior === "effect") applyEffect(next, token, ctx, events);
1975
+ if (taskBehavior === "catch") {
1976
+ parkCatch(token, ctx);
1977
+ break;
1978
+ }
1979
+ }
1980
+ const activityBehavior = activityBehaviorOf(type);
1981
+ if (activityBehavior !== void 0) {
1982
+ assertActivitySupported(type, token.nodeId, activityBehavior);
1983
+ if (activityBehavior === "call") {
1984
+ const target = ctx.graph.callTargetOf(token.nodeId);
1985
+ if (target === void 0) {
1986
+ throw stateShapeInvalid(`node '${token.nodeId}' is a callActivity without a call target`, {
1987
+ nodeId: token.nodeId,
1988
+ instanceId: next.instanceId
1989
+ });
1990
+ }
1991
+ pendingCalls.push(parkForCall(next, token, target));
1992
+ break;
1993
+ }
1994
+ }
1995
+ if (isWaitingNode(type)) {
1996
+ const settled = settleAssignee(token, ctx, next, i);
1997
+ if (settled === "wait") {
1998
+ if (token.createdAt === void 0) token.createdAt = ctx.at;
1999
+ if (!landings.includes(token.nodeId)) landings.push(token.nodeId);
2000
+ break;
2001
+ }
2002
+ if (settled === "expanded") break;
2003
+ }
2004
+ const to = ctx.graph.nextOf(token.nodeId);
2005
+ if (to === void 0) {
2006
+ throw definitionMissing(next.processId, ctx.graph.definitionVersion);
2007
+ }
2008
+ markCompleted(next, token.nodeId);
2009
+ token.nodeId = to;
2010
+ clearAssignment(token);
2011
+ }
2012
+ }
2013
+ return next;
2014
+ }
2015
+ function applyEffect(next, token, ctx, events) {
2016
+ const effect = ctx.effectsOf(token.nodeId, token.id, next.variables);
2017
+ const patch = effect.variables;
2018
+ if (patch !== void 0 && Object.keys(patch).length > 0) {
2019
+ next.variables = { ...next.variables, ...patch };
2020
+ }
2021
+ for (const e of effect.events ?? []) events.push(e);
2022
+ }
2023
+ function forkToken(next, token, index, routed, race) {
2024
+ const first = routed[0];
2025
+ const root = token.branch ?? token.id;
2026
+ token.nodeId = first.to;
2027
+ token.branch = `${root}#${first.flowId}`;
2028
+ clearAssignment(token);
2029
+ if (race !== void 0) token.race = race;
2030
+ const created = [];
2031
+ for (let j = 1; j < routed.length; j += 1) {
2032
+ const r = routed[j];
2033
+ created.push({
2034
+ id: `${token.id}#${r.flowId}`,
2035
+ nodeId: r.to,
2036
+ state: "active",
2037
+ branch: `${root}#${r.flowId}`,
2038
+ ...race === void 0 ? {} : { race }
2039
+ });
2040
+ }
2041
+ next.tokens = [
2042
+ ...next.tokens.slice(0, index + 1),
2043
+ ...created,
2044
+ ...next.tokens.slice(index + 1)
2045
+ ];
2046
+ }
2047
+ function joinPass(state, ctx) {
2048
+ let merged = false;
2049
+ const visited = /* @__PURE__ */ new Set();
2050
+ for (const t of state.tokens) {
2051
+ if (!LIVE_TOKEN_STATES.includes(t.state)) continue;
2052
+ const nodeId = t.nodeId;
2053
+ if (visited.has(nodeId)) continue;
2054
+ visited.add(nodeId);
2055
+ const type = ctx.graph.typeOf(nodeId);
2056
+ if (type === void 0 || !isGatewayType(type) || !isConverging(type)) continue;
2057
+ if (ctx.graph.inFlowsOf(nodeId).length < 2) continue;
2058
+ if (!canJoin(state, nodeId, ctx.graph)) continue;
2059
+ const idxs = waitingAt(state, nodeId);
2060
+ if (idxs.length === 0) continue;
2061
+ for (let k = 1; k < idxs.length; k += 1) {
2062
+ const other = state.tokens[idxs[k]];
2063
+ if (other !== void 0) other.state = "completed";
2064
+ }
2065
+ const head = state.tokens[idxs[0]];
2066
+ if (head !== void 0) delete head.branch;
2067
+ merged = true;
2068
+ }
2069
+ return merged;
2070
+ }
2071
+ function parkCatch(token, ctx) {
2072
+ const binding = ctx.graph.catchOf(token.nodeId);
2073
+ if (binding === void 0) {
2074
+ throw stateShapeInvalid(`node '${token.nodeId}' is a catch node without a catch binding`, {
2075
+ nodeId: token.nodeId,
2076
+ type: ctx.graph.typeOf(token.nodeId) ?? null,
2077
+ instanceId: ctx.graph.processId
2078
+ });
2079
+ }
2080
+ parkForCatch(token, binding);
2081
+ if (token.createdAt === void 0) token.createdAt = ctx.at;
2082
+ }
2083
+ function settleAssignee(token, ctx, next, index) {
2084
+ if (token.assignee !== void 0) return "wait";
2085
+ const approval = ctx.graph.approvalOf(token.nodeId);
2086
+ const chosen = selectAssignees(ctx.assigneesOf(token.nodeId), approval?.approverPolicy);
2087
+ if (chosen.length === 0) {
2088
+ const onEmpty = approval?.onEmpty ?? "error";
2089
+ if (onEmpty === "error") {
2090
+ throw approverEmpty(token.nodeId, onEmpty, {
2091
+ instanceId: next.instanceId,
2092
+ tokenId: token.id
2093
+ });
2094
+ }
2095
+ return "skipped";
2096
+ }
2097
+ if (chosen.length === 1) {
2098
+ token.assignee = chosen[0];
2099
+ return "wait";
2100
+ }
2101
+ expandGroup(token, ctx, next, index, chosen, approval?.sequential === true);
2102
+ return "expanded";
2103
+ }
2104
+ function selectAssignees(list, policy) {
2105
+ if (policy === "first" || policy === "any") return list.slice(0, 1);
2106
+ return list;
2107
+ }
2108
+ function expandGroup(token, ctx, next, index, assignees, sequential) {
2109
+ const groupId = `${token.nodeId}#${token.id}`;
2110
+ const created = assignees.map((assignee, i) => {
2111
+ const active = i === 0 || !sequential;
2112
+ const t = {
2113
+ id: `${groupId}#${i}`,
2114
+ nodeId: token.nodeId,
2115
+ state: active ? "active" : "waiting",
2116
+ assignee,
2117
+ instanceGroup: groupId,
2118
+ // ★ 分支标记**继承**:这一组人整体处在某条并行分支上(T16)
2119
+ ...token.branch !== void 0 ? { branch: token.branch } : {}
2120
+ };
2121
+ if (active) t.createdAt = ctx.at;
2122
+ return t;
2123
+ });
2124
+ next.tokens = [...next.tokens.slice(0, index), ...created, ...next.tokens.slice(index + 1)];
2125
+ }
2126
+ function taskStatusOf(state, token) {
2127
+ if (state.status === "suspended") return "suspended";
2128
+ if (token.returnTo !== void 0) return "delegated";
2129
+ return "active";
2130
+ }
2131
+ function tasksOf(state, graph) {
2132
+ const out = [];
2133
+ for (const t of state.tokens) {
2134
+ if (t.state !== "active") continue;
2135
+ if (t.assignee === void 0) continue;
2136
+ const view = {
2137
+ /*
2138
+ * ★ `taskId` = `${nodeId}:${tokenId}` —— **不是**裸 `token.id`。
2139
+ * 同一个令牌会依次流经多个等待节点(approve → 下一节点 → …),
2140
+ * 若 taskId 就是令牌 id,那么"驳回后重办"与"推进到下一步"会复用同一行,
2141
+ * 宿主的待办表就分不出「这是新待办」还是「还是原来那条」。
2142
+ */
2143
+ taskId: `${t.nodeId}:${t.id}`,
2144
+ instanceId: state.instanceId,
2145
+ nodeId: t.nodeId,
2146
+ assignee: t.assignee,
2147
+ status: taskStatusOf(state, t),
2148
+ createdAt: t.createdAt ?? state.startedAt
2149
+ };
2150
+ const name = graph.nameOf(t.nodeId);
2151
+ if (name !== void 0) view.nodeName = name;
2152
+ const formKey = graph.formKeyOf(t.nodeId);
2153
+ if (formKey !== void 0) view.formKey = formKey;
2154
+ out.push(view);
2155
+ }
2156
+ return out;
2157
+ }
2158
+
2159
+ // src/runtime/deliver.ts
2160
+ function deliverStep(state, ctx, match, mode = "point") {
2161
+ const graph = ctx.graph;
2162
+ const matches = matchingTokens(state, match);
2163
+ const winners = pickRaceWinners(matches);
2164
+ const fires = mode === "broadcast" ? armedBoundaries(state, graph, match) : winners.length === 0 ? armedBoundaries(state, graph, match).slice(0, 1) : [];
2165
+ if (winners.length === 0 && fires.length === 0) {
2166
+ throw deliverNoTarget(state.instanceId, match.kind, match.name, [
2167
+ ...waitingNamesOf(state),
2168
+ ...armedNamesOf(state, graph)
2169
+ ]);
2170
+ }
2171
+ const racedOut = resolveRace(state, winners);
2172
+ let cur = applyBoundaryFires(state, fires, racedOut);
2173
+ if (winners.length > 0) cur = leaveWait(cur, winners.map((t) => t.id), graph);
2174
+ const r = runToWait(cur, ctx);
2175
+ return {
2176
+ next: r.next,
2177
+ landings: r.landings,
2178
+ events: r.events,
2179
+ pendingCalls: r.pendingCalls,
2180
+ woken: winners.map((t) => t.id),
2181
+ nodeIds: winners.map((t) => t.nodeId),
2182
+ racedOut,
2183
+ fired: fires.map((f) => ({
2184
+ nodeId: f.boundary.nodeId,
2185
+ attachedTo: f.boundary.attachedTo,
2186
+ cancelActivity: f.boundary.cancelActivity,
2187
+ hostTokenId: f.hostTokenId
2188
+ }))
2189
+ };
2190
+ }
2191
+ function pickRaceWinners(matches) {
2192
+ const winners = [];
2193
+ const seen = /* @__PURE__ */ new Set();
2194
+ for (const t of matches) {
2195
+ if (t.race === void 0) {
2196
+ winners.push(t);
2197
+ continue;
2198
+ }
2199
+ if (seen.has(t.race)) continue;
2200
+ seen.add(t.race);
2201
+ winners.push(t);
2202
+ }
2203
+ return winners;
2204
+ }
2205
+ function resolveRace(state, winners) {
2206
+ const races = /* @__PURE__ */ new Set();
2207
+ for (const w of winners) if (w.race !== void 0) races.add(w.race);
2208
+ if (races.size === 0) return [];
2209
+ const winnerIds = new Set(winners.map((t) => t.id));
2210
+ return state.tokens.filter(
2211
+ (t) => t.race !== void 0 && races.has(t.race) && !winnerIds.has(t.id) && LIVE_TOKEN_STATES.includes(t.state)
2212
+ ).map((t) => t.id);
2213
+ }
2214
+ function applyBoundaryFires(state, fires, racedOut) {
2215
+ const next = cloneState(state);
2216
+ for (const id of racedOut) {
2217
+ const t = next.tokens.find((x) => x.id === id);
2218
+ if (t !== void 0) t.state = "cancelled";
2219
+ }
2220
+ for (const fire of fires) {
2221
+ const host = next.tokens.find((t) => t.id === fire.hostTokenId);
2222
+ if (host === void 0) continue;
2223
+ if (fire.boundary.cancelActivity) {
2224
+ for (const id of cancelTargetsOf(next, fire.boundary.attachedTo)) {
2225
+ const t = next.tokens.find((x) => x.id === id);
2226
+ if (t !== void 0) t.state = "cancelled";
2227
+ }
2228
+ }
2229
+ next.tokens.push(boundaryTokenOf(next, fire, host));
2230
+ }
2231
+ return next;
2232
+ }
2233
+ function leaveWait(state, tokenIds, graph) {
2234
+ const next = wakeTokens(state, tokenIds);
2235
+ for (const id of tokenIds) {
2236
+ const t = next.tokens.find((x) => x.id === id);
2237
+ if (t === void 0) continue;
2238
+ const to = graph.nextOf(t.nodeId);
2239
+ if (to === void 0) {
2240
+ throw definitionMissing(graph.processId, graph.definitionVersion);
2241
+ }
2242
+ markCompleted(next, t.nodeId);
2243
+ t.nodeId = to;
2244
+ clearAssignment(t);
2245
+ }
2246
+ return next;
2247
+ }
2248
+
2249
+ // src/runtime/timers.ts
2250
+ function timerKeyOf(nodeId, tokenId) {
2251
+ return `${nodeId}::${tokenId}`;
2252
+ }
2253
+ function timeoutSpecOf(t) {
2254
+ const out = {};
2255
+ if (typeof t.duration === "string") out.duration = t.duration;
2256
+ if (typeof t.date === "string") out.date = t.date;
2257
+ if (typeof t.cycle === "string") out.cycle = t.cycle;
2258
+ if (typeof t.workCalendar === "string") out.workCalendar = t.workCalendar;
2259
+ return out;
2260
+ }
2261
+ function timingKeysOf(state, graph) {
2262
+ const out = /* @__PURE__ */ new Map();
2263
+ for (const t of state.tokens) {
2264
+ if (t.state !== "active") continue;
2265
+ if (t.assignee === void 0) continue;
2266
+ const approval = graph.approvalOf(t.nodeId);
2267
+ const timeout = approval?.timeout;
2268
+ if (timeout === void 0) continue;
2269
+ const kinds = (timeout.actions ?? []).map((a) => a?.type).filter((k) => typeof k === "string");
2270
+ if (kinds.length === 0) continue;
2271
+ out.set(timerKeyOf(t.nodeId, t.id), {
2272
+ tokenId: t.id,
2273
+ nodeId: t.nodeId,
2274
+ // ★ 计时的起点 = **这条待办的创建时刻**,不是实例的启动时刻
2275
+ fromAt: t.createdAt ?? state.startedAt,
2276
+ timeout: timeoutSpecOf(timeout),
2277
+ kinds
2278
+ });
2279
+ }
2280
+ return out;
2281
+ }
2282
+ function diffTimers(prev, next, graph) {
2283
+ const before = timingKeysOf(prev, graph);
2284
+ const after = timingKeysOf(next, graph);
2285
+ const schedule = [];
2286
+ for (const [key, intent] of after) {
2287
+ if (!before.has(key)) schedule.push(intent);
2288
+ }
2289
+ const cancel = [];
2290
+ for (const key of before.keys()) {
2291
+ if (after.has(key)) continue;
2292
+ const [, tokenId] = splitKey(key);
2293
+ const token = next.tokens.find((t) => t.id === tokenId);
2294
+ const handles = token?.timerHandles ?? [];
2295
+ if (handles.length === 0) continue;
2296
+ cancel.push({ tokenId, handles });
2297
+ }
2298
+ return { schedule, cancel };
2299
+ }
2300
+ function splitKey(key) {
2301
+ const i = key.indexOf("::");
2302
+ return [key.slice(0, i), key.slice(i + 2)];
2303
+ }
2304
+
2305
+ // src/runtime/trace.ts
2306
+ var SYSTEM_AUDIT_ACTIONS = [
2307
+ "start",
2308
+ "callActivityReturn",
2309
+ "deliverMessage",
2310
+ "deliverSignal"
2311
+ ];
2312
+ function traceKindOf(action) {
2313
+ return ACTION_NAMES.includes(action) ? "approval" : "system";
2314
+ }
2315
+ function traceOf(state) {
2316
+ const entries = (state.auditTrail ?? []).map((e) => entryOf(e));
2317
+ const first = entries[0];
2318
+ if (first === void 0 || first.seq <= 1) {
2319
+ return { entries, truncated: false };
2320
+ }
2321
+ return {
2322
+ entries,
2323
+ truncated: true,
2324
+ droppedFromSeq: 1,
2325
+ droppedToSeq: first.seq - 1
2326
+ };
2327
+ }
2328
+ function entryOf(e) {
2329
+ const out = {
2330
+ seq: e.seq,
2331
+ at: e.at,
2332
+ actor: e.actor,
2333
+ action: e.action,
2334
+ kind: traceKindOf(e.action)
2335
+ };
2336
+ if (e.nodeId !== void 0) out.nodeId = e.nodeId;
2337
+ if (e.tokenId !== void 0) out.tokenId = e.tokenId;
2338
+ if (e.from !== void 0) out.from = e.from;
2339
+ if (e.to !== void 0) out.to = e.to;
2340
+ if (e.payload !== void 0) out.payload = e.payload;
2341
+ return out;
2342
+ }
2343
+
2344
+ // src/runtime/emit.ts
2345
+ function eventsOf(input) {
2346
+ const { before, delta, next } = input;
2347
+ const base = {
2348
+ at: delta.action.at,
2349
+ instanceId: next.instanceId,
2350
+ processId: next.processId,
2351
+ definitionVersion: next.definitionVersion,
2352
+ action: delta.action
2353
+ };
2354
+ const beforeMap = new Map(before.map((t) => [t.taskId, t]));
2355
+ const out = [];
2356
+ const taskEvent = (name, view, taskStatus) => {
2357
+ const e = {
2358
+ ...base,
2359
+ name,
2360
+ taskId: view.taskId,
2361
+ nodeId: view.nodeId,
2362
+ assignee: view.assignee,
2363
+ taskStatus
2364
+ };
2365
+ if (view.nodeName !== void 0) e.nodeName = view.nodeName;
2366
+ if (view.formKey !== void 0) e.formKey = view.formKey;
2367
+ out.push(e);
2368
+ };
2369
+ const instanceEvent = (name) => {
2370
+ const e = { ...base, name, status: next.status, instance: headerOf(next) };
2371
+ out.push(e);
2372
+ };
2373
+ if (input.previousStatus === void 0) instanceEvent("started");
2374
+ const statusEvent = instanceEventNameOf(input.previousStatus, next.status);
2375
+ if (statusEvent === "suspended" || statusEvent === "resumed") instanceEvent(statusEvent);
2376
+ for (const taskId of delta.removed) {
2377
+ const view = beforeMap.get(taskId);
2378
+ if (view === void 0) continue;
2379
+ const name = removalEventName(view, next);
2380
+ taskEvent(name, view, name === "taskCompleted" ? "done" : "cancelled");
2381
+ }
2382
+ for (const view of delta.added) {
2383
+ taskEvent("taskCreated", view, view.status);
2384
+ if (view.assignee.length > 0) taskEvent("taskAssigned", view, view.status);
2385
+ }
2386
+ for (const view of delta.changed) {
2387
+ taskEvent("taskUpdated", view, view.status);
2388
+ }
2389
+ if (statusEvent === "completed" || statusEvent === "terminated") instanceEvent(statusEvent);
2390
+ return out;
2391
+ }
2392
+ function instanceEventNameOf(previous, next) {
2393
+ if (previous === void 0) return "started";
2394
+ if (previous === next) return void 0;
2395
+ if (next === "suspended") return "suspended";
2396
+ if (next === "running") return "resumed";
2397
+ if (next === "completed") return "completed";
2398
+ if (next === "terminated") return "terminated";
2399
+ return void 0;
2400
+ }
2401
+ function removalEventName(view, next) {
2402
+ const token = next.tokens.find((t) => `${view.nodeId}:${t.id}` === view.taskId);
2403
+ if (token === void 0) return "taskCancelled";
2404
+ if (token.state === "completed") return "taskCompleted";
2405
+ if (token.state === "cancelled") return "taskCancelled";
2406
+ return token.nodeId === view.nodeId ? "taskCancelled" : "taskCompleted";
2407
+ }
2408
+ function emitAll(sink, events) {
2409
+ if (sink === void 0 || events.length === 0) return;
2410
+ for (const event of events) {
2411
+ try {
2412
+ const r = sink.emit(event);
2413
+ if (r !== null && typeof r === "object" && typeof r.then === "function") {
2414
+ r.then(void 0, () => void 0);
2415
+ }
2416
+ } catch {
2417
+ }
2418
+ }
2419
+ }
2420
+
2421
+ // src/actions/compile.ts
2422
+ function groupSizeOf(state, token) {
2423
+ const g = token.instanceGroup;
2424
+ if (g === void 0) return 1;
2425
+ return state.tokens.filter(
2426
+ (t) => t.instanceGroup === g && (LIVE_TOKEN_STATES.includes(t.state) || t.vote !== void 0)
2427
+ ).length;
2428
+ }
2429
+ var isActionName = (v) => ACTION_NAMES.includes(v);
2430
+ function resolveToken(state, ctx, action) {
2431
+ if (ctx.tokenId !== void 0) {
2432
+ const t = state.tokens.find((x) => x.id === ctx.tokenId);
2433
+ if (t === void 0) {
2434
+ throw stateShapeInvalid(`action '${action}' targets unknown token '${ctx.tokenId}'`, {
2435
+ action,
2436
+ tokenId: ctx.tokenId,
2437
+ tokenIds: state.tokens.map((x) => x.id)
2438
+ });
2439
+ }
2440
+ return t;
2441
+ }
2442
+ const live = state.tokens.filter((t) => LIVE_TOKEN_STATES.includes(t.state));
2443
+ if (live.length === 1) return live[0];
2444
+ throw stateShapeInvalid(
2445
+ `action '${action}' cannot resolve a unique token: pass context.tokenId (found ${live.length} live tokens)`,
2446
+ { action, liveTokenIds: live.map((t) => t.id) }
2447
+ );
2448
+ }
2449
+ function resolveNext(state, ctx, from, action) {
2450
+ const to = ctx.nextOf?.(from);
2451
+ if (typeof to !== "string" || to.length === 0) {
2452
+ throw definitionMissing(state.processId, state.definitionVersion);
2453
+ }
2454
+ return to;
2455
+ }
2456
+ function resolveAssignees(ctx, nodeId, action) {
2457
+ const list = ctx.assignees ?? [];
2458
+ if (list.length === 0) throw approverEmpty(nodeId, action);
2459
+ return list;
2460
+ }
2461
+ function resolveSingleAssignee(ctx, nodeId, action) {
2462
+ return resolveAssignees(ctx, nodeId, action)[0];
2463
+ }
2464
+ function compileAction(input, state, ctx) {
2465
+ const name = input.action;
2466
+ if (!isActionName(name)) {
2467
+ throw actionUnknown(name, ACTION_NAMES);
2468
+ }
2469
+ const spec = ACTION_SPEC_BY_NAME[name];
2470
+ const approval = ctx.approval;
2471
+ assertActionEnabled(name, approval);
2472
+ assertComment(name, spec, approval, input.comment);
2473
+ if (spec.primitives.length === 0) {
2474
+ throw stateShapeInvalid(
2475
+ `action '${name}' is not handled by the kernel (it has no primitive mapping)`,
2476
+ { action: name }
2477
+ );
2478
+ }
2479
+ const head = name === "suspend" || name === "resume" ? name : spec.primitives[0];
2480
+ const vote = groupVoteOf(name, state, ctx);
2481
+ if (vote !== void 0) {
2482
+ return { name, spec, calls: [], target: void 0, vote };
2483
+ }
2484
+ switch (head) {
2485
+ // —— 实例级控制(不需要令牌)——
2486
+ case "suspend":
2487
+ return { name, spec, calls: [{ primitive: "suspend", input: reason(input) }], target: void 0 };
2488
+ case "resume":
2489
+ return { name, spec, calls: [{ primitive: "resume", input: reason(input) }], target: void 0 };
2490
+ // —— 清场(不需要令牌)——
2491
+ case "halt":
2492
+ return { name, spec, calls: [{ primitive: "halt", input: reason(input) }], target: void 0 };
2493
+ // —— 回退类 6 项:先校验目标(INV-6),再编译 ——
2494
+ case "jumpTo":
2495
+ case "rollbackTo": {
2496
+ const token = resolveToken(state, ctx, name);
2497
+ const target = assertTarget({
2498
+ name,
2499
+ approval,
2500
+ completedNodes: state.completedNodes,
2501
+ target: input.target,
2502
+ ...ctx.startNodeId !== void 0 ? { startNodeId: ctx.startNodeId } : {}
2503
+ });
2504
+ const groupId = token.instanceGroup;
2505
+ if (groupId !== void 0 && groupSizeOf(state, token) > 1) {
2506
+ const others = state.tokens.filter(
2507
+ (t) => t.instanceGroup === groupId && t.id !== token.id && LIVE_TOKEN_STATES.includes(t.state)
2508
+ ).map((t) => t.id);
2509
+ const calls = [];
2510
+ if (others.length > 0) {
2511
+ calls.push({ primitive: "cancelInstances", input: { tokenIds: others } });
2512
+ }
2513
+ calls.push(rollbackOrJump(head, token, target));
2514
+ return { name, spec, calls, target, post: { dissolveTokenIds: [token.id] } };
2515
+ }
2516
+ return {
2517
+ name,
2518
+ spec,
2519
+ calls: [rollbackOrJump(head, token, target)],
2520
+ target
2521
+ };
2522
+ }
2523
+ // —— 正向推进(需要定义图算后继)——
2524
+ case "advance": {
2525
+ const token = resolveToken(state, ctx, name);
2526
+ if (name === "approve" && token.returnTo !== void 0) {
2527
+ return { name, spec, calls: [], target: void 0, post: { returnFromTokenId: token.id } };
2528
+ }
2529
+ const to = resolveNext(state, ctx, token.nodeId);
2530
+ return {
2531
+ name,
2532
+ spec,
2533
+ calls: [{ primitive: "advance", input: { tokenId: token.id, to } }],
2534
+ target: void 0
2535
+ };
2536
+ }
2537
+ // —— 换人(不动令牌)——
2538
+ case "transfer": {
2539
+ const token = resolveToken(state, ctx, name);
2540
+ return {
2541
+ name,
2542
+ spec,
2543
+ calls: [
2544
+ {
2545
+ primitive: "transfer",
2546
+ input: {
2547
+ tokenId: token.id,
2548
+ assignee: resolveSingleAssignee(ctx, token.nodeId, name)
2549
+ }
2550
+ }
2551
+ ],
2552
+ target: void 0
2553
+ };
2554
+ }
2555
+ case "delegate": {
2556
+ const token = resolveToken(state, ctx, name);
2557
+ return {
2558
+ name,
2559
+ spec,
2560
+ calls: [
2561
+ {
2562
+ primitive: "delegate",
2563
+ input: {
2564
+ tokenId: token.id,
2565
+ assignee: resolveSingleAssignee(ctx, token.nodeId, name)
2566
+ }
2567
+ }
2568
+ ],
2569
+ target: void 0
2570
+ };
2571
+ }
2572
+ // —— 加签 / 会签(INV-12 上限 + INV-13 空集)——
2573
+ case "spawnInstances": {
2574
+ const token = resolveToken(state, ctx, name);
2575
+ const nodeId = name === "addSignAfter" ? resolveNext(state, ctx, token.nodeId) : token.nodeId;
2576
+ const groupId = ctx.groupId ?? `${nodeId}#sign`;
2577
+ const assignees = resolveAssignees(ctx, nodeId, name);
2578
+ const isMultiInstance = spec.mode !== void 0;
2579
+ if (isMultiInstance && approval === void 0) {
2580
+ throw stateShapeInvalid(
2581
+ `action '${name}' requires a floken:approval config on node '${nodeId}' (mode / onReject / vote come from it)`,
2582
+ { action: name, nodeId }
2583
+ );
2584
+ }
2585
+ const maxCount = approval?.addSign.maxCount;
2586
+ if (typeof maxCount === "number") {
2587
+ const current = state.tokens.filter(
2588
+ (t) => t.nodeId === nodeId && LIVE_TOKEN_STATES.includes(t.state)
2589
+ ).length;
2590
+ const after = current + assignees.length;
2591
+ if (after > maxCount) throw addSignLimit(nodeId, maxCount, after);
2592
+ }
2593
+ return {
2594
+ name,
2595
+ spec,
2596
+ calls: [
2597
+ {
2598
+ primitive: "spawnInstances",
2599
+ input: {
2600
+ nodeId,
2601
+ groupId,
2602
+ assignees,
2603
+ // 会签 / 或签 / 票签是"展开":取代当前那个占位令牌;加签是"新增"
2604
+ ...spec.mode !== void 0 ? { replaceTokenId: token.id } : {},
2605
+ // 展开建组(要汇聚);加签不建组(见 `SpawnInstancesInput.grouped` 注释)
2606
+ grouped: isMultiInstance
2607
+ }
2608
+ }
2609
+ ],
2610
+ target: void 0
2611
+ };
2612
+ }
2613
+ // —— 减签(必须点名范围,不代劳)——
2614
+ case "cancelInstances": {
2615
+ const tokenIds = ctx.reduceTokenIds ?? [];
2616
+ if (tokenIds.length === 0) {
2617
+ throw stateShapeInvalid(
2618
+ `action '${name}' requires context.reduceTokenIds (which tokens to cancel)`,
2619
+ { action: name }
2620
+ );
2621
+ }
2622
+ return {
2623
+ name,
2624
+ spec,
2625
+ calls: [{ primitive: "cancelInstances", input: { tokenIds } }],
2626
+ target: void 0
2627
+ };
2628
+ }
2629
+ default: {
2630
+ const _exhaustive = head;
2631
+ throw stateShapeInvalid(`action '${name}' maps to unmapped primitive`, {
2632
+ action: name,
2633
+ primitive: String(_exhaustive)
2634
+ });
2635
+ }
2636
+ }
2637
+ }
2638
+ function rollbackOrJump(head, token, target) {
2639
+ if (head === "rollbackTo") {
2640
+ return {
2641
+ primitive: "rollbackTo",
2642
+ input: {
2643
+ tokenId: token.id,
2644
+ to: target,
2645
+ ...token.branch !== void 0 ? { branch: token.branch } : {}
2646
+ }
2647
+ };
2648
+ }
2649
+ return { primitive: "jumpTo", input: { tokenId: token.id, to: target } };
2650
+ }
2651
+ function groupVoteOf(name, state, ctx) {
2652
+ if (name !== "approve" && name !== "reject" && name !== "rejectToPrev") return void 0;
2653
+ const token = resolveToken(state, ctx, name);
2654
+ if (groupSizeOf(state, token) <= 1) return void 0;
2655
+ return { tokenId: token.id, vote: name === "approve" ? "approved" : "rejected" };
2656
+ }
2657
+ function reason(input) {
2658
+ return input.comment !== void 0 && input.comment.length > 0 ? { reason: input.comment } : {};
2659
+ }
2660
+
2661
+ // src/core/hooks.ts
2662
+ function freezeActionContext(ctx) {
2663
+ return deepFreeze(jsonClone(ctx));
2664
+ }
2665
+ function jsonClone(value) {
2666
+ return JSON.parse(JSON.stringify(value));
2667
+ }
2668
+ function deepFreeze(value) {
2669
+ if (value === null || typeof value !== "object") return value;
2670
+ if (Object.isFrozen(value)) return value;
2671
+ Object.freeze(value);
2672
+ for (const key of Object.keys(value)) {
2673
+ deepFreeze(value[key]);
2674
+ }
2675
+ return value;
2676
+ }
2677
+
2678
+ // src/store/memory.ts
2679
+ function createMemoryStore() {
2680
+ const rows = /* @__PURE__ */ new Map();
2681
+ return {
2682
+ async load(id) {
2683
+ const row = rows.get(id);
2684
+ return row === void 0 ? null : cloneState(row);
2685
+ },
2686
+ async save(next, expectedRev) {
2687
+ assertSerializable(next, "next");
2688
+ const current = rows.get(next.instanceId);
2689
+ if (expectedRev === 0) {
2690
+ if (current !== void 0) throw persistAlreadyExists(next.instanceId);
2691
+ rows.set(next.instanceId, cloneState({ ...next, rev: 1 }));
2692
+ return;
2693
+ }
2694
+ if (current === void 0) throw persistConflict(next.instanceId, expectedRev);
2695
+ if (current.rev !== expectedRev) {
2696
+ throw persistConflict(next.instanceId, expectedRev, current.rev);
2697
+ }
2698
+ rows.set(next.instanceId, cloneState({ ...next, rev: expectedRev + 1 }));
2699
+ }
2700
+ };
2701
+ }
2702
+ var JUEL_INTERPOLATION = /\$\{/;
2703
+ function assertNotJuel(source, where) {
2704
+ if (!JUEL_INTERPOLATION.test(source)) return;
2705
+ throw conditionInvalid(
2706
+ source,
2707
+ "JUEL \u63D2\u503C ${...} \u4E0D\u662F FEEL \u8BED\u6CD5",
2708
+ { hintKind: "juel", where }
2709
+ );
2710
+ }
2711
+ function createFeelConditionHandler(options = {}) {
2712
+ const feel = {
2713
+ ...options.allowedFunctions !== void 0 ? { allowedFunctions: [...options.allowedFunctions] } : {},
2714
+ ...options.maxNodes !== void 0 ? { maxNodes: options.maxNodes } : {},
2715
+ ...options.maxDepth !== void 0 ? { maxDepth: options.maxDepth } : {},
2716
+ ...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {}
2717
+ };
2718
+ return {
2719
+ evaluate(expression, ctx) {
2720
+ assertNotJuel(expression, "condition");
2721
+ const src = expression.trim();
2722
+ if (src === "") return true;
2723
+ const result = evaluate(src, ctx.variables, feel);
2724
+ if (typeof result.value !== "boolean") {
2725
+ throw conditionInvalid(
2726
+ expression,
2727
+ result.value === null || result.value === void 0 ? "\u6C42\u503C\u4E3A null\uFF08\u672A\u77E5\uFF09\u2014\u2014 \u7F51\u5173\u6761\u4EF6\u5FC5\u987B\u662F true / false" : `\u6C42\u503C\u7ED3\u679C\u4E0D\u662F\u5E03\u5C14\u503C\uFF08${typeof result.value}\uFF09`,
2728
+ {
2729
+ value: result.value ?? null,
2730
+ warnings: [...result.warnings]
2731
+ }
2732
+ );
2733
+ }
2734
+ return result.value;
2735
+ }
2736
+ };
2737
+ }
2738
+ async function evaluateCondition(handler, expression, ctx) {
2739
+ const value = await handler.evaluate(expression, ctx);
2740
+ if (typeof value !== "boolean") {
2741
+ throw conditionInvalid(
2742
+ expression,
2743
+ value === null || value === void 0 ? "conditionHandler \u8FD4\u56DE\u4E86 null / undefined \u2014\u2014 \u6C42\u503C\u5931\u8D25\u5FC5\u987B\u629B\u9519\uFF0C\u4E0D\u5F97\u8FD4\u56DE\u7A7A" : `conditionHandler \u8FD4\u56DE\u4E86\u975E\u5E03\u5C14\u503C\uFF08${typeof value}\uFF09`,
2744
+ { value: value ?? null, handler: "injected" }
2745
+ );
2746
+ }
2747
+ return value;
2748
+ }
2749
+ var ConditionUnresolved = class extends Error {
2750
+ name = "ConditionUnresolved";
2751
+ /** 内部信号:便于与其它错误一眼区分(不依赖 instanceof,跨包/跨产物都稳) */
2752
+ unresolved = true;
2753
+ flowId;
2754
+ expression;
2755
+ nodeId;
2756
+ /**
2757
+ * ★ **到达该网关那一刻**的变量快照(T17)。
2758
+ *
2759
+ * 为什么必须由哨兵带出来:解析发生在**重跑**里,那时拿不到"当时"的状态。
2760
+ * 若用提交前的旧变量去求值,`scriptTask` / `serviceTask` 在本次推进里改过的变量
2761
+ * 就被忽略了 —— 表现为「脚本把 amount 改成了 9000,网关却按旧值走了分支」。
2762
+ */
2763
+ variables;
2764
+ constructor(flowId, expression, nodeId, variables = {}) {
2765
+ super(`condition of flow '${flowId}' is not resolved yet`);
2766
+ this.flowId = flowId;
2767
+ this.expression = expression;
2768
+ this.nodeId = nodeId;
2769
+ this.variables = variables;
2770
+ }
2771
+ };
2772
+ function unresolvedCondition(flowId, expression, nodeId, variables = {}) {
2773
+ return new ConditionUnresolved(flowId, expression, nodeId, variables);
2774
+ }
2775
+ function asUnresolved(e) {
2776
+ if (e instanceof ConditionUnresolved) return e;
2777
+ if (typeof e === "object" && e !== null && e.unresolved === true && typeof e.flowId === "string") {
2778
+ return e;
2779
+ }
2780
+ return void 0;
2781
+ }
2782
+ function evaluateScript(source, variables) {
2783
+ assertNotJuel(source, "script");
2784
+ const src = source.trim();
2785
+ const result = evaluate(src, variables);
2786
+ return { value: result.value, warnings: [...result.warnings] };
2787
+ }
2788
+
2789
+ // src/runtime/engine.ts
2790
+ var MAX_RESOLVE_ROUNDS = 256;
2791
+ function assertDeliverInput(input) {
2792
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
2793
+ throw optionInvalid("input", "must be a plain object", input);
2794
+ }
2795
+ const i = input;
2796
+ const fail = (field, reason2) => {
2797
+ throw optionInvalid(`input.${field}`, reason2, i[field]);
2798
+ };
2799
+ if (typeof i.name !== "string" || i.name.length === 0) fail("name", "must be a non-empty string");
2800
+ if (typeof i.actor !== "string" || i.actor.length === 0) fail("actor", "must be a non-empty string");
2801
+ if (i.at !== void 0 && (typeof i.at !== "string" || i.at.length === 0)) {
2802
+ fail("at", "must be a non-empty ISO 8601 string when present");
2803
+ }
2804
+ if (i.payload !== void 0 && (typeof i.payload !== "object" || i.payload === null || Array.isArray(i.payload))) {
2805
+ fail("payload", "must be a plain object when present");
2806
+ }
2807
+ }
2808
+ var ENGINE_CONFIG_KEYS = [
2809
+ "store",
2810
+ "definitionSource",
2811
+ "projection",
2812
+ "approverSource",
2813
+ "handlers",
2814
+ "authResolver",
2815
+ "formProvider",
2816
+ "conditionHandler",
2817
+ "decisionHandler",
2818
+ "events",
2819
+ "scheduler",
2820
+ "hooks",
2821
+ "clock",
2822
+ "maxAuditEntries"
2823
+ ];
2824
+ function assertEngineConfig(config) {
2825
+ if (typeof config !== "object" || config === null || Array.isArray(config)) {
2826
+ throw optionInvalid("config", "must be a plain object", config);
2827
+ }
2828
+ const c = config;
2829
+ for (const key of Object.keys(c)) {
2830
+ if (!ENGINE_CONFIG_KEYS.includes(key)) {
2831
+ throw optionUnknown(key, ENGINE_CONFIG_KEYS);
2832
+ }
2833
+ }
2834
+ const needFn = (key, methods) => {
2835
+ const v = c[key];
2836
+ if (v === void 0) return;
2837
+ if (typeof v !== "object" || v === null) {
2838
+ throw optionInvalid(key, "must be an object", v);
2839
+ }
2840
+ const o = v;
2841
+ for (const m of methods) {
2842
+ if (typeof o[m] !== "function") {
2843
+ throw optionInvalid(`${key}.${m}`, "must be a function", o[m]);
2844
+ }
2845
+ }
2846
+ };
2847
+ needFn("store", ["load", "save"]);
2848
+ needFn("definitionSource", ["getDefinition"]);
2849
+ needFn("projection", ["apply", "sync"]);
2850
+ needFn("approverSource", ["resolve"]);
2851
+ needFn("conditionHandler", ["evaluate"]);
2852
+ needFn("decisionHandler", ["evaluate"]);
2853
+ if (c.definitionSource === void 0) {
2854
+ throw optionInvalid("definitionSource", "is required (AC-E10: definitions are fetched by processId + version)", void 0);
2855
+ }
2856
+ if (c.clock !== void 0 && typeof c.clock !== "function") {
2857
+ throw optionInvalid("clock", "must be a function returning an ISO 8601 string", c.clock);
2858
+ }
2859
+ if (c.maxAuditEntries !== void 0) {
2860
+ const n = c.maxAuditEntries;
2861
+ if (typeof n !== "number" || !Number.isInteger(n) || n < 1) {
2862
+ throw optionInvalid("maxAuditEntries", "must be a positive integer", n);
2863
+ }
2864
+ }
2865
+ }
2866
+ function assertStartOptions(opts) {
2867
+ if (typeof opts !== "object" || opts === null || Array.isArray(opts)) {
2868
+ throw optionInvalid("opts", "must be a plain object", opts);
2869
+ }
2870
+ const o = opts;
2871
+ const fail = (field, reason2) => {
2872
+ throw optionInvalid(`opts.${field}`, reason2, o[field]);
2873
+ };
2874
+ if (!Number.isInteger(o.definitionVersion) || o.definitionVersion < 1) {
2875
+ fail("definitionVersion", "must be a positive integer");
2876
+ }
2877
+ if (typeof o.starter !== "string" || o.starter.length === 0) {
2878
+ fail("starter", "must be a non-empty string");
2879
+ }
2880
+ if (o.businessKey !== void 0 && typeof o.businessKey !== "string") {
2881
+ fail("businessKey", "must be a string when present");
2882
+ }
2883
+ if (o.tenantId !== void 0 && typeof o.tenantId !== "string") {
2884
+ fail("tenantId", "must be a string when present");
2885
+ }
2886
+ if (o.variables !== void 0 && (typeof o.variables !== "object" || o.variables === null || Array.isArray(o.variables))) {
2887
+ fail("variables", "must be a plain object when present");
2888
+ }
2889
+ }
2890
+ var defaultClock = () => (/* @__PURE__ */ new Date()).toISOString();
2891
+ var DEFAULT_APPROVER_SOURCE = {
2892
+ async resolve(spec) {
2893
+ if (spec !== null && typeof spec === "object" && spec.type === "user" && typeof spec.value === "string") {
2894
+ return [spec.value];
2895
+ }
2896
+ const type = spec?.type;
2897
+ throw optionInvalid(
2898
+ "approverSource",
2899
+ `cannot resolve approver spec type '${String(type)}' without an injected ApproverSource`,
2900
+ spec
2901
+ );
2902
+ }
2903
+ };
2904
+ var instanceSeq = 0;
2905
+ function newInstanceId() {
2906
+ instanceSeq += 1;
2907
+ const rand = Math.random().toString(36).slice(2, 10);
2908
+ return `pi_${Date.now().toString(36)}_${instanceSeq.toString(36)}_${rand}`;
2909
+ }
2910
+ function createEngine(config) {
2911
+ assertEngineConfig(config);
2912
+ const clock = config.clock ?? defaultClock;
2913
+ const store = config.store ?? createMemoryStore();
2914
+ const { definitionSource } = config;
2915
+ const projection = config.projection;
2916
+ const scheduler = config.scheduler;
2917
+ const approverSource = config.approverSource ?? DEFAULT_APPROVER_SOURCE;
2918
+ const condition = config.conditionHandler ?? createFeelConditionHandler();
2919
+ const handlers = config.handlers;
2920
+ const decisionHandler = config.decisionHandler;
2921
+ const maxAuditEntries = config.maxAuditEntries;
2922
+ const hooks = config.hooks;
2923
+ const sink = config.events;
2924
+ const queue = createInstanceQueue();
2925
+ function now() {
2926
+ const t = clock();
2927
+ if (typeof t !== "string" || t.length === 0) {
2928
+ throw optionInvalid("clock", "must return a non-empty ISO 8601 string", t);
2929
+ }
2930
+ return t;
2931
+ }
2932
+ async function graphOf(state) {
2933
+ const def = await definitionSource.getDefinition(state.processId, state.definitionVersion);
2934
+ if (def === null || def === void 0) {
2935
+ throw definitionMissing(state.processId, state.definitionVersion);
2936
+ }
2937
+ return createProcessGraph(def, state.processId, state.definitionVersion);
2938
+ }
2939
+ async function buildApply(params) {
2940
+ const { state, graph, at, record, run } = params;
2941
+ const conditions = /* @__PURE__ */ new Map();
2942
+ const conditionsOf = (flow, nodeId, variables) => {
2943
+ if (flow.expression === void 0) return true;
2944
+ const value = conditions.get(flow.id);
2945
+ if (value === void 0) {
2946
+ throw unresolvedCondition(flow.id, flow.expression, nodeId, variables);
2947
+ }
2948
+ return value;
2949
+ };
2950
+ const effects = /* @__PURE__ */ new Map();
2951
+ const effectsOf = (nodeId, tokenId, variables) => {
2952
+ const effect = effects.get(effectKeyOf(nodeId, tokenId));
2953
+ if (effect === void 0) {
2954
+ const kind = effectKindOf(graph.typeOf(nodeId));
2955
+ if (kind === void 0) {
2956
+ throw stateShapeInvalid(`node '${nodeId}' is not an effect task`, {
2957
+ nodeId,
2958
+ type: graph.typeOf(nodeId) ?? null
2959
+ });
2960
+ }
2961
+ throw unresolvedEffect({ nodeId, tokenId, kind, variables });
2962
+ }
2963
+ return effect;
2964
+ };
2965
+ const pendingEvents = [];
2966
+ const pendingCalls = [];
2967
+ for (let round = 0; ; round += 1) {
2968
+ try {
2969
+ const probe = /* @__PURE__ */ new Set();
2970
+ run(cloneState(state), {
2971
+ graph,
2972
+ at,
2973
+ assigneesOf: (nodeId) => {
2974
+ probe.add(nodeId);
2975
+ return [PROBE_ASSIGNEE];
2976
+ },
2977
+ conditionsOf,
2978
+ effectsOf
2979
+ });
2980
+ const resolved = /* @__PURE__ */ new Map();
2981
+ for (const nodeId of probe) {
2982
+ resolved.set(nodeId, await resolveAssigneesFor(nodeId, state, graph, approverSource));
2983
+ }
2984
+ return {
2985
+ pendingEvents,
2986
+ pendingCalls,
2987
+ apply: (draft) => {
2988
+ const r = run(
2989
+ draft,
2990
+ { graph, at, assigneesOf: (nodeId) => resolved.get(nodeId) ?? [], conditionsOf, effectsOf }
2991
+ );
2992
+ pendingEvents.length = 0;
2993
+ for (const e of r.events) pendingEvents.push(e);
2994
+ pendingCalls.length = 0;
2995
+ for (const c of r.pendingCalls) pendingCalls.push(c);
2996
+ return r.next;
2997
+ }
2998
+ };
2999
+ } catch (e) {
3000
+ const pendingEffect = asUnresolvedEffect(e);
3001
+ if (pendingEffect !== void 0) {
3002
+ if (round >= MAX_RESOLVE_ROUNDS) {
3003
+ throw stateShapeInvalid("effect resolution did not converge (retry budget exceeded)", {
3004
+ budget: MAX_RESOLVE_ROUNDS,
3005
+ instanceId: state.instanceId
3006
+ });
3007
+ }
3008
+ effects.set(
3009
+ pendingEffect.key,
3010
+ await resolveEffect({
3011
+ nodeId: pendingEffect.nodeId,
3012
+ tokenId: pendingEffect.tokenId,
3013
+ kind: pendingEffect.kind,
3014
+ variables: pendingEffect.variables,
3015
+ graph,
3016
+ state,
3017
+ record,
3018
+ handlers,
3019
+ decisionHandler
3020
+ })
3021
+ );
3022
+ continue;
3023
+ }
3024
+ const pending = asUnresolved(e);
3025
+ if (pending === void 0) throw e;
3026
+ if (round >= MAX_RESOLVE_ROUNDS) {
3027
+ throw stateShapeInvalid("condition resolution did not converge (retry budget exceeded)", {
3028
+ budget: MAX_RESOLVE_ROUNDS,
3029
+ instanceId: state.instanceId
3030
+ });
3031
+ }
3032
+ conditions.set(
3033
+ pending.flowId,
3034
+ await evaluateCondition(condition, pending.expression, {
3035
+ instanceId: state.instanceId,
3036
+ nodeId: pending.nodeId,
3037
+ // ★ 用**到达该网关那一刻**的变量快照(哨兵带来),不是提交前的旧值
3038
+ variables: pending.variables
3039
+ })
3040
+ );
3041
+ }
3042
+ }
3043
+ }
3044
+ async function start(processId, opts) {
3045
+ assertStartOptions(opts);
3046
+ const at = now();
3047
+ const graph = await graphOf({ processId, definitionVersion: opts.definitionVersion });
3048
+ const instanceId = newInstanceId();
3049
+ const base = {
3050
+ instanceId,
3051
+ processId,
3052
+ definitionVersion: opts.definitionVersion,
3053
+ status: "running",
3054
+ rev: 0,
3055
+ // ★ INSERT 信号(store 据此分两条路径)
3056
+ stateSchema: STATE_SCHEMA_VERSION,
3057
+ startedAt: at,
3058
+ updatedAt: at,
3059
+ tokens: [{ id: "tk_start", nodeId: graph.startNodeId, state: "active" }],
3060
+ completedNodes: [],
3061
+ variables: { ...opts.variables ?? {} },
3062
+ auditTrail: [],
3063
+ starter: opts.starter
3064
+ };
3065
+ if (opts.businessKey !== void 0) base.businessKey = opts.businessKey;
3066
+ if (opts.tenantId !== void 0) base.tenantId = opts.tenantId;
3067
+ const record = { name: "start", actor: opts.starter, at };
3068
+ const built = await buildApply({
3069
+ state: base,
3070
+ graph,
3071
+ at,
3072
+ record,
3073
+ run: (s, ctx) => step(s, ctx, { calls: [] })
3074
+ });
3075
+ const looped = built.apply(cloneState(base));
3076
+ const startTokenId = "tk_start";
3077
+ const startTo = looped.tokens.find((t) => t.id === startTokenId)?.nodeId;
3078
+ const startEntry = {
3079
+ seq: 1,
3080
+ at,
3081
+ actor: opts.starter,
3082
+ action: "start",
3083
+ nodeId: graph.startNodeId,
3084
+ tokenId: startTokenId,
3085
+ from: graph.startNodeId,
3086
+ ...startTo !== void 0 ? { to: startTo } : {}
3087
+ };
3088
+ const next = {
3089
+ ...looped,
3090
+ rev: 1,
3091
+ lastAction: record,
3092
+ auditTrail: [startEntry]
3093
+ };
3094
+ const delta = {
3095
+ rev: next.rev,
3096
+ action: record,
3097
+ added: tasksOf(next, graph),
3098
+ removed: [],
3099
+ changed: [],
3100
+ instance: headerOf(next)
3101
+ };
3102
+ await reconcileTimers(base, next, graph);
3103
+ await store.save(next, 0);
3104
+ if (projection !== void 0) await projection.apply(instanceId, delta);
3105
+ emitAll(sink, [...eventsOf({ before: [], delta, next }), ...built.pendingEvents]);
3106
+ await followUp(next, { next, pendingCalls: built.pendingCalls });
3107
+ return instanceId;
3108
+ }
3109
+ async function submit(instanceId, action) {
3110
+ const outcome = await queue.run(instanceId, () => doSubmit(instanceId, action));
3111
+ await followUp(outcome.next, outcome);
3112
+ return outcome.delta;
3113
+ }
3114
+ async function doSubmit(instanceId, action) {
3115
+ const state = await store.load(instanceId);
3116
+ if (state === null || state === void 0) throw stateNotFound(instanceId);
3117
+ if (isTerminalStatus(state.status)) {
3118
+ throw stateTerminal(state.instanceId, state.status, action.action);
3119
+ }
3120
+ const at = action.at ?? now();
3121
+ const input = action.at === void 0 ? { ...action, at } : action;
3122
+ const graph = await graphOf(state);
3123
+ assertTokensInGraph(state, graph);
3124
+ const tokenId = resolveSubjectToken(state, input);
3125
+ const subject = tokenId === void 0 ? void 0 : state.tokens.find((t) => t.id === tokenId);
3126
+ const nodeId = subject?.nodeId;
3127
+ const approval = nodeId === void 0 ? void 0 : graph.approvalOf(nodeId);
3128
+ const assignees = await resolveActionAssignees(input, nodeId, state, graph, approverSource);
3129
+ const compiled = compileAction(input, state, {
3130
+ approval,
3131
+ nextOf: graph.nextOf,
3132
+ startNodeId: graph.startNodeId,
3133
+ ...tokenId !== void 0 ? { tokenId } : {},
3134
+ ...assignees !== void 0 ? { assignees } : {},
3135
+ ...payloadString(input.payload, "groupId") !== void 0 ? { groupId: payloadString(input.payload, "groupId") } : {},
3136
+ ...payloadStringArray(input.payload, "reduceTokenIds") !== void 0 ? { reduceTokenIds: payloadStringArray(input.payload, "reduceTokenIds") } : {}
3137
+ });
3138
+ const before = tasksOf(state, graph);
3139
+ const record = { name: input.action, actor: input.actor, at };
3140
+ const stepInput = {
3141
+ calls: compiled.calls,
3142
+ ...compiled.vote !== void 0 ? { vote: compiled.vote } : {},
3143
+ ...compiled.post !== void 0 ? { post: compiled.post } : {},
3144
+ ...input.target !== void 0 ? { rejectTarget: input.target } : {}
3145
+ };
3146
+ const built = await buildApply({
3147
+ /*
3148
+ * ★ 传给探测/闭包的是**已并入 payload 增量**的状态。
3149
+ * `plan()` 在 ④.5 先并变量、④.6 才调 `apply` —— 探测必须用同一份,
3150
+ * 否则「表单里把 amount 改成 9000、网关却按旧值走分支」(§7.2 要防的头号事故)。
3151
+ */
3152
+ state: withPayload(state, input.payload),
3153
+ graph,
3154
+ at,
3155
+ record,
3156
+ run: (s, ctx2) => step(s, ctx2, stepInput)
3157
+ });
3158
+ const result = plan(state, input, {
3159
+ clock,
3160
+ ...maxAuditEntries !== void 0 ? { maxAuditEntries } : {},
3161
+ apply: built.apply,
3162
+ tasks: (s) => tasksOf(s, graph)
3163
+ });
3164
+ await reconcileTimers(state, result.next, graph);
3165
+ const ctx = freezeActionContext({
3166
+ action: result.delta.action,
3167
+ state: headerOf(state),
3168
+ next: headerOf(result.next),
3169
+ delta: result.delta
3170
+ });
3171
+ if (hooks?.beforeAction !== void 0) {
3172
+ const verdict = await hooks.beforeAction(ctx);
3173
+ if (verdict === false) throw actionVetoed(input.action, instanceId);
3174
+ }
3175
+ await store.save(result.next, state.rev);
3176
+ if (projection !== void 0) await projection.apply(instanceId, result.delta);
3177
+ if (hooks?.afterAction !== void 0) await hooks.afterAction(ctx);
3178
+ emitAll(
3179
+ sink,
3180
+ [
3181
+ ...eventsOf({ before, delta: result.delta, next: result.next, previousStatus: state.status }),
3182
+ // ★ 节点副作用产出的事件(`manualTask` 的留痕)—— 状态已落库,此刻投递才安全
3183
+ ...built.pendingEvents
3184
+ ]
3185
+ );
3186
+ return { next: result.next, pendingCalls: built.pendingCalls, delta: result.delta };
3187
+ }
3188
+ async function deliverMessage(instanceId, input) {
3189
+ const outcome = await queue.run(instanceId, () => doDeliver(instanceId, "message", input, "throw"));
3190
+ await followUp(outcome.next, outcome);
3191
+ return outcome.delta;
3192
+ }
3193
+ async function deliverSignal(instanceIds, input) {
3194
+ assertDeliverInput(input);
3195
+ if (!Array.isArray(instanceIds) || instanceIds.length === 0) {
3196
+ throw optionInvalid("instanceIds", "must be a non-empty array", instanceIds);
3197
+ }
3198
+ const deltas = [];
3199
+ const waiting = [];
3200
+ for (const id of instanceIds) {
3201
+ const outcome = await queue.run(id, () => doDeliver(id, "signal", input, "skip", waiting));
3202
+ if (outcome === null) continue;
3203
+ await followUp(outcome.next, outcome);
3204
+ deltas.push(outcome.delta);
3205
+ }
3206
+ if (deltas.length === 0) {
3207
+ throw deliverNoTarget(void 0, "signal", input.name, waiting, {
3208
+ candidates: [...instanceIds]
3209
+ });
3210
+ }
3211
+ return deltas;
3212
+ }
3213
+ async function doDeliver(instanceId, kind, input, onMiss, waitingSink) {
3214
+ assertDeliverInput(input);
3215
+ const state = await store.load(instanceId);
3216
+ if (state === null || state === void 0) throw stateNotFound(instanceId);
3217
+ const actionName = kind === "message" ? MESSAGE_DELIVER_ACTION : SIGNAL_DELIVER_ACTION;
3218
+ if (isTerminalStatus(state.status) || state.status === "suspended") {
3219
+ if (onMiss === "skip") return null;
3220
+ if (isTerminalStatus(state.status)) {
3221
+ throw stateTerminal(state.instanceId, state.status, actionName);
3222
+ }
3223
+ throw stateSuspended(state.instanceId, actionName);
3224
+ }
3225
+ const graph = await graphOf(state);
3226
+ assertTokensInGraph(state, graph);
3227
+ const match = { kind, name: input.name };
3228
+ const mode = onMiss === "throw" ? "point" : "broadcast";
3229
+ const waiters = matchingTokens(state, match);
3230
+ const armed = armedBoundaries(state, graph, match);
3231
+ const hitCount = mode === "point" ? waiters.length > 0 || armed.length > 0 ? 1 : 0 : waiters.length + armed.length;
3232
+ if (hitCount === 0) {
3233
+ const names = [...waitingNamesOf(state), ...armedNamesOf(state, graph)];
3234
+ if (waitingSink !== void 0) {
3235
+ for (const n of names) if (!waitingSink.includes(n)) waitingSink.push(n);
3236
+ }
3237
+ if (onMiss === "skip") return null;
3238
+ throw deliverNoTarget(instanceId, kind, input.name, names);
3239
+ }
3240
+ const at = input.at ?? now();
3241
+ const before = tasksOf(state, graph);
3242
+ const record = { name: actionName, actor: input.actor, at };
3243
+ const ai = {
3244
+ action: actionName,
3245
+ actor: input.actor,
3246
+ at,
3247
+ // 审计要记「唤醒了哪儿」:多个命中时取第一个(保序,可重放)
3248
+ target: waiters[0]?.nodeId ?? armed[0]?.boundary.nodeId,
3249
+ ...input.payload !== void 0 ? { payload: input.payload } : {}
3250
+ };
3251
+ const built = await buildApply({
3252
+ state: withPayload(state, input.payload),
3253
+ graph,
3254
+ at,
3255
+ record,
3256
+ run: (s, ctx) => deliverStep(s, ctx, match, mode)
3257
+ });
3258
+ const result = plan(state, ai, {
3259
+ clock,
3260
+ ...maxAuditEntries !== void 0 ? { maxAuditEntries } : {},
3261
+ apply: built.apply,
3262
+ tasks: (s) => tasksOf(s, graph)
3263
+ });
3264
+ await reconcileTimers(state, result.next, graph);
3265
+ const hookCtx = freezeActionContext({
3266
+ action: result.delta.action,
3267
+ state: headerOf(state),
3268
+ next: headerOf(result.next),
3269
+ delta: result.delta
3270
+ });
3271
+ if (hooks?.beforeAction !== void 0) {
3272
+ const verdict = await hooks.beforeAction(hookCtx);
3273
+ if (verdict === false) throw actionVetoed(actionName, instanceId);
3274
+ }
3275
+ await store.save(result.next, state.rev);
3276
+ if (projection !== void 0) await projection.apply(instanceId, result.delta);
3277
+ if (hooks?.afterAction !== void 0) await hooks.afterAction(hookCtx);
3278
+ emitAll(
3279
+ sink,
3280
+ [
3281
+ ...eventsOf({ before, delta: result.delta, next: result.next, previousStatus: state.status }),
3282
+ ...built.pendingEvents
3283
+ ]
3284
+ );
3285
+ return { next: result.next, pendingCalls: built.pendingCalls, delta: result.delta };
3286
+ }
3287
+ async function reconcileTimers(prev, next, graph) {
3288
+ if (scheduler === void 0) return;
3289
+ const diff = diffTimers(prev, next, graph);
3290
+ const gained = /* @__PURE__ */ new Map();
3291
+ for (const p of diff.schedule) {
3292
+ const handles = [];
3293
+ for (const kind of p.kinds) {
3294
+ handles.push(
3295
+ await scheduler.schedule({
3296
+ instanceId: next.instanceId,
3297
+ nodeId: p.nodeId,
3298
+ tokenId: p.tokenId,
3299
+ fromAt: p.fromAt,
3300
+ timeout: p.timeout,
3301
+ kind
3302
+ })
3303
+ );
3304
+ }
3305
+ gained.set(p.tokenId, handles);
3306
+ }
3307
+ for (const c of diff.cancel) {
3308
+ for (const h of c.handles) await scheduler.cancel(h);
3309
+ const t = next.tokens.find((x) => x.id === c.tokenId);
3310
+ if (t !== void 0) delete t.timerHandles;
3311
+ }
3312
+ for (const [tokenId, handles] of gained) {
3313
+ const t = next.tokens.find((x) => x.id === tokenId);
3314
+ if (t === void 0) continue;
3315
+ t.timerHandles = [...t.timerHandles ?? [], ...handles];
3316
+ }
3317
+ }
3318
+ async function followUp(parent, outcome) {
3319
+ for (const call of outcome.pendingCalls) {
3320
+ await startChild({
3321
+ instanceId: call.instanceId,
3322
+ processId: call.processId,
3323
+ definitionVersion: call.definitionVersion,
3324
+ variables: { ...call.variables },
3325
+ starter: parent.starter ?? parent.instanceId,
3326
+ ...parent.tenantId !== void 0 ? { tenantId: parent.tenantId } : {},
3327
+ parent: { instanceId: parent.instanceId, nodeId: call.nodeId, tokenId: call.tokenId }
3328
+ });
3329
+ }
3330
+ if (isTerminalStatus(outcome.next.status)) {
3331
+ if (outcome.next.parent !== void 0) await resumeParent(outcome.next);
3332
+ await haltLiveChildren(outcome.next);
3333
+ }
3334
+ }
3335
+ async function startChild(spec) {
3336
+ const outcome = await queue.run(spec.instanceId, () => doStartChild(spec));
3337
+ await followUp(outcome.next, outcome);
3338
+ }
3339
+ async function doStartChild(spec) {
3340
+ const at = now();
3341
+ const graph = await graphOf({
3342
+ processId: spec.processId,
3343
+ definitionVersion: spec.definitionVersion
3344
+ });
3345
+ const base = {
3346
+ instanceId: spec.instanceId,
3347
+ processId: spec.processId,
3348
+ definitionVersion: spec.definitionVersion,
3349
+ status: "running",
3350
+ rev: 0,
3351
+ // ★ INSERT 信号
3352
+ stateSchema: STATE_SCHEMA_VERSION,
3353
+ startedAt: at,
3354
+ updatedAt: at,
3355
+ tokens: [{ id: "tk_start", nodeId: graph.startNodeId, state: "active" }],
3356
+ completedNodes: [],
3357
+ variables: { ...spec.variables },
3358
+ auditTrail: [],
3359
+ starter: spec.starter,
3360
+ parent: spec.parent
3361
+ };
3362
+ if (spec.tenantId !== void 0) base.tenantId = spec.tenantId;
3363
+ const record = { name: "start", actor: spec.starter, at };
3364
+ const built = await buildApply({
3365
+ state: base,
3366
+ graph,
3367
+ at,
3368
+ record,
3369
+ run: (s, ctx) => step(s, ctx, { calls: [] })
3370
+ });
3371
+ const looped = built.apply(cloneState(base));
3372
+ const next = {
3373
+ ...looped,
3374
+ rev: 1,
3375
+ lastAction: record,
3376
+ auditTrail: [{ seq: 1, at, actor: spec.starter, action: "start", nodeId: graph.startNodeId }]
3377
+ };
3378
+ const delta = {
3379
+ rev: next.rev,
3380
+ action: record,
3381
+ added: tasksOf(next, graph),
3382
+ removed: [],
3383
+ changed: [],
3384
+ instance: headerOf(next)
3385
+ };
3386
+ await reconcileTimers(base, next, graph);
3387
+ await store.save(next, 0);
3388
+ if (projection !== void 0) await projection.apply(spec.instanceId, delta);
3389
+ emitAll(sink, [...eventsOf({ before: [], delta, next }), ...built.pendingEvents]);
3390
+ return { next, pendingCalls: built.pendingCalls };
3391
+ }
3392
+ async function resumeParent(child) {
3393
+ const p = child.parent;
3394
+ if (p === void 0) return;
3395
+ const outcome = await queue.run(p.instanceId, () => doResumeParent(child, p));
3396
+ if (outcome !== void 0) await followUp(outcome.next, outcome);
3397
+ }
3398
+ async function doResumeParent(child, p) {
3399
+ const parent = await store.load(p.instanceId);
3400
+ if (parent === null || parent === void 0) throw stateNotFound(p.instanceId);
3401
+ if (isTerminalStatus(parent.status)) return void 0;
3402
+ const token = parent.tokens.find((t) => t.id === p.tokenId);
3403
+ if (token === void 0 || token.state !== "waiting") return void 0;
3404
+ const graph = await graphOf(parent);
3405
+ const to = graph.nextOf(p.nodeId);
3406
+ if (to === void 0) throw definitionMissing(parent.processId, parent.definitionVersion);
3407
+ const at = now();
3408
+ const actor = child.lastAction?.actor ?? child.starter ?? child.instanceId;
3409
+ const record = { name: CALL_RETURN_ACTION, actor, at, nodeId: p.nodeId };
3410
+ const resumed = callReturnOf(parent, p, to);
3411
+ const built = await buildApply({
3412
+ state: resumed,
3413
+ graph,
3414
+ at,
3415
+ record,
3416
+ run: (s, ctx) => step(s, ctx, { calls: [] })
3417
+ });
3418
+ const before = tasksOf(parent, graph);
3419
+ const result = plan(parent, { action: CALL_RETURN_ACTION, actor, at }, {
3420
+ clock,
3421
+ ...maxAuditEntries !== void 0 ? { maxAuditEntries } : {},
3422
+ apply: (draft) => built.apply(callReturnOf(draft, p, to)),
3423
+ tasks: (s) => tasksOf(s, graph)
3424
+ });
3425
+ await reconcileTimers(parent, result.next, graph);
3426
+ await store.save(result.next, parent.rev);
3427
+ if (projection !== void 0) await projection.apply(p.instanceId, result.delta);
3428
+ emitAll(sink, [
3429
+ ...eventsOf({ before, delta: result.delta, next: result.next, previousStatus: parent.status }),
3430
+ ...built.pendingEvents
3431
+ ]);
3432
+ return { next: result.next, pendingCalls: built.pendingCalls };
3433
+ }
3434
+ async function haltLiveChildren(parent) {
3435
+ for (const childId of parent.childInstanceIds ?? []) await haltChild(childId);
3436
+ }
3437
+ async function haltChild(childId) {
3438
+ await queue.run(childId, async () => {
3439
+ const child = await store.load(childId);
3440
+ if (child === null || child === void 0) return;
3441
+ if (isTerminalStatus(child.status)) return;
3442
+ const at = now();
3443
+ const record = {
3444
+ name: "terminate",
3445
+ actor: child.lastAction?.actor ?? child.starter ?? childId,
3446
+ at
3447
+ };
3448
+ const next = {
3449
+ ...cloneState(child),
3450
+ status: "terminated",
3451
+ rev: child.rev + 1,
3452
+ updatedAt: at,
3453
+ endedAt: at,
3454
+ lastAction: record,
3455
+ tokens: child.tokens.map(
3456
+ (t) => LIVE_TOKEN_STATES.includes(t.state) ? { ...t, state: "cancelled" } : t
3457
+ ),
3458
+ auditTrail: appendAudit(child.auditTrail, { at, actor: record.actor, action: record.name })
3459
+ };
3460
+ if (maxAuditEntries !== void 0 && next.auditTrail.length > maxAuditEntries) {
3461
+ next.auditTrail = next.auditTrail.slice(next.auditTrail.length - maxAuditEntries);
3462
+ }
3463
+ const graph = await graphOf(child);
3464
+ const before = tasksOf(child, graph);
3465
+ const delta = {
3466
+ rev: next.rev,
3467
+ action: record,
3468
+ added: [],
3469
+ removed: before.map((t) => t.taskId),
3470
+ changed: [],
3471
+ instance: headerOf(next)
3472
+ };
3473
+ await store.save(next, child.rev);
3474
+ if (projection !== void 0) await projection.apply(childId, delta);
3475
+ emitAll(sink, eventsOf({ before, delta, next, previousStatus: child.status }));
3476
+ await haltLiveChildren(next);
3477
+ });
3478
+ }
3479
+ async function exportTrace(instanceId) {
3480
+ const state = await store.load(instanceId);
3481
+ if (state === null || state === void 0) throw stateNotFound(instanceId);
3482
+ return traceOf(state);
3483
+ }
3484
+ return {
3485
+ start,
3486
+ submit,
3487
+ deliverMessage,
3488
+ deliverSignal,
3489
+ exportTrace,
3490
+ plan: (state, action, options) => plan(state, action, {
3491
+ clock,
3492
+ ...maxAuditEntries !== void 0 ? { maxAuditEntries } : {},
3493
+ ...options
3494
+ })
3495
+ };
3496
+ }
3497
+ async function resolveEffect(params) {
3498
+ const { nodeId, tokenId, kind, variables, graph, state, record } = params;
3499
+ const { handlers, decisionHandler } = params;
3500
+ if (kind === "manual") return { nodeId, events: manualTaskEvents({ nodeId, tokenId, graph, state, record }) };
3501
+ if (kind === "service" || kind === "script" && !isFeelScriptFormat(graph.scriptFormatOf(nodeId))) {
3502
+ const ref = graph.handlerRefOf(nodeId);
3503
+ const fn = handlers?.get(ref);
3504
+ if (fn === void 0) {
3505
+ const why = kind === "script" ? `scriptTask '${nodeId}' \u7684 scriptFormat '${String(graph.scriptFormatOf(nodeId))}' \u4E0D\u662F FEEL` : `serviceTask '${nodeId}' \u6CA1\u6709\u6CE8\u518C\u5904\u7406\u5668`;
3506
+ throw optionInvalid("handlers", `${why} \u2014\u2014 \u8BF7\u5728 handlers \u8868\u6CE8\u518C ref '${ref}'`, {
3507
+ nodeId,
3508
+ ref,
3509
+ kind,
3510
+ /*
3511
+ * ⚠️ 提示文案**刻意不逐字写**那三个禁用 API 名(`test/tasks.test.ts` 有一道
3512
+ * 「源码扫描」门禁,`src/**` 里出现它们就红,而字符串字面量不在注释剥离范围内)。
3513
+ * 语义照样讲清楚:本引擎没有任何"动态执行宿主代码"的能力,一律由宿主提供实现。
3514
+ */
3515
+ hint: "\u5F15\u64CE\u4E0D\u6267\u884C\u4EFB\u610F JS\uFF08\u65E0\u52A8\u6001\u6C42\u503C\u80FD\u529B\uFF0C\u4E5F\u4E0D\u52A0\u8F7D vm \u7C7B\u6A21\u5757\uFF09\uFF1B\u975E FEEL \u7684\u811A\u672C\u4E0E\u5176\u5B83\u5B9E\u73B0\u4E00\u5F8B\u7531\u5BBF\u4E3B\u63D0\u4F9B"
3516
+ });
3517
+ }
3518
+ const out2 = await fn(variables, {
3519
+ instanceId: state.instanceId,
3520
+ processId: state.processId,
3521
+ definitionVersion: state.definitionVersion,
3522
+ nodeId
3523
+ });
3524
+ return { nodeId, variables: assertVariablePatch(nodeId, kind, out2) };
3525
+ }
3526
+ if (kind === "script") {
3527
+ const source = graph.scriptOf(nodeId);
3528
+ if (source === void 0) {
3529
+ throw stateShapeInvalid(`scriptTask '${nodeId}' has no <script> content`, {
3530
+ nodeId,
3531
+ hint: "\u7ED9\u8BE5\u8282\u70B9\u5199 <bpmn:script>\uFF0C\u6216\u6539\u4E3A\u975E FEEL \u7684 scriptFormat \u5E76\u5728 handlers \u8868\u6CE8\u518C\u5904\u7406\u5668"
3532
+ });
3533
+ }
3534
+ const r = evaluateScript(source, variables);
3535
+ return { nodeId, variables: { [nodeId]: r.value } };
3536
+ }
3537
+ if (decisionHandler === void 0) {
3538
+ throw optionInvalid(
3539
+ "decisionHandler",
3540
+ `businessRuleTask '${nodeId}' \u9700\u8981 decisionHandler \u2014\u2014 \u672A\u6CE8\u5165\u5373\u62A5\u300C\u672A\u914D\u7F6E\u300D`,
3541
+ { nodeId, hint: "\u53EF\u63A5 @floken-io/dmn\uFF0C\u4E5F\u53EF\u4EE5\u76F4\u63A5\u4F20\u4E00\u4E2A (input, ctx) => output \u7684\u51FD\u6570" }
3542
+ );
3543
+ }
3544
+ const out = await decisionHandler.evaluate({ ...variables }, {
3545
+ instanceId: state.instanceId,
3546
+ nodeId,
3547
+ input: { ...variables }
3548
+ });
3549
+ return { nodeId, variables: assertVariablePatch(nodeId, kind, out) };
3550
+ }
3551
+ function manualTaskEvents(params) {
3552
+ const { nodeId, tokenId, graph, state, record } = params;
3553
+ const base = {
3554
+ at: record.at,
3555
+ instanceId: state.instanceId,
3556
+ processId: state.processId,
3557
+ definitionVersion: state.definitionVersion,
3558
+ action: record
3559
+ };
3560
+ const name = graph.nameOf(nodeId);
3561
+ const formKey = graph.formKeyOf(nodeId);
3562
+ const make = (eventName, taskStatus) => {
3563
+ const e = { ...base, name: eventName, taskId: `${nodeId}:${tokenId}`, nodeId, taskStatus };
3564
+ if (name !== void 0) e.nodeName = name;
3565
+ if (formKey !== void 0) e.formKey = formKey;
3566
+ return e;
3567
+ };
3568
+ return [make("taskCreated", "active"), make("taskCompleted", "done")];
3569
+ }
3570
+ function appendAudit(trail, entry) {
3571
+ let max = 0;
3572
+ for (const e of trail) {
3573
+ if (typeof e.seq === "number" && e.seq > max) max = e.seq;
3574
+ }
3575
+ return [...trail, { ...entry, seq: max + 1 }];
3576
+ }
3577
+ function withPayload(state, payload) {
3578
+ if (payload === void 0) return state;
3579
+ return { ...state, variables: { ...state.variables, ...payload } };
3580
+ }
3581
+ async function resolveAssigneesFor(nodeId, state, graph, approverSource) {
3582
+ const approval = graph.approvalOf(nodeId);
3583
+ if (approval === void 0) {
3584
+ throw approverEmpty(nodeId, "error", {
3585
+ instanceId: state.instanceId,
3586
+ reason: `node '${nodeId}' has no ${"floken:approval"} config`
3587
+ });
3588
+ }
3589
+ const ctx = {
3590
+ instanceId: state.instanceId,
3591
+ processId: state.processId,
3592
+ nodeId,
3593
+ starter: state.starter ?? "",
3594
+ variables: state.variables
3595
+ };
3596
+ const out = [];
3597
+ for (const spec of approval.approvers) {
3598
+ const list = await approverSource.resolve(spec, ctx);
3599
+ for (const id of list ?? []) {
3600
+ if (typeof id === "string" && id.length > 0) out.push(id);
3601
+ }
3602
+ }
3603
+ return [...new Set(out)];
3604
+ }
3605
+ function resolveSubjectToken(state, input) {
3606
+ return subjectTokenOf(state, input.actor)?.id;
3607
+ }
3608
+ async function resolveActionAssignees(input, nodeId, state, graph, approverSource) {
3609
+ if (nodeId === void 0) return void 0;
3610
+ const explicit = explicitAssignees(input.payload);
3611
+ if (HANDOVER_ACTIONS.includes(input.action)) {
3612
+ return explicit;
3613
+ }
3614
+ if (SPAWN_ACTIONS.includes(input.action)) {
3615
+ if (explicit !== void 0) return explicit;
3616
+ return await resolveAssigneesFor(nodeId, state, graph, approverSource);
3617
+ }
3618
+ return void 0;
3619
+ }
3620
+ function explicitAssignees(payload) {
3621
+ if (payload === void 0) return void 0;
3622
+ const one = payload["assignee"];
3623
+ if (typeof one === "string" && one.length > 0) return [one];
3624
+ const many = payload["assignees"];
3625
+ if (Array.isArray(many)) {
3626
+ const list = many.filter((x) => typeof x === "string" && x.length > 0);
3627
+ if (list.length > 0) return list;
3628
+ }
3629
+ return void 0;
3630
+ }
3631
+ function payloadString(payload, key) {
3632
+ const v = payload?.[key];
3633
+ return typeof v === "string" && v.length > 0 ? v : void 0;
3634
+ }
3635
+ function payloadStringArray(payload, key) {
3636
+ const v = payload?.[key];
3637
+ if (!Array.isArray(v)) return void 0;
3638
+ const list = v.filter((x) => typeof x === "string" && x.length > 0);
3639
+ return list.length > 0 ? list : void 0;
3640
+ }
3641
+
3642
+ // src/entries/index.ts
3643
+ var PACKAGE = "@floken-io/engine";
3644
+
3645
+ export { ACTION_NAMES, BOUNDARY_TYPE, CALL_RETURN_ACTION, CATCH_KINDS, DELIVER_ACTIONS, ENGINE_EVENT_NAMES, INSTANCE_EVENT_NAMES, MESSAGE_DELIVER_ACTION, PACKAGE, PRIMITIVE_GROUPS, PRIMITIVE_NAMES, SIGNAL_DELIVER_ACTION, SPI_GROUPS, SPI_NAMES, SYSTEM_AUDIT_ACTIONS, TASK_EVENT_NAMES, TASK_STATUSES, applyPost, armedBoundaries, armedNamesOf, boundaryBindingOf, boundaryTokenIdOf, callReturnOf, cancelTargetsOf, castVote, catchBindingOf, convergeCtxOf, createEngine, createFeelConditionHandler, createInstanceQueue, createMemoryStore, deliverStep, diffTimers, emitAll, enabledActionNames, evaluateCondition, evaluateConvergence, eventsOf, groupTallies, inScopeOf, instanceEventNameOf, isInstanceEvent, isTaskEvent, matchingTokens, plan, promoteSequential, requiredOf, restTokenIds, settleGroups, shouldConverge, shouldTerminate, step, subjectTokenOf, timeoutSpecOf, timerKeyOf, timingKeysOf, traceKindOf, traceOf, waitingNamesOf };