@private.me/xbind 3.0.2 → 3.0.3

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.
Files changed (221) hide show
  1. package/README.md +2366 -204
  2. package/README.md.backup +2121 -0
  3. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
  4. package/dist-standalone/_deps/shared/cjs/errors.js +1 -729
  5. package/dist-standalone/_deps/shared/cjs/index.js +1 -463
  6. package/dist-standalone/_deps/shared/cjs/types.js +1 -315
  7. package/dist-standalone/_deps/shared/errors.js +1 -244
  8. package/dist-standalone/_deps/shared/index.js +1 -72
  9. package/dist-standalone/_deps/shared/types.js +1 -86
  10. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  20. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  21. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  28. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  29. package/dist-standalone/_deps/xchange/errors.js +1 -1
  30. package/dist-standalone/_deps/xchange/index.js +1 -1
  31. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  32. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  33. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  34. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  40. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  41. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  42. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  43. package/dist-standalone/_deps/xregistry/index.js +1 -1
  44. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  45. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  46. package/dist-standalone/_deps/xregistry/types.js +1 -1
  47. package/dist-standalone/agent-call.d.ts +2 -2
  48. package/dist-standalone/agent-call.js +1 -659
  49. package/dist-standalone/agent-sdk.js +1 -328
  50. package/dist-standalone/agent.d.ts +2 -0
  51. package/dist-standalone/agent.js +1 -1800
  52. package/dist-standalone/approval.js +1 -193
  53. package/dist-standalone/async-iterators.d.ts +3 -3
  54. package/dist-standalone/async-iterators.js +1 -382
  55. package/dist-standalone/auth.js +1 -219
  56. package/dist-standalone/auto-accept.js +1 -229
  57. package/dist-standalone/backup-config.js +1 -201
  58. package/dist-standalone/backup.js +1 -326
  59. package/dist-standalone/batch-operations.js +1 -388
  60. package/dist-standalone/cancellation.js +1 -477
  61. package/dist-standalone/checkpoint.js +1 -186
  62. package/dist-standalone/circuit-breaker.js +1 -468
  63. package/dist-standalone/cjs/agent-call.js +1 -701
  64. package/dist-standalone/cjs/agent-sdk.js +1 -332
  65. package/dist-standalone/cjs/agent.js +1 -1837
  66. package/dist-standalone/cjs/approval.js +1 -199
  67. package/dist-standalone/cjs/async-iterators.js +1 -392
  68. package/dist-standalone/cjs/auth.js +1 -225
  69. package/dist-standalone/cjs/auto-accept.js +1 -233
  70. package/dist-standalone/cjs/backup-config.js +1 -207
  71. package/dist-standalone/cjs/backup.js +1 -330
  72. package/dist-standalone/cjs/batch-operations.js +1 -397
  73. package/dist-standalone/cjs/cancellation.js +1 -490
  74. package/dist-standalone/cjs/checkpoint.js +1 -193
  75. package/dist-standalone/cjs/circuit-breaker.js +1 -476
  76. package/dist-standalone/cjs/cli/init.js +1 -492
  77. package/dist-standalone/cjs/config-validation.js +1 -522
  78. package/dist-standalone/cjs/connect.js +1 -312
  79. package/dist-standalone/cjs/connection-pool.js +1 -506
  80. package/dist-standalone/cjs/correlation-id.js +1 -339
  81. package/dist-standalone/cjs/crypto-utils.js +1 -176
  82. package/dist-standalone/cjs/debug-mode.js +1 -534
  83. package/dist-standalone/cjs/did-document.js +1 -101
  84. package/dist-standalone/cjs/did-privateme.js +1 -130
  85. package/dist-standalone/cjs/did-web.js +1 -201
  86. package/dist-standalone/cjs/discovery.js +1 -462
  87. package/dist-standalone/cjs/dual-mode.js +1 -251
  88. package/dist-standalone/cjs/email-templates.js +1 -313
  89. package/dist-standalone/cjs/email-transport.js +1 -239
  90. package/dist-standalone/cjs/envelope.js +1 -538
  91. package/dist-standalone/cjs/errors.js +1 -913
  92. package/dist-standalone/cjs/event-emitter.js +1 -461
  93. package/dist-standalone/cjs/gateway-state.js +1 -55
  94. package/dist-standalone/cjs/gateway-transport.js +1 -120
  95. package/dist-standalone/cjs/graceful-degradation.js +1 -403
  96. package/dist-standalone/cjs/guardrails.js +1 -223
  97. package/dist-standalone/cjs/health-check.js +1 -336
  98. package/dist-standalone/cjs/http-compat.js +1 -272
  99. package/dist-standalone/cjs/http-status-map.js +1 -571
  100. package/dist-standalone/cjs/identity.js +1 -645
  101. package/dist-standalone/cjs/index.js +1 -406
  102. package/dist-standalone/cjs/invitation.js +1 -421
  103. package/dist-standalone/cjs/invite.js +1 -328
  104. package/dist-standalone/cjs/key-agreement.js +1 -335
  105. package/dist-standalone/cjs/lazy-init.js +1 -300
  106. package/dist-standalone/cjs/logger.js +1 -291
  107. package/dist-standalone/cjs/loopback-transport.js +1 -0
  108. package/dist-standalone/cjs/mdns-discovery.js +1 -202
  109. package/dist-standalone/cjs/nonce-store.js +1 -80
  110. package/dist-standalone/cjs/pairing-manager.js +1 -223
  111. package/dist-standalone/cjs/plugin-system.js +1 -264
  112. package/dist-standalone/cjs/plugins/logging.js +1 -168
  113. package/dist-standalone/cjs/plugins/metrics.js +1 -181
  114. package/dist-standalone/cjs/plugins/validation.js +1 -302
  115. package/dist-standalone/cjs/policy.js +1 -320
  116. package/dist-standalone/cjs/progress-callbacks.js +1 -583
  117. package/dist-standalone/cjs/redis-nonce-store.js +1 -76
  118. package/dist-standalone/cjs/registry-middleware.js +1 -50
  119. package/dist-standalone/cjs/retry-strategies.js +1 -544
  120. package/dist-standalone/cjs/retry-transport.js +1 -102
  121. package/dist-standalone/cjs/runtime/browser.js +1 -533
  122. package/dist-standalone/cjs/runtime/edge.js +1 -526
  123. package/dist-standalone/cjs/runtime/react-native.js +1 -394
  124. package/dist-standalone/cjs/security-policy.js +1 -245
  125. package/dist-standalone/cjs/serialization.js +1 -1040
  126. package/dist-standalone/cjs/split-channel.js +1 -225
  127. package/dist-standalone/cjs/subscription-proof.js +1 -230
  128. package/dist-standalone/cjs/succession.js +1 -148
  129. package/dist-standalone/cjs/timeouts.js +1 -412
  130. package/dist-standalone/cjs/trace-context.js +1 -424
  131. package/dist-standalone/cjs/trace-spans.js +1 -495
  132. package/dist-standalone/cjs/transport.js +1 -63
  133. package/dist-standalone/cjs/trust-registry.js +1 -991
  134. package/dist-standalone/cjs/types/error-response.js +1 -56
  135. package/dist-standalone/cjs/vault-auth.js +1 -178
  136. package/dist-standalone/cjs/vault-store-loader.js +1 -194
  137. package/dist-standalone/cjs/verify.js +1 -25
  138. package/dist-standalone/cjs/version-info.js +1 -543
  139. package/dist-standalone/cjs/xfetch.js +1 -340
  140. package/dist-standalone/cli/init.js +1 -455
  141. package/dist-standalone/cli/setup.js +1 -514
  142. package/dist-standalone/cli/types.js +1 -27
  143. package/dist-standalone/cli/xbind.js +1 -148
  144. package/dist-standalone/config-validation.js +1 -513
  145. package/dist-standalone/connect.js +1 -274
  146. package/dist-standalone/connection-pool.js +1 -500
  147. package/dist-standalone/correlation-id.js +1 -326
  148. package/dist-standalone/crypto-utils.d.ts +2 -7
  149. package/dist-standalone/crypto-utils.js +1 -157
  150. package/dist-standalone/debug-mode.js +1 -510
  151. package/dist-standalone/did-document.js +1 -96
  152. package/dist-standalone/did-privateme.js +1 -121
  153. package/dist-standalone/did-web.js +1 -196
  154. package/dist-standalone/discovery.js +1 -458
  155. package/dist-standalone/dual-mode.js +1 -247
  156. package/dist-standalone/email-templates.js +1 -309
  157. package/dist-standalone/email-transport.d.ts +2 -2
  158. package/dist-standalone/email-transport.js +1 -232
  159. package/dist-standalone/envelope.js +1 -525
  160. package/dist-standalone/errors.d.ts +13 -3
  161. package/dist-standalone/errors.js +1 -896
  162. package/dist-standalone/event-emitter.js +1 -456
  163. package/dist-standalone/gateway-state.d.ts +1 -1
  164. package/dist-standalone/gateway-state.js +1 -51
  165. package/dist-standalone/gateway-transport.js +1 -116
  166. package/dist-standalone/graceful-degradation.js +1 -396
  167. package/dist-standalone/guardrails.js +1 -216
  168. package/dist-standalone/health-check.d.ts +5 -1
  169. package/dist-standalone/health-check.js +1 -332
  170. package/dist-standalone/http-compat.d.ts +1 -1
  171. package/dist-standalone/http-compat.js +1 -267
  172. package/dist-standalone/http-status-map.js +1 -561
  173. package/dist-standalone/identity.js +1 -619
  174. package/dist-standalone/index.d.ts +15 -4
  175. package/dist-standalone/index.js +1 -78
  176. package/dist-standalone/invitation.js +1 -415
  177. package/dist-standalone/invite.js +1 -324
  178. package/dist-standalone/key-agreement.js +1 -325
  179. package/dist-standalone/lazy-init.d.ts +11 -6
  180. package/dist-standalone/lazy-init.js +1 -295
  181. package/dist-standalone/logger.js +1 -285
  182. package/dist-standalone/loopback-transport.d.ts +87 -0
  183. package/dist-standalone/loopback-transport.js +1 -0
  184. package/dist-standalone/mdns-discovery.js +1 -195
  185. package/dist-standalone/nonce-store.js +1 -76
  186. package/dist-standalone/pairing-manager.js +1 -219
  187. package/dist-standalone/plugin-system.js +1 -257
  188. package/dist-standalone/plugins/logging.js +1 -163
  189. package/dist-standalone/plugins/metrics.d.ts +4 -4
  190. package/dist-standalone/plugins/metrics.js +1 -176
  191. package/dist-standalone/plugins/validation.js +1 -297
  192. package/dist-standalone/policy.js +1 -315
  193. package/dist-standalone/progress-callbacks.js +1 -576
  194. package/dist-standalone/redis-nonce-store.js +1 -72
  195. package/dist-standalone/registry-middleware.js +1 -47
  196. package/dist-standalone/retry-strategies.js +1 -534
  197. package/dist-standalone/retry-transport.js +1 -98
  198. package/dist-standalone/runtime/browser.js +1 -516
  199. package/dist-standalone/runtime/edge.js +1 -511
  200. package/dist-standalone/runtime/react-native.d.ts +1 -1
  201. package/dist-standalone/runtime/react-native.js +1 -383
  202. package/dist-standalone/security-policy.js +1 -239
  203. package/dist-standalone/serialization.js +1 -1031
  204. package/dist-standalone/split-channel.js +1 -219
  205. package/dist-standalone/subscription-proof.js +1 -224
  206. package/dist-standalone/succession.js +1 -142
  207. package/dist-standalone/timeouts.js +1 -398
  208. package/dist-standalone/trace-context.js +1 -414
  209. package/dist-standalone/trace-spans.js +1 -488
  210. package/dist-standalone/transport.js +1 -59
  211. package/dist-standalone/trust-registry.d.ts +3 -3
  212. package/dist-standalone/trust-registry.js +1 -950
  213. package/dist-standalone/types/error-response.js +1 -52
  214. package/dist-standalone/vault-auth.js +1 -174
  215. package/dist-standalone/vault-store-loader.d.ts +9 -0
  216. package/dist-standalone/vault-store-loader.js +1 -187
  217. package/dist-standalone/verify.js +1 -16
  218. package/dist-standalone/version-info.js +1 -530
  219. package/dist-standalone/xfetch.js +1 -335
  220. package/package.json +1 -1
  221. package/share1.dat +0 -0
@@ -1,477 +1 @@
1
- /**
2
- * @module cancellation
3
- * Cancellation token support for xBind operations
4
- *
5
- * Provides AbortController/AbortSignal integration for enhanced operation control:
6
- * - Cancellable async operations (fetch, agent.call, transport)
7
- * - Timeout management with automatic cleanup
8
- * - Composite signals (multiple cancellation sources)
9
- * - Resource cleanup on cancellation
10
- *
11
- * Architecture:
12
- * - Uses standard AbortController/AbortSignal (Web API + Node.js)
13
- * - Timeout wrapper creates time-limited signals
14
- * - Composite signals combine multiple cancellation sources
15
- * - Cleanup callbacks ensure proper resource disposal
16
- *
17
- * @example
18
- * ```typescript
19
- * // Timeout with cancellation
20
- * const signal = createTimeoutSignal(5000);
21
- * await xfetch('https://api.example.com', { signal });
22
- *
23
- * // Manual cancellation
24
- * const controller = new AbortController();
25
- * setTimeout(() => controller.abort(), 1000);
26
- * await agent.call('tool:action', params, { signal: controller.signal });
27
- *
28
- * // Composite signals
29
- * const timeout = createTimeoutSignal(10000);
30
- * const manual = new AbortController();
31
- * const combined = combineSignals([timeout.signal, manual.signal]);
32
- * await operation({ signal: combined });
33
- * ```
34
- */
35
- import { ok, err } from"./_deps/shared/index.js";
36
- /**
37
- * Cancellation error thrown when operation is aborted
38
- */
39
- export class CancellationError extends Error {
40
- reason;
41
- context;
42
- constructor(message, reason, context) {
43
- super(message);
44
- this.reason = reason;
45
- this.context = context;
46
- this.name = 'CancellationError';
47
- }
48
- }
49
- /**
50
- * Create an AbortSignal that triggers after a timeout
51
- *
52
- * Automatically aborts the signal after the specified duration.
53
- * Useful for setting time limits on async operations.
54
- *
55
- * @param timeout - Timeout in milliseconds
56
- * @param options - Optional reason and context
57
- * @returns TimeoutController with signal and clear function
58
- *
59
- * @example
60
- * ```typescript
61
- * const timeout = createTimeoutSignal(5000);
62
- * try {
63
- * await fetch('https://api.example.com', { signal: timeout.signal });
64
- * } catch (error) {
65
- * if (error.name === 'AbortError') {
66
- * console.log('Request timed out after 5s');
67
- * }
68
- * } finally {
69
- * timeout.clear(); // Clean up timer
70
- * }
71
- * ```
72
- */
73
- export function createTimeoutSignal(timeout, options) {
74
- const controller = new AbortController();
75
- const startTime = Date.now();
76
- const timeoutId = setTimeout(() => {
77
- const reason = options?.reason || `Operation timed out after ${timeout}ms`;
78
- controller.abort(new CancellationError(reason, 'timeout', options?.context));
79
- }, timeout);
80
- return {
81
- signal: controller.signal,
82
- clear: () => {
83
- clearTimeout(timeoutId);
84
- },
85
- remaining: () => {
86
- const elapsed = Date.now() - startTime;
87
- return Math.max(0, timeout - elapsed);
88
- },
89
- };
90
- }
91
- /**
92
- * Combine multiple AbortSignals into a single signal
93
- *
94
- * The combined signal aborts when ANY of the source signals abort.
95
- * Useful for operations with multiple cancellation sources (timeout, user action, etc.).
96
- *
97
- * @param signals - Array of signals to combine
98
- * @returns Combined AbortSignal
99
- *
100
- * @example
101
- * ```typescript
102
- * const timeout = createTimeoutSignal(10000);
103
- * const userCancel = new AbortController();
104
- * const combined = combineSignals([timeout.signal, userCancel.signal]);
105
- *
106
- * // Either timeout OR user cancellation will abort
107
- * await fetch('https://api.example.com', { signal: combined });
108
- * ```
109
- */
110
- export function combineSignals(signals) {
111
- // Filter out already-aborted signals and undefined
112
- const activeSignals = signals.filter(s => s && !s.aborted);
113
- // If all signals are already aborted, return an aborted signal
114
- if (activeSignals.length === 0) {
115
- const alreadyAborted = signals.find(s => s?.aborted);
116
- if (alreadyAborted) {
117
- // Return already-aborted signal
118
- return alreadyAborted;
119
- }
120
- // No signals provided - return never-aborts signal
121
- return new AbortController().signal;
122
- }
123
- // If only one active signal, return it directly
124
- if (activeSignals.length === 1) {
125
- return activeSignals[0];
126
- }
127
- // Use AbortSignal.any() if available (Node 20+, modern browsers)
128
- if (typeof AbortSignal.any === 'function') {
129
- return AbortSignal.any(activeSignals);
130
- }
131
- // Fallback: manually combine signals
132
- const controller = new AbortController();
133
- const abortListeners = [];
134
- for (const signal of activeSignals) {
135
- const listener = () => {
136
- controller.abort(signal.reason);
137
- // Remove all listeners after first abort
138
- abortListeners.forEach((remove) => remove());
139
- };
140
- signal.addEventListener('abort', listener, { once: true });
141
- // Store cleanup function
142
- abortListeners.push(() => {
143
- signal.removeEventListener('abort', listener);
144
- });
145
- }
146
- return controller.signal;
147
- }
148
- /**
149
- * Register cleanup callback to run when signal is aborted
150
- *
151
- * Ensures resources are properly disposed when operation is cancelled.
152
- * Cleanup runs immediately if signal is already aborted.
153
- *
154
- * @param signal - AbortSignal to monitor
155
- * @param cleanup - Cleanup callback
156
- *
157
- * @example
158
- * ```typescript
159
- * const controller = new AbortController();
160
- * const connection = await openConnection();
161
- *
162
- * onCancellation(controller.signal, async () => {
163
- * console.log('Cleaning up connection...');
164
- * await connection.close();
165
- * });
166
- *
167
- * // Later...
168
- * controller.abort(); // Cleanup runs automatically
169
- * ```
170
- */
171
- export function onCancellation(signal, cleanup) {
172
- // If already aborted, run cleanup immediately
173
- if (signal.aborted) {
174
- void Promise.resolve().then(() => cleanup()).catch(() => {
175
- // Cleanup errors are handled silently to avoid unhandled rejections
176
- });
177
- return;
178
- }
179
- // Register cleanup to run on abort
180
- signal.addEventListener('abort', () => {
181
- // Run cleanup and catch errors silently to avoid unhandled rejections
182
- void Promise.resolve().then(() => cleanup()).catch(() => {
183
- // Cleanup errors are expected and handled silently
184
- // (test can spy on this if needed, but we don't log by default)
185
- });
186
- }, { once: true });
187
- }
188
- /**
189
- * Check if signal is aborted and throw CancellationError if so
190
- *
191
- * Useful for checking cancellation at specific points in long-running operations.
192
- *
193
- * @param signal - AbortSignal to check
194
- * @param context - Optional context for error
195
- * @throws CancellationError if signal is aborted
196
- *
197
- * @example
198
- * ```typescript
199
- * async function processLargeDataset(data: unknown[], signal?: AbortSignal) {
200
- * for (const item of data) {
201
- * throwIfAborted(signal, { item: item.id });
202
- * await processItem(item);
203
- * }
204
- * }
205
- * ```
206
- */
207
- export function throwIfAborted(signal, context) {
208
- if (signal?.aborted) {
209
- const reason = signal.reason instanceof Error
210
- ? signal.reason.message
211
- : String(signal.reason || 'Operation was cancelled');
212
- throw new CancellationError(reason, 'aborted', context);
213
- }
214
- }
215
- /**
216
- * Wrap a promise with cancellation support
217
- *
218
- * If signal aborts before promise resolves, returns error result.
219
- * Otherwise returns the promise's result.
220
- *
221
- * @param promise - Promise to wrap
222
- * @param signal - AbortSignal for cancellation
223
- * @returns Result wrapper around promise
224
- *
225
- * @example
226
- * ```typescript
227
- * const timeout = createTimeoutSignal(5000);
228
- * const result = await withCancellation(
229
- * fetch('https://api.example.com'),
230
- * timeout.signal
231
- * );
232
- *
233
- * if (!result.ok) {
234
- * console.error('Request cancelled:', result.error.message);
235
- * }
236
- * ```
237
- */
238
- export async function withCancellation(promise, signal) {
239
- if (!signal) {
240
- // No signal - just await promise
241
- try {
242
- const value = await promise;
243
- return ok(value);
244
- }
245
- catch (error) {
246
- return err(new CancellationError(error instanceof Error ? error.message : String(error), 'promise_rejected'));
247
- }
248
- }
249
- // If already aborted, return immediately
250
- if (signal.aborted) {
251
- const reason = signal.reason instanceof Error
252
- ? signal.reason.message
253
- : String(signal.reason || 'Operation was cancelled');
254
- return err(new CancellationError(reason, 'aborted'));
255
- }
256
- // Race promise against abort signal
257
- return new Promise((resolve) => {
258
- // Handle abort
259
- const onAbort = () => {
260
- const reason = signal.reason instanceof Error
261
- ? signal.reason.message
262
- : String(signal.reason || 'Operation was cancelled');
263
- resolve(err(new CancellationError(reason, 'aborted')));
264
- };
265
- signal.addEventListener('abort', onAbort, { once: true });
266
- // Handle promise resolution
267
- promise
268
- .then((value) => {
269
- signal.removeEventListener('abort', onAbort);
270
- resolve(ok(value));
271
- })
272
- .catch((error) => {
273
- signal.removeEventListener('abort', onAbort);
274
- resolve(err(new CancellationError(error instanceof Error ? error.message : String(error), 'promise_rejected')));
275
- });
276
- });
277
- }
278
- /**
279
- * Create a cancellable delay
280
- *
281
- * Returns a promise that resolves after the specified duration,
282
- * but can be cancelled early via signal.
283
- *
284
- * @param ms - Delay in milliseconds
285
- * @param signal - Optional AbortSignal for cancellation
286
- * @returns Promise that resolves after delay or rejects on cancellation
287
- *
288
- * @example
289
- * ```typescript
290
- * const controller = new AbortController();
291
- *
292
- * // Start 10s delay
293
- * const delayPromise = delay(10000, controller.signal);
294
- *
295
- * // Cancel after 2s
296
- * setTimeout(() => controller.abort(), 2000);
297
- *
298
- * try {
299
- * await delayPromise; // Throws CancellationError after 2s
300
- * } catch (error) {
301
- * console.log('Delay cancelled');
302
- * }
303
- * ```
304
- */
305
- export function delay(ms, signal) {
306
- return new Promise((resolve, reject) => {
307
- // Check if already aborted
308
- if (signal?.aborted) {
309
- const reason = signal.reason instanceof Error
310
- ? signal.reason.message
311
- : String(signal.reason || 'Delay cancelled');
312
- reject(new CancellationError(reason, 'aborted'));
313
- return;
314
- }
315
- const timeoutId = setTimeout(() => {
316
- if (signal) {
317
- signal.removeEventListener('abort', onAbort);
318
- }
319
- resolve();
320
- }, ms);
321
- const onAbort = () => {
322
- clearTimeout(timeoutId);
323
- const reason = signal.reason instanceof Error
324
- ? signal.reason.message
325
- : String(signal.reason || 'Delay cancelled');
326
- reject(new CancellationError(reason, 'aborted'));
327
- };
328
- if (signal) {
329
- signal.addEventListener('abort', onAbort, { once: true });
330
- }
331
- });
332
- }
333
- /**
334
- * Retry an operation with cancellation support
335
- *
336
- * Attempts operation multiple times with exponential backoff.
337
- * Respects cancellation signal between retry attempts.
338
- *
339
- * @param operation - Async operation to retry
340
- * @param options - Retry configuration
341
- * @returns Result of operation
342
- *
343
- * @example
344
- * ```typescript
345
- * const timeout = createTimeoutSignal(30000);
346
- *
347
- * const result = await withRetry(
348
- * async () => {
349
- * const res = await fetch('https://api.example.com');
350
- * if (!res.ok) throw new Error('Request failed');
351
- * return res.json();
352
- * },
353
- * {
354
- * maxAttempts: 5,
355
- * initialDelay: 1000,
356
- * multiplier: 2,
357
- * signal: timeout.signal,
358
- * }
359
- * );
360
- * ```
361
- */
362
- export async function withRetry(operation, options) {
363
- const maxAttempts = options?.maxAttempts ?? 3;
364
- const initialDelay = options?.initialDelay ?? 1000;
365
- const multiplier = options?.multiplier ?? 2;
366
- const signal = options?.signal;
367
- const shouldRetry = options?.shouldRetry ?? (() => true);
368
- let lastError;
369
- let delayMs = initialDelay;
370
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
371
- // Check cancellation before attempt
372
- if (signal?.aborted) {
373
- const reason = signal.reason instanceof Error
374
- ? signal.reason.message
375
- : String(signal.reason || 'Operation was cancelled');
376
- return err(new CancellationError(reason, 'aborted', { attempt }));
377
- }
378
- try {
379
- const value = await operation();
380
- return ok(value);
381
- }
382
- catch (error) {
383
- lastError = error instanceof Error ? error : new Error(String(error));
384
- // On last attempt or if shouldRetry returns false, don't retry
385
- if (attempt >= maxAttempts || !shouldRetry(error, attempt)) {
386
- // No more attempts or predicate said no
387
- break;
388
- }
389
- // Wait before retry (with cancellation support)
390
- try {
391
- await delay(delayMs, signal);
392
- delayMs *= multiplier;
393
- }
394
- catch (cancelError) {
395
- // Cancelled during delay
396
- if (cancelError instanceof CancellationError) {
397
- return err(cancelError);
398
- }
399
- throw cancelError;
400
- }
401
- }
402
- }
403
- // All attempts failed
404
- return err(lastError || new Error('Operation failed after retries'));
405
- }
406
- /**
407
- * Create a manual cancellation controller with helper methods
408
- *
409
- * Extends AbortController with additional utilities for common patterns.
410
- *
411
- * @returns Enhanced controller with helper methods
412
- *
413
- * @example
414
- * ```typescript
415
- * const controller = createCancellationController();
416
- *
417
- * // Start operation
418
- * const promise = longRunningOperation({ signal: controller.signal });
419
- *
420
- * // Cancel with custom reason
421
- * controller.cancel('User requested cancellation');
422
- *
423
- * // Check if cancelled
424
- * if (controller.isCancelled) {
425
- * console.log('Operation was cancelled');
426
- * }
427
- * ```
428
- */
429
- export function createCancellationController() {
430
- const controller = new AbortController();
431
- let cancelledReason;
432
- return {
433
- /** Abort signal for cancellation */
434
- signal: controller.signal,
435
- /** True if operation has been cancelled */
436
- get isCancelled() {
437
- return controller.signal.aborted;
438
- },
439
- /** Reason for cancellation (if any) */
440
- get reason() {
441
- return cancelledReason;
442
- },
443
- /** Cancel the operation with optional reason */
444
- cancel(reason) {
445
- cancelledReason = reason || 'Operation cancelled';
446
- controller.abort(new CancellationError(cancelledReason, 'manual'));
447
- },
448
- /** Throw if cancelled */
449
- throwIfCancelled(context) {
450
- throwIfAborted(controller.signal, context);
451
- },
452
- };
453
- }
454
- /**
455
- * Check if an error is a cancellation error
456
- *
457
- * @param error - Error to check
458
- * @returns True if error is from cancellation
459
- *
460
- * @example
461
- * ```typescript
462
- * try {
463
- * await operation({ signal });
464
- * } catch (error) {
465
- * if (isCancellationError(error)) {
466
- * console.log('Operation was cancelled');
467
- * } else {
468
- * console.error('Operation failed:', error);
469
- * }
470
- * }
471
- * ```
472
- */
473
- export function isCancellationError(error) {
474
- return error instanceof CancellationError ||
475
- (error instanceof Error && error.name === 'AbortError') ||
476
- (error instanceof Error && error.name === 'CancellationError');
477
- }
1
+ import{ok,err}from"./_deps/shared/index.js";export class CancellationError extends Error{reason;context;constructor(r,e,n){super(r),this.reason=e,this.context=n,this.name="CancellationError"}}export function createTimeoutSignal(r,e){const n=new AbortController,t=Date.now(),o=setTimeout(()=>{const t=e?.reason||`Operation timed out after ${r}ms`;n.abort(new CancellationError(t,"timeout",e?.context))},r);return{signal:n.signal,clear:()=>{clearTimeout(o)},remaining:()=>{const e=Date.now()-t;return Math.max(0,r-e)}}}export function combineSignals(r){const e=r.filter(r=>r&&!r.aborted);if(0===e.length){const e=r.find(r=>r?.aborted);return e||(new AbortController).signal}if(1===e.length)return e[0];if("function"==typeof AbortSignal.any)return AbortSignal.any(e);const n=new AbortController,t=[];for(const r of e){const e=()=>{n.abort(r.reason),t.forEach(r=>r())};r.addEventListener("abort",e,{once:!0}),t.push(()=>{r.removeEventListener("abort",e)})}return n.signal}export function onCancellation(r,e){r.aborted?Promise.resolve().then(()=>e()).catch(()=>{}):r.addEventListener("abort",()=>{Promise.resolve().then(()=>e()).catch(()=>{})},{once:!0})}export function throwIfAborted(r,e){if(r?.aborted){const n=r.reason instanceof Error?r.reason.message:String(r.reason||"Operation was cancelled");throw new CancellationError(n,"aborted",e)}}export async function withCancellation(r,e){if(!e)try{const e=await r;return ok(e)}catch(r){return err(new CancellationError(r instanceof Error?r.message:String(r),"promise_rejected"))}if(e.aborted){const r=e.reason instanceof Error?e.reason.message:String(e.reason||"Operation was cancelled");return err(new CancellationError(r,"aborted"))}return new Promise(n=>{const t=()=>{const r=e.reason instanceof Error?e.reason.message:String(e.reason||"Operation was cancelled");n(err(new CancellationError(r,"aborted")))};e.addEventListener("abort",t,{once:!0}),r.then(r=>{e.removeEventListener("abort",t),n(ok(r))}).catch(r=>{e.removeEventListener("abort",t),n(err(new CancellationError(r instanceof Error?r.message:String(r),"promise_rejected")))})})}export function delay(r,e){return new Promise((n,t)=>{if(e?.aborted){const r=e.reason instanceof Error?e.reason.message:String(e.reason||"Delay cancelled");return void t(new CancellationError(r,"aborted"))}const o=setTimeout(()=>{e&&e.removeEventListener("abort",a),n()},r),a=()=>{clearTimeout(o);const r=e.reason instanceof Error?e.reason.message:String(e.reason||"Delay cancelled");t(new CancellationError(r,"aborted"))};e&&e.addEventListener("abort",a,{once:!0})})}export async function withRetry(r,e){const n=e?.maxAttempts??3,t=e?.initialDelay??1e3,o=e?.multiplier??2,a=e?.signal,i=e?.shouldRetry??(()=>!0);let s,c=t;for(let e=1;e<=n;e++){if(a?.aborted){const r=a.reason instanceof Error?a.reason.message:String(a.reason||"Operation was cancelled");return err(new CancellationError(r,"aborted",{attempt:e}))}try{const e=await r();return ok(e)}catch(r){if(s=r instanceof Error?r:new Error(String(r)),e>=n||!i(r,e))break;try{await delay(c,a),c*=o}catch(r){if(r instanceof CancellationError)return err(r);throw r}}}return err(s||new Error("Operation failed after retries"))}export function createCancellationController(){const r=new AbortController;let e;return{signal:r.signal,get isCancelled(){return r.signal.aborted},get reason(){return e},cancel(n){e=n||"Operation cancelled",r.abort(new CancellationError(e,"manual"))},throwIfCancelled(e){throwIfAborted(r.signal,e)}}}export function isCancellationError(r){return r instanceof CancellationError||r instanceof Error&&"AbortError"===r.name||r instanceof Error&&"CancellationError"===r.name}
@@ -1,186 +1 @@
1
- import { ok, err } from"./_deps/shared/index.js";
2
- import { signMlDsa65, verifyMlDsa65 } from './identity.js';
3
- /* ── Checkpoint Creation (Gateway-side) ── */
4
- /**
5
- * Create a signed checkpoint for a DID (gateway-side operation).
6
- *
7
- * Gateway signs the DID state snapshot using its ML-DSA-65 private key.
8
- * Clients verify this signature using the gateway's published public key.
9
- *
10
- * @param subject - DID being checkpointed
11
- * @param publicKey - Current public key bytes
12
- * @param revoked - Current revocation status
13
- * @param rotationSequence - Current rotation sequence counter
14
- * @param gatewayPrivateKey - Gateway's ML-DSA-65 secret key (32-byte seed or 4032-byte expanded)
15
- * @returns Signed checkpoint or error
16
- *
17
- * @example
18
- * ```typescript
19
- * const checkpoint = await createCheckpoint(
20
- * 'did:key:z6Mk...',
21
- * publicKeyBytes,
22
- * false,
23
- * 5,
24
- * gatewaySecretKey
25
- * );
26
- * if (checkpoint.ok) {
27
- * // Send checkpoint to client for staleness detection
28
- * sendToClient(checkpoint.value);
29
- * }
30
- * ```
31
- */
32
- export async function createCheckpoint(subject, publicKey, revoked, rotationSequence, gatewayPrivateKey) {
33
- const timestamp = Date.now();
34
- // Construct canonical message to sign
35
- const publicKeyB64 = Buffer.from(publicKey).toString('base64');
36
- const message = `DIDStateCheckpoint||1.0||${subject}||${publicKeyB64}||${revoked}||${rotationSequence}||${timestamp}`;
37
- const messageBytes = new TextEncoder().encode(message);
38
- // Sign using gateway's ML-DSA-65 key
39
- const sigResult = await signMlDsa65(gatewayPrivateKey, messageBytes);
40
- if (!sigResult.ok) {
41
- return err('SIGN_FAILED');
42
- }
43
- return ok({
44
- type: 'DIDStateCheckpoint',
45
- version: '1.0',
46
- subject,
47
- current_public_key: publicKeyB64,
48
- revoked,
49
- rotation_sequence: rotationSequence,
50
- timestamp,
51
- checkpoint_signature_algorithm: 'ML-DSA-65',
52
- checkpoint_signature: Buffer.from(sigResult.value).toString('base64')
53
- });
54
- }
55
- /* ── Checkpoint Verification (Client-side) ── */
56
- /**
57
- * Verify a checkpoint signature (client-side operation).
58
- *
59
- * Clients MUST verify checkpoint signatures before trusting the state.
60
- * Uses gateway's published ML-DSA-65 public key to verify signature.
61
- *
62
- * @param checkpoint - Checkpoint to verify
63
- * @param gatewayPublicKey - Gateway's ML-DSA-65 public key (1952 bytes)
64
- * @returns true if signature valid, false if invalid, error if verification fails
65
- *
66
- * @example
67
- * ```typescript
68
- * const valid = await verifyCheckpoint(checkpoint, gatewayPubKey);
69
- * if (valid.ok && valid.value) {
70
- * // Checkpoint is authentic - safe to use for staleness detection
71
- * if (isCacheStale(localCache, checkpoint)) {
72
- * // Refresh local cache
73
- * }
74
- * }
75
- * ```
76
- */
77
- export async function verifyCheckpoint(checkpoint, gatewayPublicKey) {
78
- // Validate checkpoint format
79
- if (checkpoint.type !== 'DIDStateCheckpoint') {
80
- return err('INVALID_FORMAT');
81
- }
82
- if (checkpoint.version !== '1.0') {
83
- return err('INVALID_FORMAT');
84
- }
85
- if (!checkpoint.subject || !checkpoint.current_public_key) {
86
- return err('INVALID_FORMAT');
87
- }
88
- if (typeof checkpoint.rotation_sequence !== 'number' || checkpoint.rotation_sequence < 0) {
89
- return err('INVALID_FORMAT');
90
- }
91
- if (typeof checkpoint.timestamp !== 'number' || checkpoint.timestamp <= 0) {
92
- return err('INVALID_TIMESTAMP');
93
- }
94
- if (checkpoint.checkpoint_signature_algorithm !== 'ML-DSA-65') {
95
- return err('INVALID_FORMAT');
96
- }
97
- // Reconstruct canonical message
98
- const message = `DIDStateCheckpoint||1.0||${checkpoint.subject}||${checkpoint.current_public_key}||${checkpoint.revoked}||${checkpoint.rotation_sequence}||${checkpoint.timestamp}`;
99
- const messageBytes = new TextEncoder().encode(message);
100
- // Decode signature
101
- let signature;
102
- try {
103
- signature = Buffer.from(checkpoint.checkpoint_signature, 'base64');
104
- }
105
- catch {
106
- return err('INVALID_SIGNATURE');
107
- }
108
- // Verify signature using gateway public key
109
- const verifyResult = await verifyMlDsa65(gatewayPublicKey, signature, messageBytes);
110
- if (!verifyResult.ok) {
111
- return err('VERIFY_FAILED');
112
- }
113
- return ok(verifyResult.value);
114
- }
115
- /* ── Staleness Detection ── */
116
- /**
117
- * Detect if local cache is stale compared to gateway checkpoint.
118
- *
119
- * Cache is stale if:
120
- * 1. Checkpoint rotation_sequence > local rotationSequence (key rotated)
121
- * 2. Checkpoint revoked !== local revoked (revocation status changed)
122
- * 3. Checkpoint public key !== local publicKey (state drift)
123
- *
124
- * @param localCache - Local cache entry for DID
125
- * @param checkpoint - Verified checkpoint from gateway
126
- * @returns true if cache needs refresh, false if cache is current
127
- *
128
- * @example
129
- * ```typescript
130
- * if (isCacheStale(localCache, checkpoint)) {
131
- * // Local cache is outdated - fetch fresh state from gateway
132
- * const freshState = await registry.getEntry(did);
133
- * }
134
- * ```
135
- */
136
- export function isCacheStale(localCache, checkpoint) {
137
- // Sequence number mismatch indicates key rotation
138
- if (checkpoint.rotation_sequence > localCache.rotationSequence) {
139
- return true;
140
- }
141
- // Revocation status changed
142
- if (checkpoint.revoked !== localCache.revoked) {
143
- return true;
144
- }
145
- // Public key mismatch indicates state drift
146
- const checkpointPubKey = Buffer.from(checkpoint.current_public_key, 'base64');
147
- if (!Buffer.from(localCache.publicKey).equals(checkpointPubKey)) {
148
- return true;
149
- }
150
- return false;
151
- }
152
- /* ── Encoding/Decoding ── */
153
- /**
154
- * Encode checkpoint to JSON string for wire transport.
155
- *
156
- * @param checkpoint - Checkpoint to encode
157
- * @returns JSON string
158
- */
159
- export function encodeCheckpoint(checkpoint) {
160
- return JSON.stringify(checkpoint);
161
- }
162
- /**
163
- * Decode checkpoint from JSON string.
164
- *
165
- * @param encoded - JSON string
166
- * @returns Parsed checkpoint or error
167
- */
168
- export function decodeCheckpoint(encoded) {
169
- try {
170
- const parsed = JSON.parse(encoded);
171
- // Basic validation
172
- if (parsed.type !== 'DIDStateCheckpoint') {
173
- return err('INVALID_FORMAT');
174
- }
175
- if (parsed.version !== '1.0') {
176
- return err('INVALID_FORMAT');
177
- }
178
- if (!parsed.subject || !parsed.current_public_key || !parsed.checkpoint_signature) {
179
- return err('INVALID_FORMAT');
180
- }
181
- return ok(parsed);
182
- }
183
- catch {
184
- return err('INVALID_FORMAT');
185
- }
186
- }
1
+ import{ok,err}from"./_deps/shared/index.js";import{signMlDsa65,verifyMlDsa65}from"./identity.js";export async function createCheckpoint(e,r,t,n,o){const i=Date.now(),c=Buffer.from(r).toString("base64"),u=`DIDStateCheckpoint||1.0||${e}||${c}||${t}||${n}||${i}`,a=(new TextEncoder).encode(u),s=await signMlDsa65(o,a);return s.ok?ok({type:"DIDStateCheckpoint",version:"1.0",subject:e,current_public_key:c,revoked:t,rotation_sequence:n,timestamp:i,checkpoint_signature_algorithm:"ML-DSA-65",checkpoint_signature:Buffer.from(s.value).toString("base64")}):err("SIGN_FAILED")}export async function verifyCheckpoint(e,r){if("DIDStateCheckpoint"!==e.type)return err("INVALID_FORMAT");if("1.0"!==e.version)return err("INVALID_FORMAT");if(!e.subject||!e.current_public_key)return err("INVALID_FORMAT");if("number"!=typeof e.rotation_sequence||e.rotation_sequence<0)return err("INVALID_FORMAT");if("number"!=typeof e.timestamp||e.timestamp<=0)return err("INVALID_TIMESTAMP");if("ML-DSA-65"!==e.checkpoint_signature_algorithm)return err("INVALID_FORMAT");const t=`DIDStateCheckpoint||1.0||${e.subject}||${e.current_public_key}||${e.revoked}||${e.rotation_sequence}||${e.timestamp}`,n=(new TextEncoder).encode(t);let o;try{o=Buffer.from(e.checkpoint_signature,"base64")}catch{return err("INVALID_SIGNATURE")}const i=await verifyMlDsa65(r,o,n);return i.ok?ok(i.value):err("VERIFY_FAILED")}export function isCacheStale(e,r){if(r.rotation_sequence>e.rotationSequence)return!0;if(r.revoked!==e.revoked)return!0;const t=Buffer.from(r.current_public_key,"base64");return!Buffer.from(e.publicKey).equals(t)}export function encodeCheckpoint(e){return JSON.stringify(e)}export function decodeCheckpoint(e){try{const r=JSON.parse(e);return"DIDStateCheckpoint"!==r.type||"1.0"!==r.version?err("INVALID_FORMAT"):r.subject&&r.current_public_key&&r.checkpoint_signature?ok(r):err("INVALID_FORMAT")}catch{return err("INVALID_FORMAT")}}