@peerbit/trusted-network 6.0.124 → 6.0.126

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.
@@ -58,6 +58,7 @@ export const TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES =
58
58
  MAX_CHECKPOINT_PAYLOAD_FRAMING_BYTES_V2;
59
59
  const MAX_QUEUED_POLICY_INPUT_BYTES_V2 =
60
60
  TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES * 2;
61
+ const MAX_TIMER_DELAY_MS_V2 = 0x7fffffff;
61
62
  const ZERO_DIGEST = new Uint8Array(32);
62
63
  const textEncoder = new TextEncoder();
63
64
  const CHECKPOINT_SCOPE_DOMAIN = textEncoder.encode(
@@ -69,6 +70,20 @@ const ANCHOR_STATE = Object.freeze({
69
70
  FORKED: 3,
70
71
  } as const);
71
72
 
73
+ const isAbortSignalV2 = (value: unknown): value is AbortSignal => {
74
+ if (value === null || typeof value !== "object") return false;
75
+ try {
76
+ const signal = value as AbortSignal;
77
+ return (
78
+ typeof signal.aborted === "boolean" &&
79
+ typeof signal.addEventListener === "function" &&
80
+ typeof signal.removeEventListener === "function"
81
+ );
82
+ } catch {
83
+ return false;
84
+ }
85
+ };
86
+
72
87
  export type CrashSafePolicyAnchorStoreV2 = CrashSafeAtomicReplaceStore;
73
88
 
74
89
  type DurableReducerOptionsV2 = {
@@ -88,6 +103,14 @@ export type TrustedNetworkV2DurablePolicyReducerOptions =
88
103
  export type PolicyLeaseReferenceV2 = {
89
104
  sequence: bigint;
90
105
  digest: Uint8Array;
106
+ /** Maximum number of authenticated parent edges traversed for this lease. */
107
+ maxSteps?: number;
108
+ /** Absolute wall-clock deadline, in milliseconds since the Unix epoch. */
109
+ deadline?: number;
110
+ /** Relative deadline measured from the call, including operation-queue wait. */
111
+ timeoutMs?: number;
112
+ /** Cancels acquisition of this lease without halting the durable reducer. */
113
+ signal?: AbortSignal;
91
114
  };
92
115
 
93
116
  export type AcceptedPolicyLeaseV2 = {
@@ -865,10 +888,23 @@ export class TrustedNetworkV2DurablePolicyReducer {
865
888
  return this.published.roles.get(keyId(subject)) ?? 0;
866
889
  }
867
890
 
891
+ /**
892
+ * Whether the last durable policy projection is ACTIVE with no replacement
893
+ * publication in flight.
894
+ *
895
+ * This is the subject-independent form of the publication fence used by
896
+ * `isAuthorized()`. Internal resource reducers use it to stop serving an
897
+ * otherwise ACTIVE resource while a policy replacement is in flight or the
898
+ * policy anchor has failed closed. It does not prove complete external-log
899
+ * replay or global freshness.
900
+ */
901
+ isUsable(): boolean {
902
+ return this.authorizationFences === 0 && this.state === "ACTIVE";
903
+ }
904
+
868
905
  isAuthorized(subject: PublicSignKey, roles: number): boolean {
869
906
  if (
870
- this.authorizationFences !== 0 ||
871
- this.state !== "ACTIVE" ||
907
+ !this.isUsable() ||
872
908
  !Number.isInteger(roles) ||
873
909
  roles === 0 ||
874
910
  (roles & ~TRUSTED_NETWORK_V2_KNOWN_ROLE_BITS) !== 0
@@ -991,6 +1027,9 @@ export class TrustedNetworkV2DurablePolicyReducer {
991
1027
 
992
1028
  let sequence: bigint;
993
1029
  let suppliedDigest: Uint8Array;
1030
+ let maxSteps: number | undefined;
1031
+ let deadline: number | undefined;
1032
+ let signal: AbortSignal | undefined;
994
1033
  try {
995
1034
  sequence = reference.sequence;
996
1035
  if (
@@ -1010,6 +1049,54 @@ export class TrustedNetworkV2DurablePolicyReducer {
1010
1049
  reason: "Policy reference digest must contain exactly 32 bytes",
1011
1050
  });
1012
1051
  }
1052
+ maxSteps = reference.maxSteps;
1053
+ if (
1054
+ maxSteps !== undefined &&
1055
+ (!Number.isSafeInteger(maxSteps) || maxSteps < 0)
1056
+ ) {
1057
+ return Promise.resolve({
1058
+ status: "rejected",
1059
+ reason: "Policy lease maxSteps must be a non-negative safe integer",
1060
+ });
1061
+ }
1062
+ const suppliedDeadline = reference.deadline;
1063
+ if (
1064
+ suppliedDeadline !== undefined &&
1065
+ (!Number.isSafeInteger(suppliedDeadline) || suppliedDeadline < 0)
1066
+ ) {
1067
+ return Promise.resolve({
1068
+ status: "rejected",
1069
+ reason: "Policy lease deadline must be a non-negative safe integer",
1070
+ });
1071
+ }
1072
+ const timeoutMs = reference.timeoutMs;
1073
+ if (
1074
+ timeoutMs !== undefined &&
1075
+ (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0)
1076
+ ) {
1077
+ return Promise.resolve({
1078
+ status: "rejected",
1079
+ reason: "Policy lease timeoutMs must be a non-negative safe integer",
1080
+ });
1081
+ }
1082
+ const requestedAt = Date.now();
1083
+ const relativeDeadline =
1084
+ timeoutMs === undefined
1085
+ ? undefined
1086
+ : Math.min(Number.MAX_SAFE_INTEGER, requestedAt + timeoutMs);
1087
+ deadline =
1088
+ suppliedDeadline === undefined
1089
+ ? relativeDeadline
1090
+ : relativeDeadline === undefined
1091
+ ? suppliedDeadline
1092
+ : Math.min(suppliedDeadline, relativeDeadline);
1093
+ signal = reference.signal;
1094
+ if (signal !== undefined && !isAbortSignalV2(signal)) {
1095
+ return Promise.resolve({
1096
+ status: "rejected",
1097
+ reason: "Policy lease signal must be an AbortSignal",
1098
+ });
1099
+ }
1013
1100
  } catch {
1014
1101
  return Promise.resolve({
1015
1102
  status: "rejected",
@@ -1041,20 +1128,120 @@ export class TrustedNetworkV2DurablePolicyReducer {
1041
1128
  });
1042
1129
  }
1043
1130
 
1044
- const result: Promise<PolicyLeaseResultV2<T>> = this.operationTail.then(
1045
- async () => {
1131
+ let queueAcquired = false;
1132
+ let callbackAcquired = false;
1133
+ let preAcquisitionResult: PolicyLeaseResultV2<T> | undefined;
1134
+ let resolveEarly!: (result: PolicyLeaseResultV2<T>) => void;
1135
+ const earlyResult = new Promise<PolicyLeaseResultV2<T>>((resolve) => {
1136
+ resolveEarly = resolve;
1137
+ });
1138
+ let deadlineTimer: ReturnType<typeof setTimeout> | undefined;
1139
+ let signalListenerInstalled = false;
1140
+
1141
+ const abortBeforeAcquisition = (): void => {
1142
+ settleBeforeAcquisition({
1143
+ status: "unavailable",
1144
+ reason: "Policy lease acquisition was aborted by the caller",
1145
+ });
1146
+ };
1147
+ const cleanupWakeups = (): void => {
1148
+ if (deadlineTimer !== undefined) {
1149
+ clearTimeout(deadlineTimer);
1150
+ deadlineTimer = undefined;
1151
+ }
1152
+ if (signalListenerInstalled) {
1153
+ signalListenerInstalled = false;
1154
+ try {
1155
+ signal?.removeEventListener("abort", abortBeforeAcquisition);
1156
+ } catch {
1157
+ // A structurally valid hostile signal must not break queue cleanup.
1158
+ }
1159
+ }
1160
+ };
1161
+ function settleBeforeAcquisition(result: PolicyLeaseResultV2<T>): void {
1162
+ if (
1163
+ queueAcquired ||
1164
+ callbackAcquired ||
1165
+ preAcquisitionResult !== undefined
1166
+ ) {
1167
+ return;
1168
+ }
1169
+ preAcquisitionResult = result;
1170
+ cleanupWakeups();
1171
+ resolveEarly(result);
1172
+ }
1173
+ const armDeadlineWakeup = (): void => {
1174
+ if (
1175
+ deadline === undefined ||
1176
+ queueAcquired ||
1177
+ callbackAcquired ||
1178
+ preAcquisitionResult !== undefined
1179
+ ) {
1180
+ return;
1181
+ }
1182
+ const remaining = deadline - Date.now();
1183
+ if (remaining <= 0) {
1184
+ settleBeforeAcquisition({
1185
+ status: "unavailable",
1186
+ reason: "Policy lease acquisition deadline elapsed",
1187
+ });
1188
+ return;
1189
+ }
1190
+ try {
1191
+ deadlineTimer = setTimeout(
1192
+ () => {
1193
+ deadlineTimer = undefined;
1194
+ armDeadlineWakeup();
1195
+ },
1196
+ Math.min(remaining, MAX_TIMER_DELAY_MS_V2),
1197
+ );
1198
+ } catch {
1199
+ settleBeforeAcquisition({
1200
+ status: "unavailable",
1201
+ reason: "Policy lease acquisition deadline could not be scheduled",
1202
+ });
1203
+ }
1204
+ };
1205
+
1206
+ const queuedResult: Promise<PolicyLeaseResultV2<T>> =
1207
+ this.operationTail.then(async () => {
1208
+ if (preAcquisitionResult !== undefined) {
1209
+ return preAcquisitionResult;
1210
+ }
1211
+ queueAcquired = true;
1212
+ cleanupWakeups();
1046
1213
  const queuedHalt = this.immediateLeaseHaltedResult();
1047
1214
  if (queuedHalt !== undefined) return queuedHalt;
1048
- const resolution = await this.core.resolveAcceptedPolicyPrefix({
1049
- sequence,
1050
- digest,
1051
- });
1215
+ const resolution = await this.core.resolveAcceptedPolicyPrefix(
1216
+ {
1217
+ sequence,
1218
+ digest,
1219
+ },
1220
+ { maxSteps, deadline, signal },
1221
+ );
1222
+ if (preAcquisitionResult !== undefined) {
1223
+ return preAcquisitionResult;
1224
+ }
1052
1225
  if (resolution.status !== "resolved") return resolution;
1053
1226
  // Lifecycle abort before callback invocation loses acquisition. There is
1054
1227
  // no await between this check and invoking user code, which is the lease's
1055
1228
  // linearization point.
1056
1229
  const resolvedHalt = this.immediateLeaseHaltedResult();
1057
1230
  if (resolvedHalt !== undefined) return resolvedHalt;
1231
+ if (signal?.aborted) {
1232
+ return {
1233
+ status: "unavailable",
1234
+ reason: "Policy lease acquisition was aborted by the caller",
1235
+ };
1236
+ }
1237
+ if (deadline !== undefined && Date.now() >= deadline) {
1238
+ return {
1239
+ status: "unavailable",
1240
+ reason: "Policy lease acquisition deadline elapsed",
1241
+ };
1242
+ }
1243
+ callbackAcquired = true;
1244
+ cleanupWakeups();
1058
1245
  const value = await use({
1059
1246
  // The core created these two projections independently and retains
1060
1247
  // neither, including when the requested policy is the current head.
@@ -1062,15 +1249,32 @@ export class TrustedNetworkV2DurablePolicyReducer {
1062
1249
  acceptedHead: resolution.acceptedHead,
1063
1250
  });
1064
1251
  return { status: "completed", value };
1065
- },
1066
- );
1067
- this.operationTail = result.then(
1252
+ });
1253
+ const retainedResult = queuedResult.finally(() => {
1254
+ cleanupWakeups();
1255
+ this.releaseOperation(retainedReferenceBytes);
1256
+ });
1257
+ this.operationTail = retainedResult.then(
1068
1258
  (): void => {},
1069
1259
  (): void => {},
1070
1260
  );
1071
- return result.finally(() => {
1072
- this.releaseOperation(retainedReferenceBytes);
1073
- });
1261
+
1262
+ if (signal !== undefined) {
1263
+ signalListenerInstalled = true;
1264
+ try {
1265
+ signal.addEventListener("abort", abortBeforeAcquisition, {
1266
+ once: true,
1267
+ });
1268
+ } catch {
1269
+ settleBeforeAcquisition({
1270
+ status: "rejected",
1271
+ reason: "Policy lease signal could not be observed",
1272
+ });
1273
+ }
1274
+ if (signal.aborted) abortBeforeAcquisition();
1275
+ }
1276
+ armDeadlineWakeup();
1277
+ return Promise.race([retainedResult, earlyResult]);
1074
1278
  }
1075
1279
 
1076
1280
  private forkFailStopResult(): PolicyAdmissionResultV2 {
@@ -47,6 +47,20 @@ const TYPED_ARRAY_TAG = Object.getOwnPropertyDescriptor(
47
47
  const ARRAY_BUFFER_IS_VIEW = ArrayBuffer.isView;
48
48
  const UINT8_ARRAY_SET = Uint8Array.prototype.set;
49
49
 
50
+ const isAbortSignalV2 = (value: unknown): value is AbortSignal => {
51
+ if (value === null || typeof value !== "object") return false;
52
+ try {
53
+ const signal = value as AbortSignal;
54
+ return (
55
+ typeof signal.aborted === "boolean" &&
56
+ typeof signal.addEventListener === "function" &&
57
+ typeof signal.removeEventListener === "function"
58
+ );
59
+ } catch {
60
+ return false;
61
+ }
62
+ };
63
+
50
64
  const exactUint8ArrayByteLength = (input: unknown): number => {
51
65
  if (
52
66
  !ARRAY_BUFFER_IS_VIEW(input) ||
@@ -162,6 +176,16 @@ export type AcceptedPolicyPrefixResolutionV2 =
162
176
  reason: string;
163
177
  };
164
178
 
179
+ /** Per-call bounds for the internal accepted-prefix history walk. */
180
+ export type AcceptedPolicyPrefixTraversalV2 = {
181
+ /** Maximum number of authenticated parent edges that may be resolved. */
182
+ maxSteps?: number;
183
+ /** Absolute wall-clock deadline, in milliseconds since the Unix epoch. */
184
+ deadline?: number;
185
+ /** Cancels this resolution without halting the reducer. */
186
+ signal?: AbortSignal;
187
+ };
188
+
165
189
  export type PolicyForkChildProofV2 = {
166
190
  sequence: bigint;
167
191
  digest: Uint8Array;
@@ -751,12 +775,16 @@ export class TrustedNetworkV2PolicyReducer {
751
775
  * users of this internal class. Historical snapshots remain resolver-backed;
752
776
  * this walk retains only one authenticated cursor at a time.
753
777
  */
754
- resolveAcceptedPolicyPrefix(reference: {
755
- sequence: bigint;
756
- digest: Uint8Array;
757
- }): Promise<AcceptedPolicyPrefixResolutionV2> {
778
+ resolveAcceptedPolicyPrefix(
779
+ reference: {
780
+ sequence: bigint;
781
+ digest: Uint8Array;
782
+ },
783
+ traversal?: AcceptedPolicyPrefixTraversalV2,
784
+ ): Promise<AcceptedPolicyPrefixResolutionV2> {
758
785
  let capturedSequence: bigint;
759
786
  let capturedDigest: Uint8Array;
787
+ let capturedTraversal: AcceptedPolicyPrefixTraversalV2;
760
788
  try {
761
789
  capturedSequence = reference.sequence;
762
790
  if (
@@ -774,6 +802,34 @@ export class TrustedNetworkV2PolicyReducer {
774
802
  throw new Error("invalid digest");
775
803
  }
776
804
  capturedDigest = copyBytes(suppliedDigest);
805
+ const maxSteps = traversal?.maxSteps;
806
+ if (
807
+ maxSteps !== undefined &&
808
+ (!Number.isSafeInteger(maxSteps) || maxSteps < 0)
809
+ ) {
810
+ return Promise.resolve({
811
+ status: "rejected",
812
+ reason: "Policy prefix maxSteps must be a non-negative safe integer",
813
+ });
814
+ }
815
+ const deadline = traversal?.deadline;
816
+ if (
817
+ deadline !== undefined &&
818
+ (!Number.isSafeInteger(deadline) || deadline < 0)
819
+ ) {
820
+ return Promise.resolve({
821
+ status: "rejected",
822
+ reason: "Policy prefix deadline must be a non-negative safe integer",
823
+ });
824
+ }
825
+ const signal = traversal?.signal;
826
+ if (signal !== undefined && !isAbortSignalV2(signal)) {
827
+ return Promise.resolve({
828
+ status: "rejected",
829
+ reason: "Policy prefix signal must be an AbortSignal",
830
+ });
831
+ }
832
+ capturedTraversal = { maxSteps, deadline, signal };
777
833
  } catch {
778
834
  return Promise.resolve({
779
835
  status: "rejected",
@@ -781,10 +837,13 @@ export class TrustedNetworkV2PolicyReducer {
781
837
  });
782
838
  }
783
839
  const result = this.admissionTail.then(() =>
784
- this.resolveAcceptedPolicyPrefixOne({
785
- sequence: capturedSequence,
786
- digest: capturedDigest,
787
- }),
840
+ this.resolveAcceptedPolicyPrefixOne(
841
+ {
842
+ sequence: capturedSequence,
843
+ digest: capturedDigest,
844
+ },
845
+ capturedTraversal,
846
+ ),
788
847
  );
789
848
  this.admissionTail = result.then(
790
849
  (): void => {},
@@ -808,19 +867,15 @@ export class TrustedNetworkV2PolicyReducer {
808
867
  this.lifecycleController.abort();
809
868
  }
810
869
 
811
- private async resolveAcceptedPolicyPrefixOne(reference: {
812
- sequence: bigint;
813
- digest: Uint8Array;
814
- }): Promise<AcceptedPolicyPrefixResolutionV2> {
815
- if (this.lifecycleController.signal.aborted || this.fork !== undefined) {
816
- return {
817
- status: "halted",
818
- reason:
819
- this.fork === undefined
820
- ? "Policy reducer lifecycle is aborted"
821
- : "Policy reducer is halted by authority equivocation",
822
- };
823
- }
870
+ private async resolveAcceptedPolicyPrefixOne(
871
+ reference: {
872
+ sequence: bigint;
873
+ digest: Uint8Array;
874
+ },
875
+ traversal: AcceptedPolicyPrefixTraversalV2,
876
+ ): Promise<AcceptedPolicyPrefixResolutionV2> {
877
+ const interrupted = this.acceptedPrefixInterruption(traversal);
878
+ if (interrupted !== undefined) return interrupted;
824
879
  if (this.unavailable !== undefined) {
825
880
  return {
826
881
  status: "unavailable",
@@ -843,7 +898,10 @@ export class TrustedNetworkV2PolicyReducer {
843
898
  // The admission queue owns the core for this complete walk, so the internal
844
899
  // accepted snapshot cannot change until the resolution settles.
845
900
  let cursor = this.acceptedHead;
901
+ let steps = 0;
846
902
  while (cursor.body.sequence > reference.sequence) {
903
+ const loopInterruption = this.acceptedPrefixInterruption(traversal);
904
+ if (loopInterruption !== undefined) return loopInterruption;
847
905
  if (
848
906
  cursor.body.sequence === reference.sequence + 1n &&
849
907
  !equals(cursor.body.previousPolicyDigest, reference.digest)
@@ -853,13 +911,16 @@ export class TrustedNetworkV2PolicyReducer {
853
911
  reason: "Policy reference is not an accepted prefix",
854
912
  };
855
913
  }
856
- const parent = await this.parentOf(cursor);
857
- if (this.lifecycleController.signal.aborted) {
914
+ if (traversal.maxSteps !== undefined && steps >= traversal.maxSteps) {
858
915
  return {
859
- status: "halted",
860
- reason: "Policy reducer lifecycle is aborted",
916
+ status: "unavailable",
917
+ reason: "Accepted policy prefix traversal reached maxSteps",
861
918
  };
862
919
  }
920
+ steps += 1;
921
+ const parent = await this.parentOf(cursor, undefined, traversal);
922
+ const resolutionInterruption = this.acceptedPrefixInterruption(traversal);
923
+ if (resolutionInterruption !== undefined) return resolutionInterruption;
863
924
  if (parent.status !== "found") {
864
925
  return {
865
926
  status: "unavailable",
@@ -868,6 +929,8 @@ export class TrustedNetworkV2PolicyReducer {
868
929
  }
869
930
  cursor = parent.parent;
870
931
  }
932
+ const completedInterruption = this.acceptedPrefixInterruption(traversal);
933
+ if (completedInterruption !== undefined) return completedInterruption;
871
934
 
872
935
  if (!equals(cursor.digest, reference.digest)) {
873
936
  return {
@@ -882,6 +945,38 @@ export class TrustedNetworkV2PolicyReducer {
882
945
  };
883
946
  }
884
947
 
948
+ private acceptedPrefixInterruption(
949
+ traversal: AcceptedPolicyPrefixTraversalV2,
950
+ ):
951
+ | Exclude<AcceptedPolicyPrefixResolutionV2, { status: "resolved" }>
952
+ | undefined {
953
+ if (this.lifecycleController.signal.aborted) {
954
+ return {
955
+ status: "halted",
956
+ reason: "Policy reducer lifecycle is aborted",
957
+ };
958
+ }
959
+ if (this.fork !== undefined) {
960
+ return {
961
+ status: "halted",
962
+ reason: "Policy reducer is halted by authority equivocation",
963
+ };
964
+ }
965
+ if (traversal.signal?.aborted) {
966
+ return {
967
+ status: "unavailable",
968
+ reason: "Policy prefix resolution was aborted by the caller",
969
+ };
970
+ }
971
+ if (traversal.deadline !== undefined && Date.now() >= traversal.deadline) {
972
+ return {
973
+ status: "unavailable",
974
+ reason: "Policy prefix resolution deadline elapsed",
975
+ };
976
+ }
977
+ return undefined;
978
+ }
979
+
885
980
  private fetchHints(): PolicyParentFetchHintV2[] {
886
981
  const unique = new Map<string, Uint8Array>();
887
982
  for (const { missingParentDigest } of this.pending.values()) {
@@ -994,69 +1089,111 @@ export class TrustedNetworkV2PolicyReducer {
994
1089
 
995
1090
  private async resolveExternalSnapshot(
996
1091
  digest: Uint8Array,
1092
+ traversal?: AcceptedPolicyPrefixTraversalV2,
997
1093
  ): Promise<ValidatedPolicySnapshotV2 | undefined> {
998
1094
  if (this.lifecycleController.signal.aborted) {
999
1095
  throw new PolicyDependencyUnavailableErrorV2(
1000
1096
  "Policy resolver lifecycle is aborted",
1001
1097
  );
1002
1098
  }
1099
+ if (traversal?.signal?.aborted) {
1100
+ throw new PolicyDependencyUnavailableErrorV2(
1101
+ "Policy resolver attempt was aborted by the caller",
1102
+ );
1103
+ }
1104
+ const deadlineRemaining =
1105
+ traversal?.deadline === undefined
1106
+ ? undefined
1107
+ : traversal.deadline - Date.now();
1108
+ const deadlineLimitsAttempt =
1109
+ deadlineRemaining !== undefined &&
1110
+ deadlineRemaining <= this.resolveTimeoutMs;
1111
+ const remaining =
1112
+ deadlineRemaining === undefined
1113
+ ? this.resolveTimeoutMs
1114
+ : Math.min(this.resolveTimeoutMs, deadlineRemaining);
1115
+ if (remaining <= 0) {
1116
+ throw new PolicyDependencyUnavailableErrorV2(
1117
+ "Policy resolver deadline elapsed",
1118
+ );
1119
+ }
1003
1120
 
1004
1121
  const attemptController = new AbortController();
1005
1122
  let timedOut = false;
1123
+ let callerAborted = false;
1006
1124
  const abortFromLifecycle = (): void => attemptController.abort();
1125
+ const abortFromCaller = (): void => {
1126
+ callerAborted = true;
1127
+ attemptController.abort();
1128
+ };
1007
1129
  this.lifecycleController.signal.addEventListener(
1008
1130
  "abort",
1009
1131
  abortFromLifecycle,
1010
1132
  { once: true },
1011
1133
  );
1134
+ traversal?.signal?.addEventListener("abort", abortFromCaller, {
1135
+ once: true,
1136
+ });
1012
1137
  const timeout = setTimeout(() => {
1013
1138
  timedOut = true;
1014
1139
  attemptController.abort();
1015
- }, this.resolveTimeoutMs);
1140
+ }, remaining);
1016
1141
  const abortError = (): PolicyDependencyUnavailableErrorV2 =>
1017
1142
  new PolicyDependencyUnavailableErrorV2(
1018
- timedOut
1019
- ? `Policy resolver timed out after ${this.resolveTimeoutMs} ms`
1020
- : "Policy resolver attempt was aborted",
1143
+ callerAborted
1144
+ ? "Policy resolver attempt was aborted by the caller"
1145
+ : timedOut && deadlineLimitsAttempt
1146
+ ? "Policy resolver deadline elapsed"
1147
+ : timedOut
1148
+ ? `Policy resolver timed out after ${this.resolveTimeoutMs} ms`
1149
+ : "Policy resolver attempt was aborted",
1021
1150
  );
1022
1151
 
1023
1152
  let rejectOnAbort: (() => void) | undefined;
1024
- const abortPromise = new Promise<never>((_resolve, reject) => {
1025
- rejectOnAbort = (): void => {
1026
- reject(abortError());
1027
- };
1028
- attemptController.signal.addEventListener("abort", rejectOnAbort, {
1029
- once: true,
1030
- });
1031
- });
1032
-
1033
- const resolution = Promise.resolve().then(async () => {
1034
- if (attemptController.signal.aborted) throw abortError();
1035
- const entryBytes = await this.resolvePolicyEntry(copyBytes(digest), {
1036
- signal: attemptController.signal,
1037
- });
1038
- // A resolver may ignore cancellation. Do not spend decode or signature
1039
- // verification work on bytes that arrive after this attempt expired.
1040
- if (attemptController.signal.aborted) throw abortError();
1041
- if (entryBytes === undefined) return undefined;
1042
- const snapshot = await authenticatePolicySnapshotEntryV2(
1043
- entryBytes,
1044
- this.descriptor,
1045
- );
1046
- if (!equals(snapshot.digest, digest)) {
1047
- throw new Error("Policy resolver returned the wrong body digest");
1048
- }
1049
- return snapshot;
1050
- });
1051
- // Promise.race installs handlers, but this explicit observer documents and
1052
- // preserves consumption if the resolver settles after its deadline.
1053
- void resolution.then(
1054
- (): void => undefined,
1055
- (): void => undefined,
1056
- );
1057
1153
 
1058
1154
  try {
1059
- return await Promise.race([resolution, abortPromise]);
1155
+ return await new Promise<ValidatedPolicySnapshotV2 | undefined>(
1156
+ (resolve, reject) => {
1157
+ let settled = false;
1158
+ const settle = (complete: () => void): void => {
1159
+ if (settled) return;
1160
+ settled = true;
1161
+ complete();
1162
+ };
1163
+ rejectOnAbort = (): void => settle(() => reject(abortError()));
1164
+ attemptController.signal.addEventListener("abort", rejectOnAbort, {
1165
+ once: true,
1166
+ });
1167
+ void Promise.resolve()
1168
+ .then(async () => {
1169
+ if (attemptController.signal.aborted) throw abortError();
1170
+ const entryBytes = await this.resolvePolicyEntry(
1171
+ copyBytes(digest),
1172
+ {
1173
+ signal: attemptController.signal,
1174
+ },
1175
+ );
1176
+ // A resolver may ignore cancellation. Do not spend decode or signature
1177
+ // verification work on bytes that arrive after this attempt expired.
1178
+ if (attemptController.signal.aborted) throw abortError();
1179
+ if (entryBytes === undefined) return undefined;
1180
+ const snapshot = await authenticatePolicySnapshotEntryV2(
1181
+ entryBytes,
1182
+ this.descriptor,
1183
+ );
1184
+ if (!equals(snapshot.digest, digest)) {
1185
+ throw new Error(
1186
+ "Policy resolver returned the wrong body digest",
1187
+ );
1188
+ }
1189
+ return snapshot;
1190
+ })
1191
+ .then(
1192
+ (value) => settle(() => resolve(value)),
1193
+ (error: unknown) => settle(() => reject(error)),
1194
+ );
1195
+ },
1196
+ );
1060
1197
  } catch (error) {
1061
1198
  if (error instanceof PolicyDependencyUnavailableErrorV2) throw error;
1062
1199
  throw new PolicyDependencyUnavailableErrorV2(
@@ -1068,6 +1205,7 @@ export class TrustedNetworkV2PolicyReducer {
1068
1205
  "abort",
1069
1206
  abortFromLifecycle,
1070
1207
  );
1208
+ traversal?.signal?.removeEventListener("abort", abortFromCaller);
1071
1209
  if (rejectOnAbort !== undefined) {
1072
1210
  attemptController.signal.removeEventListener("abort", rejectOnAbort);
1073
1211
  }
@@ -1077,6 +1215,7 @@ export class TrustedNetworkV2PolicyReducer {
1077
1215
  private async resolveSnapshot(
1078
1216
  digest: Uint8Array,
1079
1217
  cache?: SnapshotResolutionCacheV2,
1218
+ traversal?: AcceptedPolicyPrefixTraversalV2,
1080
1219
  ): Promise<ValidatedPolicySnapshotV2 | undefined> {
1081
1220
  const digestKey = bytesKey(digest);
1082
1221
  if (this.acceptedHead?.digestKey === digestKey) {
@@ -1086,7 +1225,7 @@ export class TrustedNetworkV2PolicyReducer {
1086
1225
  if (pending !== undefined) return copySnapshot(pending.snapshot);
1087
1226
  let resolution = cache?.get(digestKey);
1088
1227
  if (resolution === undefined) {
1089
- resolution = this.resolveExternalSnapshot(digest);
1228
+ resolution = this.resolveExternalSnapshot(digest, traversal);
1090
1229
  cache?.set(digestKey, resolution);
1091
1230
  }
1092
1231
  const snapshot = await resolution;
@@ -1096,6 +1235,7 @@ export class TrustedNetworkV2PolicyReducer {
1096
1235
  private async parentOf(
1097
1236
  child: ValidatedPolicySnapshotV2,
1098
1237
  cache?: SnapshotResolutionCacheV2,
1238
+ traversal?: AcceptedPolicyPrefixTraversalV2,
1099
1239
  ): Promise<ParentResolutionV2> {
1100
1240
  if (child.body.sequence === 0n) {
1101
1241
  return {
@@ -1109,6 +1249,7 @@ export class TrustedNetworkV2PolicyReducer {
1109
1249
  parent = await this.resolveSnapshot(
1110
1250
  child.body.previousPolicyDigest,
1111
1251
  cache,
1252
+ traversal,
1112
1253
  );
1113
1254
  } catch (error) {
1114
1255
  return {