@sublang/playbook 7.0.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -7
- package/docs/cli.md +88 -43
- package/docs/configuration.md +221 -119
- package/docs/embedding.md +78 -27
- package/package.json +4 -3
- package/reference/sdlc/captain.playbook/captain.playbook.js +16 -5
- package/reference/sdlc/captain.playbook/captain.playbook.ts +20 -6
- package/reference/sdlc/code.md +1 -1
- package/reference/sdlc/code.playbook/bin/interactive-session.js +816 -0
- package/reference/sdlc/code.playbook/bin/launch-config.js +1078 -116
- package/reference/sdlc/code.playbook/bin/playbook.js +489 -34
- package/reference/sdlc/code.playbook/bin/run.js +283 -298
- package/reference/sdlc/code.playbook/bin/session-store.js +818 -26
- package/reference/sdlc/code.playbook/code.fsm.d.ts +9 -6
- package/reference/sdlc/code.playbook/code.fsm.introspect.js +2 -2
- package/reference/sdlc/code.playbook/code.fsm.introspect.ts +2 -2
- package/reference/sdlc/code.playbook/code.fsm.js +18 -15
- package/reference/sdlc/code.playbook/code.fsm.ts +21 -21
- package/reference/sdlc/code.playbook/code.gears.md +1 -1
- package/reference/sdlc/code.playbook/code.playbook.d.ts +2 -1
- package/reference/sdlc/code.playbook/code.playbook.js +25 -15
- package/reference/sdlc/code.playbook/code.playbook.ts +34 -17
- package/reference/sdlc/code.playbook/code.registry.d.ts +5 -13
- package/reference/sdlc/code.playbook/code.registry.js +3 -10
- package/reference/sdlc/code.playbook/code.registry.ts +7 -32
- package/reference/sdlc/code.playbook/playbook-captain.d.ts +39 -14
- package/reference/sdlc/code.playbook/playbook-captain.js +1014 -299
- package/reference/sdlc/code.playbook/playbook-captain.ts +1450 -406
- package/reference/sdlc/code.playbook/playbook.config.template.yaml +41 -49
- package/reference/sdlc/decide.md +4 -4
- package/reference/sdlc/decide.playbook/decide.fsm.d.ts +10 -10
- package/reference/sdlc/decide.playbook/decide.fsm.js +21 -14
- package/reference/sdlc/decide.playbook/decide.fsm.ts +27 -23
- package/reference/sdlc/decide.playbook/decide.gears.md +3 -5
- package/reference/sdlc/decide.playbook/decide.playbook.d.ts +11 -13
- package/reference/sdlc/decide.playbook/decide.playbook.js +465 -246
- package/reference/sdlc/decide.playbook/decide.playbook.ts +623 -283
- package/reference/sdlc/decide.playbook/decide.registry.d.ts +5 -13
- package/reference/sdlc/decide.playbook/decide.registry.js +3 -9
- package/reference/sdlc/decide.playbook/decide.registry.ts +7 -31
- package/reference/sdlc/review.md +4 -5
- package/reference/sdlc/review.playbook/review.fsm.d.ts +9 -11
- package/reference/sdlc/review.playbook/review.fsm.js +30 -24
- package/reference/sdlc/review.playbook/review.fsm.ts +39 -35
- package/reference/sdlc/review.playbook/review.gears.md +6 -5
- package/reference/sdlc/review.playbook/review.playbook.d.ts +2 -1
- package/reference/sdlc/review.playbook/review.playbook.js +29 -23
- package/reference/sdlc/review.playbook/review.playbook.ts +38 -28
- package/reference/sdlc/review.playbook/review.registry.d.ts +5 -13
- package/reference/sdlc/review.playbook/review.registry.js +3 -16
- package/reference/sdlc/review.playbook/review.registry.ts +7 -38
- package/slc/gears2fsm.md +45 -24
- package/slc/link.md +297 -135
- package/slc/text2gears.md +19 -18
- package/src/runtime.d.ts +21 -16
- package/src/runtime.ts +20 -23
- package/src/xstate-playbook-runtime.d.ts +34 -20
- package/src/xstate-playbook-runtime.js +973 -400
- package/src/xstate-playbook-runtime.ts +1203 -457
- package/src/xstate-runtime.d.ts +17 -7
- package/src/xstate-runtime.js +198 -81
- package/src/xstate-runtime.ts +339 -112
package/src/xstate-runtime.ts
CHANGED
|
@@ -68,11 +68,45 @@ function withAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// slc/link.md §Abort: cancellation is causal identity with the applicable
|
|
72
|
+
// signal's reason; an `AbortError`-named rejection that is not that exact
|
|
73
|
+
// reason is a control-plane failure to surface, never an abort to swallow.
|
|
71
74
|
function isAbortReason(error: unknown, signal: AbortSignal): boolean {
|
|
72
|
-
return (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
return signal.aborted && Object.is(error, signal.reason);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Immutable cancellation provenance for one runtime operation. The captured
|
|
80
|
+
* signal identities do not change when a mutable runtime advances to another
|
|
81
|
+
* public boundary, while each signal's eventual reason remains observable.
|
|
82
|
+
*/
|
|
83
|
+
interface AbortReasonClassifier {
|
|
84
|
+
isAbortReason(error: unknown): boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createAbortReasonClassifier(
|
|
88
|
+
...sources: readonly (
|
|
89
|
+
| AbortSignal
|
|
90
|
+
| AbortReasonClassifier
|
|
91
|
+
| undefined
|
|
92
|
+
)[]
|
|
93
|
+
): AbortReasonClassifier {
|
|
94
|
+
const captured = Object.freeze(
|
|
95
|
+
sources.filter(
|
|
96
|
+
(
|
|
97
|
+
source,
|
|
98
|
+
): source is AbortSignal | AbortReasonClassifier =>
|
|
99
|
+
source !== undefined,
|
|
100
|
+
),
|
|
75
101
|
);
|
|
102
|
+
return Object.freeze({
|
|
103
|
+
isAbortReason: (error: unknown): boolean =>
|
|
104
|
+
captured.some((source) =>
|
|
105
|
+
source instanceof AbortSignal
|
|
106
|
+
? isAbortReason(error, source)
|
|
107
|
+
: source.isAbortReason(error),
|
|
108
|
+
),
|
|
109
|
+
});
|
|
76
110
|
}
|
|
77
111
|
|
|
78
112
|
const NEVER_ABORTED_SIGNAL = new AbortController().signal;
|
|
@@ -119,7 +153,10 @@ export function registerPlaybookAbortCleanup(
|
|
|
119
153
|
void cleanup.catch(() => undefined);
|
|
120
154
|
}
|
|
121
155
|
|
|
122
|
-
async function drainPlaybookAbortCleanups(
|
|
156
|
+
async function drainPlaybookAbortCleanups(
|
|
157
|
+
signal: AbortSignal,
|
|
158
|
+
aborts: AbortReasonClassifier,
|
|
159
|
+
): Promise<void> {
|
|
123
160
|
const failures: unknown[] = [];
|
|
124
161
|
while (true) {
|
|
125
162
|
const pending = abortCleanups.get(signal);
|
|
@@ -128,7 +165,12 @@ async function drainPlaybookAbortCleanups(signal: AbortSignal): Promise<void> {
|
|
|
128
165
|
pending.clear();
|
|
129
166
|
const outcomes = await Promise.allSettled(batch);
|
|
130
167
|
for (const outcome of outcomes) {
|
|
131
|
-
if (
|
|
168
|
+
if (
|
|
169
|
+
outcome.status === 'rejected' &&
|
|
170
|
+
!aborts.isAbortReason(outcome.reason)
|
|
171
|
+
) {
|
|
172
|
+
failures.push(outcome.reason);
|
|
173
|
+
}
|
|
132
174
|
}
|
|
133
175
|
}
|
|
134
176
|
abortCleanups.delete(signal);
|
|
@@ -386,6 +428,50 @@ function capturedSessionStore(
|
|
|
386
428
|
});
|
|
387
429
|
}
|
|
388
430
|
|
|
431
|
+
function capturedRoleBindings(
|
|
432
|
+
descriptors: PropertyDescriptorMap,
|
|
433
|
+
): NonNullable<PlaybookSession['roleBindings']> {
|
|
434
|
+
const captured = snapshotJsonValue(
|
|
435
|
+
capturedDataValue(
|
|
436
|
+
descriptors,
|
|
437
|
+
'roleBindings',
|
|
438
|
+
'playbook session roleBindings',
|
|
439
|
+
),
|
|
440
|
+
'playbook session roleBindings',
|
|
441
|
+
);
|
|
442
|
+
if (!isRecord(captured)) {
|
|
443
|
+
throw new TypeError('playbook session roleBindings must be an object');
|
|
444
|
+
}
|
|
445
|
+
const bindings: Record<
|
|
446
|
+
string,
|
|
447
|
+
{ readonly playerId: string; readonly promptIdentity: string }
|
|
448
|
+
> = {};
|
|
449
|
+
for (const [roleId, value] of Object.entries(captured)) {
|
|
450
|
+
requireNonEmptyString(roleId, 'playbook session roleBindings role id');
|
|
451
|
+
if (!isRecord(value)) {
|
|
452
|
+
throw new TypeError(
|
|
453
|
+
`playbook session roleBindings.${roleId} must be an object`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
rejectUnknownKeys(
|
|
457
|
+
value,
|
|
458
|
+
['playerId', 'promptIdentity'],
|
|
459
|
+
`playbook session roleBindings.${roleId}`,
|
|
460
|
+
);
|
|
461
|
+
defineEnumerableDataProperty(bindings, roleId, Object.freeze({
|
|
462
|
+
playerId: requireNonEmptyString(
|
|
463
|
+
value.playerId,
|
|
464
|
+
`playbook session roleBindings.${roleId}.playerId`,
|
|
465
|
+
),
|
|
466
|
+
promptIdentity: requireNonEmptyString(
|
|
467
|
+
value.promptIdentity,
|
|
468
|
+
`playbook session roleBindings.${roleId}.promptIdentity`,
|
|
469
|
+
),
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
return Object.freeze(bindings);
|
|
473
|
+
}
|
|
474
|
+
|
|
389
475
|
/** Validate session causality and detach its immutable identity from the host. */
|
|
390
476
|
export function snapshotPlaybookSession(
|
|
391
477
|
session: PlaybookSession,
|
|
@@ -453,6 +539,10 @@ export function snapshotPlaybookSession(
|
|
|
453
539
|
sessionDescriptors,
|
|
454
540
|
'playerSessions',
|
|
455
541
|
);
|
|
542
|
+
const hasRoleBindings = Object.prototype.hasOwnProperty.call(
|
|
543
|
+
sessionDescriptors,
|
|
544
|
+
'roleBindings',
|
|
545
|
+
);
|
|
456
546
|
let parentSessionId: string | undefined;
|
|
457
547
|
let parentCallId: string | undefined;
|
|
458
548
|
if (depth === 0) {
|
|
@@ -501,6 +591,9 @@ export function snapshotPlaybookSession(
|
|
|
501
591
|
const playerSessions = hasPlayerSessions
|
|
502
592
|
? capturedSessionStore(sessionDescriptors)
|
|
503
593
|
: undefined;
|
|
594
|
+
const roleBindings = hasRoleBindings
|
|
595
|
+
? capturedRoleBindings(sessionDescriptors)
|
|
596
|
+
: undefined;
|
|
504
597
|
return Object.freeze({
|
|
505
598
|
sessionId,
|
|
506
599
|
playbookId,
|
|
@@ -508,6 +601,7 @@ export function snapshotPlaybookSession(
|
|
|
508
601
|
...(parentSessionId === undefined ? {} : { parentSessionId }),
|
|
509
602
|
...(parentCallId === undefined ? {} : { parentCallId }),
|
|
510
603
|
depth,
|
|
604
|
+
...(roleBindings === undefined ? {} : { roleBindings }),
|
|
511
605
|
...(playerSessions === undefined ? {} : { playerSessions }),
|
|
512
606
|
ports,
|
|
513
607
|
});
|
|
@@ -619,7 +713,10 @@ function normalizeStateValue(
|
|
|
619
713
|
|
|
620
714
|
export interface PlaybookStateMetadata {
|
|
621
715
|
stateId: string;
|
|
622
|
-
|
|
716
|
+
// Optional by contract: a state whose source declares no description
|
|
717
|
+
// carries none, and no id is ever promoted into one
|
|
718
|
+
// (slc/link.md §Snapshot normalization).
|
|
719
|
+
description?: string;
|
|
623
720
|
}
|
|
624
721
|
|
|
625
722
|
interface MachineSnapshotLike {
|
|
@@ -666,17 +763,31 @@ export function activePlaybookStateMetadata(
|
|
|
666
763
|
meta.playbook.stateId,
|
|
667
764
|
`${nodeId}.meta.playbook.stateId`,
|
|
668
765
|
);
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
766
|
+
// Description is optional: a state may declare none and stay fully
|
|
767
|
+
// usable, merely carrying no `stateDescription` downstream. A declared
|
|
768
|
+
// description must still be a nonempty string.
|
|
769
|
+
const description =
|
|
770
|
+
meta.playbook.description === undefined
|
|
771
|
+
? undefined
|
|
772
|
+
: requireNonEmptyString(
|
|
773
|
+
meta.playbook.description,
|
|
774
|
+
`${nodeId}.meta.playbook.description`,
|
|
775
|
+
);
|
|
673
776
|
const previous = byStateId.get(stateId);
|
|
674
|
-
if (
|
|
777
|
+
if (
|
|
778
|
+
previous?.description !== undefined &&
|
|
779
|
+
description !== undefined &&
|
|
780
|
+
previous.description !== description
|
|
781
|
+
) {
|
|
675
782
|
throw new TypeError(
|
|
676
783
|
`active state id ${stateId} has conflicting descriptions`,
|
|
677
784
|
);
|
|
678
785
|
}
|
|
679
|
-
|
|
786
|
+
const effective = description ?? previous?.description;
|
|
787
|
+
byStateId.set(stateId, {
|
|
788
|
+
stateId,
|
|
789
|
+
...(effective === undefined ? {} : { description: effective }),
|
|
790
|
+
});
|
|
680
791
|
}
|
|
681
792
|
return [...byStateId.values()].sort((left, right) =>
|
|
682
793
|
left.stateId.localeCompare(right.stateId),
|
|
@@ -805,9 +916,10 @@ function snapshotSuspendedCall(
|
|
|
805
916
|
return Object.freeze(call);
|
|
806
917
|
}
|
|
807
918
|
|
|
808
|
-
// DR-014 §1 / DR-031 §5: validate and detach a host-supplied
|
|
809
|
-
// snapshot before restore touches any state. A suspended
|
|
810
|
-
// rejected unless the restore path explicitly promises to seed and
|
|
919
|
+
// DR-014 §1 / DR-031 §5 / DR-032: validate and detach a host-supplied
|
|
920
|
+
// schema-3 runtime snapshot before restore touches any state. A suspended
|
|
921
|
+
// call is rejected unless the restore path explicitly promises to seed and
|
|
922
|
+
// claim it; older schemas are rejected rather than guessing role identity.
|
|
811
923
|
export function assertPlaybookRuntimeSnapshot(
|
|
812
924
|
value: unknown,
|
|
813
925
|
expectedPlaybookId: string,
|
|
@@ -838,19 +950,18 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
838
950
|
);
|
|
839
951
|
}
|
|
840
952
|
const allowSuspendedCall = capturedOptions.allowSuspendedCall ?? false;
|
|
841
|
-
if (snapshot.schemaVersion !==
|
|
953
|
+
if (snapshot.schemaVersion !== 3) {
|
|
842
954
|
throw new TypeError(
|
|
843
|
-
`runtime snapshot schemaVersion ${String(snapshot.schemaVersion)} is not supported (expected
|
|
955
|
+
`runtime snapshot schemaVersion ${String(snapshot.schemaVersion)} is not supported (expected 3)`,
|
|
844
956
|
);
|
|
845
957
|
}
|
|
846
|
-
const schemaVersion = snapshot.schemaVersion;
|
|
847
958
|
rejectUnknownKeys(
|
|
848
959
|
snapshot,
|
|
849
960
|
[
|
|
850
961
|
'schemaVersion',
|
|
851
962
|
'playbookId',
|
|
852
963
|
'machine',
|
|
853
|
-
'
|
|
964
|
+
'roleResumeTokens',
|
|
854
965
|
'sequences',
|
|
855
966
|
'state',
|
|
856
967
|
'pendingBossQuestions',
|
|
@@ -858,13 +969,8 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
858
969
|
],
|
|
859
970
|
'runtime snapshot',
|
|
860
971
|
);
|
|
861
|
-
if (schemaVersion === 1 && own(snapshot, 'suspendedCall')) {
|
|
862
|
-
throw new TypeError(
|
|
863
|
-
'runtime snapshot schemaVersion 1 must not carry suspendedCall',
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
972
|
let suspendedCall: PlaybookSuspendedCall | undefined;
|
|
867
|
-
if (
|
|
973
|
+
if (own(snapshot, 'suspendedCall')) {
|
|
868
974
|
suspendedCall = snapshotSuspendedCall(snapshot.suspendedCall);
|
|
869
975
|
if (!allowSuspendedCall) {
|
|
870
976
|
throw new TypeError(
|
|
@@ -885,19 +991,19 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
885
991
|
throw new TypeError('runtime snapshot machine must be an object');
|
|
886
992
|
}
|
|
887
993
|
const machine = snapshot.machine;
|
|
888
|
-
if (!isRecord(snapshot.
|
|
994
|
+
if (!isRecord(snapshot.roleResumeTokens)) {
|
|
889
995
|
throw new TypeError(
|
|
890
|
-
'runtime snapshot
|
|
996
|
+
'runtime snapshot roleResumeTokens must be an object',
|
|
891
997
|
);
|
|
892
998
|
}
|
|
893
|
-
const
|
|
894
|
-
for (const [
|
|
999
|
+
const roleResumeTokens: Record<string, string> = {};
|
|
1000
|
+
for (const [roleId, token] of Object.entries(snapshot.roleResumeTokens)) {
|
|
895
1001
|
defineEnumerableDataProperty(
|
|
896
|
-
|
|
897
|
-
|
|
1002
|
+
roleResumeTokens,
|
|
1003
|
+
requireNonEmptyString(roleId, 'runtime snapshot roleResumeTokens role id'),
|
|
898
1004
|
requireNonEmptyString(
|
|
899
1005
|
token,
|
|
900
|
-
`runtime snapshot
|
|
1006
|
+
`runtime snapshot roleResumeTokens.${roleId}`,
|
|
901
1007
|
),
|
|
902
1008
|
);
|
|
903
1009
|
}
|
|
@@ -932,7 +1038,7 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
932
1038
|
const state = snapshot.state as unknown as PlaybookState;
|
|
933
1039
|
if (state.tags.includes(SUSPENDED_TAG) && suspendedCall === undefined) {
|
|
934
1040
|
throw new TypeError(
|
|
935
|
-
`runtime snapshot state tagged ${SUSPENDED_TAG} requires
|
|
1041
|
+
`runtime snapshot state tagged ${SUSPENDED_TAG} requires suspendedCall`,
|
|
936
1042
|
);
|
|
937
1043
|
}
|
|
938
1044
|
if (suspendedCall) {
|
|
@@ -976,15 +1082,36 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
976
1082
|
if (!isRecord(entry)) throw new TypeError(`${path} must be an object`);
|
|
977
1083
|
rejectUnknownKeys(
|
|
978
1084
|
entry,
|
|
979
|
-
['questionId', '
|
|
1085
|
+
['questionId', 'asker', 'question', 'sourceItem'],
|
|
980
1086
|
path,
|
|
981
1087
|
);
|
|
1088
|
+
if (!isRecord(entry.asker)) {
|
|
1089
|
+
throw new TypeError(`${path}.asker must be an object`);
|
|
1090
|
+
}
|
|
1091
|
+
let asker: PlaybookPendingBossQuestion['asker'];
|
|
1092
|
+
if (entry.asker.kind === 'captain') {
|
|
1093
|
+
rejectUnknownKeys(entry.asker, ['kind'], `${path}.asker`);
|
|
1094
|
+
asker = Object.freeze({ kind: 'captain' });
|
|
1095
|
+
} else if (entry.asker.kind === 'role') {
|
|
1096
|
+
rejectUnknownKeys(entry.asker, ['kind', 'roleId'], `${path}.asker`);
|
|
1097
|
+
asker = Object.freeze({
|
|
1098
|
+
kind: 'role',
|
|
1099
|
+
roleId: requireNonEmptyString(
|
|
1100
|
+
entry.asker.roleId,
|
|
1101
|
+
`${path}.asker.roleId`,
|
|
1102
|
+
),
|
|
1103
|
+
});
|
|
1104
|
+
} else {
|
|
1105
|
+
throw new TypeError(
|
|
1106
|
+
`${path}.asker.kind must be "captain" or "role"`,
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
982
1109
|
const question: PlaybookPendingBossQuestion = {
|
|
983
1110
|
questionId: requireNonEmptyString(
|
|
984
1111
|
entry.questionId,
|
|
985
1112
|
`${path}.questionId`,
|
|
986
1113
|
),
|
|
987
|
-
|
|
1114
|
+
asker,
|
|
988
1115
|
question: requireNonEmptyString(entry.question, `${path}.question`),
|
|
989
1116
|
...(entry.sourceItem === undefined
|
|
990
1117
|
? {}
|
|
@@ -1001,16 +1128,13 @@ export function assertPlaybookRuntimeSnapshot(
|
|
|
1001
1128
|
const fields = {
|
|
1002
1129
|
playbookId,
|
|
1003
1130
|
machine,
|
|
1004
|
-
|
|
1131
|
+
roleResumeTokens: Object.freeze(roleResumeTokens),
|
|
1005
1132
|
sequences: Object.freeze(sequences),
|
|
1006
1133
|
state,
|
|
1007
1134
|
pendingBossQuestions: Object.freeze(pendingBossQuestions),
|
|
1008
1135
|
};
|
|
1009
|
-
if (schemaVersion === 1) {
|
|
1010
|
-
return Object.freeze({ schemaVersion: 1, ...fields });
|
|
1011
|
-
}
|
|
1012
1136
|
return Object.freeze({
|
|
1013
|
-
schemaVersion:
|
|
1137
|
+
schemaVersion: 3,
|
|
1014
1138
|
...fields,
|
|
1015
1139
|
...(suspendedCall === undefined ? {} : { suspendedCall }),
|
|
1016
1140
|
});
|
|
@@ -1041,12 +1165,29 @@ export interface NestedPlaybookBridgeOptions {
|
|
|
1041
1165
|
request: PlaybookCallRequest,
|
|
1042
1166
|
signal: AbortSignal,
|
|
1043
1167
|
): Promise<PlaybookCallStart>;
|
|
1044
|
-
emitStarted(
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1168
|
+
emitStarted(
|
|
1169
|
+
event: PlaybookCallStarted,
|
|
1170
|
+
aborts?: AbortReasonClassifier,
|
|
1171
|
+
): Promise<void>;
|
|
1172
|
+
emitFinished(
|
|
1173
|
+
event: PlaybookCallFinished,
|
|
1174
|
+
aborts?: AbortReasonClassifier,
|
|
1175
|
+
): Promise<void>;
|
|
1176
|
+
drain(aborts?: AbortReasonClassifier): Promise<void>;
|
|
1177
|
+
bindResumeSignal?(
|
|
1178
|
+
signal: AbortSignal,
|
|
1179
|
+
aborts?: AbortReasonClassifier,
|
|
1180
|
+
): void;
|
|
1181
|
+
/** Bind provenance to the root transition caused by this child result. */
|
|
1182
|
+
bindActorSettlement?(aborts: AbortReasonClassifier): void;
|
|
1183
|
+
onControlPlaneError?(
|
|
1184
|
+
error: unknown,
|
|
1185
|
+
aborts?: AbortReasonClassifier,
|
|
1186
|
+
): void;
|
|
1187
|
+
onBackgroundError?(
|
|
1188
|
+
error: unknown,
|
|
1189
|
+
aborts?: AbortReasonClassifier,
|
|
1190
|
+
): void;
|
|
1050
1191
|
}
|
|
1051
1192
|
|
|
1052
1193
|
export class NestedPlaybookCallError extends Error {
|
|
@@ -1070,6 +1211,7 @@ interface ActiveCall {
|
|
|
1070
1211
|
readonly finished: Deferred<void>;
|
|
1071
1212
|
readonly controller: AbortController;
|
|
1072
1213
|
readonly signal: AbortSignal;
|
|
1214
|
+
readonly aborts: AbortReasonClassifier;
|
|
1073
1215
|
phase: 'starting' | 'restoring' | 'suspended' | 'settling';
|
|
1074
1216
|
childSessionId?: string;
|
|
1075
1217
|
abortListener?: () => void;
|
|
@@ -1228,6 +1370,9 @@ export function validatePlayerResult(
|
|
|
1228
1370
|
);
|
|
1229
1371
|
validateRunStatus(result.status, `${path}.status`);
|
|
1230
1372
|
validateOptionalString(result, 'resumeToken', path);
|
|
1373
|
+
if (result.resumeToken !== undefined) {
|
|
1374
|
+
requireNonEmptyString(result.resumeToken, `${path}.resumeToken`);
|
|
1375
|
+
}
|
|
1231
1376
|
validateOptionalString(result, 'finalText', path);
|
|
1232
1377
|
validateOptionalString(result, 'error', path);
|
|
1233
1378
|
return result as unknown as PlayerResult;
|
|
@@ -1441,21 +1586,29 @@ export function createNestedPlaybookBridge<
|
|
|
1441
1586
|
(pendingCall: PlaybookPendingCall) => void
|
|
1442
1587
|
>();
|
|
1443
1588
|
|
|
1444
|
-
const reportBackgroundError = (
|
|
1589
|
+
const reportBackgroundError = (
|
|
1590
|
+
error: unknown,
|
|
1591
|
+
aborts?: AbortReasonClassifier,
|
|
1592
|
+
): void => {
|
|
1593
|
+
if (aborts?.isAbortReason(error)) return;
|
|
1445
1594
|
try {
|
|
1446
|
-
options.onBackgroundError?.(error);
|
|
1595
|
+
options.onBackgroundError?.(error, aborts);
|
|
1447
1596
|
} catch {
|
|
1448
1597
|
// Background observers are a terminal sink and cannot own cleanup.
|
|
1449
1598
|
}
|
|
1450
1599
|
};
|
|
1451
1600
|
|
|
1452
|
-
const reportControlPlaneError = (
|
|
1601
|
+
const reportControlPlaneError = (
|
|
1602
|
+
error: unknown,
|
|
1603
|
+
aborts?: AbortReasonClassifier,
|
|
1604
|
+
): void => {
|
|
1605
|
+
if (aborts?.isAbortReason(error)) return;
|
|
1453
1606
|
try {
|
|
1454
|
-
options.onControlPlaneError?.(error);
|
|
1607
|
+
options.onControlPlaneError?.(error, aborts);
|
|
1455
1608
|
} catch (callbackError) {
|
|
1456
1609
|
// Observability callbacks must never prevent terminal cleanup of the
|
|
1457
1610
|
// invocation they are observing.
|
|
1458
|
-
reportBackgroundError(callbackError);
|
|
1611
|
+
reportBackgroundError(callbackError, aborts);
|
|
1459
1612
|
}
|
|
1460
1613
|
};
|
|
1461
1614
|
|
|
@@ -1510,18 +1663,31 @@ export function createNestedPlaybookBridge<
|
|
|
1510
1663
|
if (current === active) current = undefined;
|
|
1511
1664
|
};
|
|
1512
1665
|
|
|
1666
|
+
// A failure causally identical to an applicable abort reason is the
|
|
1667
|
+
// cancellation's own evidence, never a control-plane error.
|
|
1668
|
+
const reportNonAbortControlError = (
|
|
1669
|
+
error: unknown,
|
|
1670
|
+
aborts: AbortReasonClassifier,
|
|
1671
|
+
): void => {
|
|
1672
|
+
reportControlPlaneError(error, aborts);
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1513
1675
|
const emitFinish = async (
|
|
1514
1676
|
active: ActiveCall,
|
|
1515
1677
|
result: PlaybookCallResult,
|
|
1678
|
+
aborts: AbortReasonClassifier,
|
|
1516
1679
|
): Promise<void> => {
|
|
1517
|
-
await options.emitFinished(
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1680
|
+
await options.emitFinished(
|
|
1681
|
+
{
|
|
1682
|
+
callId: active.callId,
|
|
1683
|
+
stateId: active.input.stateId,
|
|
1684
|
+
playbookId: active.input.playbookId,
|
|
1685
|
+
text: active.input.text,
|
|
1686
|
+
result,
|
|
1687
|
+
},
|
|
1688
|
+
aborts,
|
|
1689
|
+
);
|
|
1690
|
+
await options.drain(aborts);
|
|
1525
1691
|
};
|
|
1526
1692
|
|
|
1527
1693
|
const finishImmediate = async (
|
|
@@ -1530,20 +1696,25 @@ export function createNestedPlaybookBridge<
|
|
|
1530
1696
|
controlError?: unknown,
|
|
1531
1697
|
resultAfterAbortCleanup?: () => PlaybookCallResult,
|
|
1532
1698
|
): Promise<JsonValue | undefined> => {
|
|
1699
|
+
const aborts = active.aborts;
|
|
1533
1700
|
let effectiveResult = result;
|
|
1534
1701
|
let cleanupControlError: unknown;
|
|
1535
1702
|
if (result.status === 'aborted' || active.signal.aborted) {
|
|
1536
1703
|
try {
|
|
1537
|
-
await drainPlaybookAbortCleanups(active.signal);
|
|
1704
|
+
await drainPlaybookAbortCleanups(active.signal, aborts);
|
|
1538
1705
|
} catch (error) {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1706
|
+
// A cleanup rejection identical to an applicable abort reason is
|
|
1707
|
+
// the cancellation's own evidence — no latch, no result override.
|
|
1708
|
+
if (!aborts.isAbortReason(error)) {
|
|
1709
|
+
cleanupControlError = error;
|
|
1710
|
+
reportControlPlaneError(error, aborts);
|
|
1711
|
+
effectiveResult = resultFromThrown(
|
|
1712
|
+
active.input.playbookId,
|
|
1713
|
+
active.childSessionId,
|
|
1714
|
+
error,
|
|
1715
|
+
false,
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1547
1718
|
}
|
|
1548
1719
|
if (cleanupControlError === undefined && resultAfterAbortCleanup) {
|
|
1549
1720
|
effectiveResult = resultAfterAbortCleanup();
|
|
@@ -1551,15 +1722,16 @@ export function createNestedPlaybookBridge<
|
|
|
1551
1722
|
}
|
|
1552
1723
|
let finishControlError: unknown;
|
|
1553
1724
|
try {
|
|
1554
|
-
await emitFinish(active, effectiveResult);
|
|
1725
|
+
await emitFinish(active, effectiveResult, aborts);
|
|
1555
1726
|
} catch (error) {
|
|
1556
|
-
|
|
1727
|
+
reportNonAbortControlError(error, aborts);
|
|
1557
1728
|
finishControlError = error;
|
|
1558
1729
|
} finally {
|
|
1559
1730
|
// An immediate call can never be resumed. Even when its finish
|
|
1560
1731
|
// emission fails, do not leave a permanently unresumable call in the
|
|
1561
1732
|
// bridge and prevent disposal or a later invocation.
|
|
1562
1733
|
clear(active);
|
|
1734
|
+
options.bindActorSettlement?.(aborts);
|
|
1563
1735
|
}
|
|
1564
1736
|
if (controlError !== undefined) throw controlError;
|
|
1565
1737
|
if (cleanupControlError !== undefined) throw cleanupControlError;
|
|
@@ -1571,6 +1743,7 @@ export function createNestedPlaybookBridge<
|
|
|
1571
1743
|
active: ActiveCall,
|
|
1572
1744
|
result: PlaybookCallResult,
|
|
1573
1745
|
controlError?: unknown,
|
|
1746
|
+
aborts: AbortReasonClassifier = active.aborts,
|
|
1574
1747
|
): Promise<void> => {
|
|
1575
1748
|
if (active.phase === 'settling' && active.settlement) {
|
|
1576
1749
|
await active.settlement;
|
|
@@ -1588,37 +1761,45 @@ export function createNestedPlaybookBridge<
|
|
|
1588
1761
|
effectiveResult = resultFromThrown(
|
|
1589
1762
|
active.input.playbookId,
|
|
1590
1763
|
active.childSessionId,
|
|
1591
|
-
active.signal.reason
|
|
1592
|
-
new Error('Nested playbook invocation aborted'),
|
|
1764
|
+
active.signal.reason,
|
|
1593
1765
|
true,
|
|
1594
1766
|
);
|
|
1595
1767
|
}
|
|
1596
1768
|
try {
|
|
1597
|
-
await drainPlaybookAbortCleanups(active.signal);
|
|
1769
|
+
await drainPlaybookAbortCleanups(active.signal, aborts);
|
|
1598
1770
|
} catch (cleanupError) {
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1771
|
+
// A cleanup rejection identical to an applicable abort reason is
|
|
1772
|
+
// the cancellation's own evidence — no latch, no result override.
|
|
1773
|
+
if (!aborts.isAbortReason(cleanupError)) {
|
|
1774
|
+
cleanupControlError = cleanupError;
|
|
1775
|
+
reportControlPlaneError(cleanupError, aborts);
|
|
1776
|
+
effectiveResult = resultFromThrown(
|
|
1777
|
+
active.input.playbookId,
|
|
1778
|
+
active.childSessionId,
|
|
1779
|
+
cleanupError,
|
|
1780
|
+
false,
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1607
1783
|
}
|
|
1608
1784
|
}
|
|
1609
1785
|
try {
|
|
1610
|
-
await emitFinish(active, effectiveResult);
|
|
1786
|
+
await emitFinish(active, effectiveResult, aborts);
|
|
1611
1787
|
} catch (error) {
|
|
1612
1788
|
// A finish event is the durable return boundary. If it cannot be
|
|
1613
1789
|
// emitted and drained, the child result must not remain retryable:
|
|
1614
1790
|
// clear the identity and fail the promise actor so its parent takes
|
|
1615
|
-
// onError instead of observing a phantom suspended child.
|
|
1616
|
-
|
|
1791
|
+
// onError instead of observing a phantom suspended child. A finish
|
|
1792
|
+
// rejection that is an applicable abort reason — the invocation's
|
|
1793
|
+
// or the settling resume's — evidences cancellation, not a
|
|
1794
|
+
// control-plane failure (slc/link.md §Abort).
|
|
1795
|
+
reportControlPlaneError(error, aborts);
|
|
1617
1796
|
clear(active);
|
|
1797
|
+
options.bindActorSettlement?.(aborts);
|
|
1618
1798
|
active.deferred.reject(error);
|
|
1619
1799
|
throw error;
|
|
1620
1800
|
}
|
|
1621
1801
|
clear(active);
|
|
1802
|
+
options.bindActorSettlement?.(aborts);
|
|
1622
1803
|
if (controlError !== undefined) {
|
|
1623
1804
|
active.deferred.reject(controlError);
|
|
1624
1805
|
} else if (cleanupControlError !== undefined) {
|
|
@@ -1652,6 +1833,7 @@ export function createNestedPlaybookBridge<
|
|
|
1652
1833
|
active.restoreRolledBack = true;
|
|
1653
1834
|
clear(active);
|
|
1654
1835
|
usedCallIds.delete(active.callId);
|
|
1836
|
+
options.bindActorSettlement?.(active.aborts);
|
|
1655
1837
|
active.deferred.reject(error);
|
|
1656
1838
|
return active;
|
|
1657
1839
|
};
|
|
@@ -1665,11 +1847,11 @@ export function createNestedPlaybookBridge<
|
|
|
1665
1847
|
const result = resultFromThrown(
|
|
1666
1848
|
active.input.playbookId,
|
|
1667
1849
|
active.childSessionId,
|
|
1668
|
-
active.signal.reason
|
|
1850
|
+
active.signal.reason,
|
|
1669
1851
|
true,
|
|
1670
1852
|
);
|
|
1671
1853
|
void settlePending(active, result).catch((error: unknown) => {
|
|
1672
|
-
reportBackgroundError(error);
|
|
1854
|
+
reportBackgroundError(error, active.aborts);
|
|
1673
1855
|
});
|
|
1674
1856
|
};
|
|
1675
1857
|
active.abortListener = abortListener;
|
|
@@ -1682,7 +1864,7 @@ export function createNestedPlaybookBridge<
|
|
|
1682
1864
|
try {
|
|
1683
1865
|
listener(pendingCall);
|
|
1684
1866
|
} catch (error) {
|
|
1685
|
-
reportBackgroundError(error);
|
|
1867
|
+
reportBackgroundError(error, active.aborts);
|
|
1686
1868
|
}
|
|
1687
1869
|
}
|
|
1688
1870
|
if (active.signal.aborted) abortListener();
|
|
@@ -1766,10 +1948,17 @@ export function createNestedPlaybookBridge<
|
|
|
1766
1948
|
}
|
|
1767
1949
|
const controller = new AbortController();
|
|
1768
1950
|
let callSignal: AbortSignal;
|
|
1951
|
+
let callAborts: AbortReasonClassifier;
|
|
1769
1952
|
try {
|
|
1953
|
+
const boundarySignal = options.getBoundarySignal?.();
|
|
1770
1954
|
callSignal = combineAbortSignals(
|
|
1771
1955
|
invocationSignal,
|
|
1772
|
-
|
|
1956
|
+
boundarySignal,
|
|
1957
|
+
controller.signal,
|
|
1958
|
+
);
|
|
1959
|
+
callAborts = createAbortReasonClassifier(
|
|
1960
|
+
invocationSignal,
|
|
1961
|
+
boundarySignal,
|
|
1773
1962
|
controller.signal,
|
|
1774
1963
|
);
|
|
1775
1964
|
} catch (error) {
|
|
@@ -1786,6 +1975,7 @@ export function createNestedPlaybookBridge<
|
|
|
1786
1975
|
finished: deferred<void>(),
|
|
1787
1976
|
controller,
|
|
1788
1977
|
signal: callSignal,
|
|
1978
|
+
aborts: callAborts,
|
|
1789
1979
|
phase: 'restoring',
|
|
1790
1980
|
childSessionId: seed.childSessionId,
|
|
1791
1981
|
};
|
|
@@ -1804,8 +1994,7 @@ export function createNestedPlaybookBridge<
|
|
|
1804
1994
|
}
|
|
1805
1995
|
rollbackRestoredCall(
|
|
1806
1996
|
mode,
|
|
1807
|
-
active.signal.reason
|
|
1808
|
-
new Error('Restored nested playbook invocation aborted'),
|
|
1997
|
+
active.signal.reason,
|
|
1809
1998
|
);
|
|
1810
1999
|
};
|
|
1811
2000
|
active.abortListener = restoreAbortListener;
|
|
@@ -1847,10 +2036,17 @@ export function createNestedPlaybookBridge<
|
|
|
1847
2036
|
usedCallIds.add(callId);
|
|
1848
2037
|
const controller = new AbortController();
|
|
1849
2038
|
let callSignal: AbortSignal;
|
|
2039
|
+
let callAborts: AbortReasonClassifier;
|
|
1850
2040
|
try {
|
|
2041
|
+
const boundarySignal = options.getBoundarySignal?.();
|
|
1851
2042
|
callSignal = combineAbortSignals(
|
|
1852
2043
|
invocationSignal,
|
|
1853
|
-
|
|
2044
|
+
boundarySignal,
|
|
2045
|
+
controller.signal,
|
|
2046
|
+
);
|
|
2047
|
+
callAborts = createAbortReasonClassifier(
|
|
2048
|
+
invocationSignal,
|
|
2049
|
+
boundarySignal,
|
|
1854
2050
|
controller.signal,
|
|
1855
2051
|
);
|
|
1856
2052
|
} catch (error) {
|
|
@@ -1863,6 +2059,7 @@ export function createNestedPlaybookBridge<
|
|
|
1863
2059
|
finished: deferred<void>(),
|
|
1864
2060
|
controller,
|
|
1865
2061
|
signal: callSignal,
|
|
2062
|
+
aborts: callAborts,
|
|
1866
2063
|
phase: 'starting',
|
|
1867
2064
|
};
|
|
1868
2065
|
current = active;
|
|
@@ -1872,40 +2069,56 @@ export function createNestedPlaybookBridge<
|
|
|
1872
2069
|
// for their entering state. Yield through the runtime's global queue so
|
|
1873
2070
|
// that transition/status telemetry is enqueued before call.started.
|
|
1874
2071
|
try {
|
|
1875
|
-
await options.drain();
|
|
2072
|
+
await options.drain(active.aborts);
|
|
1876
2073
|
} catch (error) {
|
|
1877
|
-
|
|
2074
|
+
reportNonAbortControlError(error, active.aborts);
|
|
1878
2075
|
clear(active);
|
|
1879
2076
|
throw error;
|
|
1880
2077
|
}
|
|
1881
2078
|
try {
|
|
1882
|
-
await options.emitStarted(
|
|
2079
|
+
await options.emitStarted(
|
|
2080
|
+
{ callId, ...normalizedInput },
|
|
2081
|
+
active.aborts,
|
|
2082
|
+
);
|
|
1883
2083
|
} catch (error) {
|
|
1884
|
-
|
|
2084
|
+
// A start-sink rejection identical to the applicable abort
|
|
2085
|
+
// reason is the cancellation itself: the pair finishes
|
|
2086
|
+
// `aborted` and nothing is reported (slc/link.md §Abort).
|
|
2087
|
+
const controlError = active.aborts.isAbortReason(error)
|
|
2088
|
+
? undefined
|
|
2089
|
+
: error;
|
|
2090
|
+
if (controlError !== undefined) {
|
|
2091
|
+
reportControlPlaneError(controlError, active.aborts);
|
|
2092
|
+
}
|
|
1885
2093
|
return await finishImmediate(
|
|
1886
2094
|
active,
|
|
1887
2095
|
resultFromThrown(
|
|
1888
2096
|
normalizedInput.playbookId,
|
|
1889
2097
|
undefined,
|
|
1890
2098
|
error,
|
|
1891
|
-
|
|
2099
|
+
controlError === undefined,
|
|
1892
2100
|
),
|
|
1893
|
-
|
|
2101
|
+
controlError,
|
|
1894
2102
|
);
|
|
1895
2103
|
}
|
|
1896
2104
|
try {
|
|
1897
|
-
await options.drain();
|
|
2105
|
+
await options.drain(active.aborts);
|
|
1898
2106
|
} catch (error) {
|
|
1899
|
-
|
|
2107
|
+
const controlError = active.aborts.isAbortReason(error)
|
|
2108
|
+
? undefined
|
|
2109
|
+
: error;
|
|
2110
|
+
if (controlError !== undefined) {
|
|
2111
|
+
reportControlPlaneError(controlError, active.aborts);
|
|
2112
|
+
}
|
|
1900
2113
|
return await finishImmediate(
|
|
1901
2114
|
active,
|
|
1902
2115
|
resultFromThrown(
|
|
1903
2116
|
normalizedInput.playbookId,
|
|
1904
2117
|
undefined,
|
|
1905
2118
|
error,
|
|
1906
|
-
|
|
2119
|
+
controlError === undefined,
|
|
1907
2120
|
),
|
|
1908
|
-
|
|
2121
|
+
controlError,
|
|
1909
2122
|
);
|
|
1910
2123
|
}
|
|
1911
2124
|
|
|
@@ -1934,7 +2147,7 @@ export function createNestedPlaybookBridge<
|
|
|
1934
2147
|
const openingCleanup = starting.then(
|
|
1935
2148
|
() => undefined,
|
|
1936
2149
|
(error: unknown) => {
|
|
1937
|
-
if (isAbortReason(error
|
|
2150
|
+
if (active.aborts.isAbortReason(error)) return;
|
|
1938
2151
|
throw error;
|
|
1939
2152
|
},
|
|
1940
2153
|
);
|
|
@@ -1951,19 +2164,23 @@ export function createNestedPlaybookBridge<
|
|
|
1951
2164
|
}
|
|
1952
2165
|
rawStart = await withAbort(starting, active.signal);
|
|
1953
2166
|
} catch (error) {
|
|
1954
|
-
const controlError = active.
|
|
1955
|
-
|
|
2167
|
+
const controlError = active.aborts.isAbortReason(error)
|
|
2168
|
+
? undefined
|
|
2169
|
+
: error;
|
|
2170
|
+
if (controlError !== undefined) {
|
|
2171
|
+
reportControlPlaneError(controlError, active.aborts);
|
|
2172
|
+
}
|
|
1956
2173
|
const result = resultFromThrown(
|
|
1957
2174
|
normalizedInput.playbookId,
|
|
1958
2175
|
undefined,
|
|
1959
2176
|
error,
|
|
1960
|
-
active.signal.aborted,
|
|
2177
|
+
controlError === undefined && active.signal.aborted,
|
|
1961
2178
|
);
|
|
1962
2179
|
return await finishImmediate(
|
|
1963
2180
|
active,
|
|
1964
2181
|
result,
|
|
1965
2182
|
controlError,
|
|
1966
|
-
active.signal.aborted
|
|
2183
|
+
controlError === undefined && active.signal.aborted
|
|
1967
2184
|
? () =>
|
|
1968
2185
|
resultFromThrown(
|
|
1969
2186
|
normalizedInput.playbookId,
|
|
@@ -2140,9 +2357,7 @@ export function createNestedPlaybookBridge<
|
|
|
2140
2357
|
}
|
|
2141
2358
|
const active = mode.active;
|
|
2142
2359
|
if (active.signal.aborted) {
|
|
2143
|
-
const error =
|
|
2144
|
-
active.signal.reason ??
|
|
2145
|
-
new Error('Restored nested playbook invocation aborted');
|
|
2360
|
+
const error = active.signal.reason;
|
|
2146
2361
|
rollbackRestoredCall(mode, error);
|
|
2147
2362
|
restoreMode = undefined;
|
|
2148
2363
|
throw error;
|
|
@@ -2167,7 +2382,7 @@ export function createNestedPlaybookBridge<
|
|
|
2167
2382
|
try {
|
|
2168
2383
|
listener(pendingCall);
|
|
2169
2384
|
} catch (error) {
|
|
2170
|
-
reportBackgroundError(error);
|
|
2385
|
+
reportBackgroundError(error, current?.aborts);
|
|
2171
2386
|
}
|
|
2172
2387
|
}
|
|
2173
2388
|
return () => pendingListeners.delete(listener);
|
|
@@ -2209,8 +2424,20 @@ export function createNestedPlaybookBridge<
|
|
|
2209
2424
|
}
|
|
2210
2425
|
throw error;
|
|
2211
2426
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2427
|
+
// A resume whose signal is already aborted delivers nothing: the
|
|
2428
|
+
// validated child result is not consumed, no finish is emitted, and
|
|
2429
|
+
// the pending call survives for a later resume with a fresh signal
|
|
2430
|
+
// (slc/link.md §Nested playbook bridge). Identity and validation
|
|
2431
|
+
// control errors above still win — they are the caller's defects.
|
|
2432
|
+
if (signal.aborted) {
|
|
2433
|
+
throw signal.reason;
|
|
2434
|
+
}
|
|
2435
|
+
const resumeAborts = createAbortReasonClassifier(
|
|
2436
|
+
active.aborts,
|
|
2437
|
+
signal,
|
|
2438
|
+
);
|
|
2439
|
+
options.bindResumeSignal?.(signal, resumeAborts);
|
|
2440
|
+
await settlePending(active, validatedResult, undefined, resumeAborts);
|
|
2214
2441
|
},
|
|
2215
2442
|
abortPending,
|
|
2216
2443
|
async dispose() {
|