@private.me/xbind 1.3.0 → 2.3.4

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 (305) hide show
  1. package/LICENSES.md +212 -0
  2. package/README.md +388 -6
  3. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
  4. package/dist-standalone/_deps/shared/cjs/errors.js +1 -275
  5. package/dist-standalone/_deps/shared/cjs/index.js +1 -138
  6. package/dist-standalone/_deps/shared/cjs/types.js +1 -90
  7. package/dist-standalone/_deps/shared/errors.js +1 -262
  8. package/dist-standalone/_deps/shared/index.js +1 -77
  9. package/dist-standalone/_deps/shared/types.js +1 -91
  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.js +1 -642
  48. package/dist-standalone/agent-sdk.js +1 -328
  49. package/dist-standalone/agent.d.ts +95 -5
  50. package/dist-standalone/agent.js +1 -1545
  51. package/dist-standalone/approval.js +1 -193
  52. package/dist-standalone/async-iterators.d.ts +275 -0
  53. package/dist-standalone/async-iterators.js +1 -0
  54. package/dist-standalone/auth.js +1 -219
  55. package/dist-standalone/auto-accept.js +1 -229
  56. package/dist-standalone/backup-config.js +1 -201
  57. package/dist-standalone/backup.d.ts +114 -0
  58. package/dist-standalone/backup.js +1 -0
  59. package/dist-standalone/batch-operations.d.ts +297 -0
  60. package/dist-standalone/batch-operations.js +1 -0
  61. package/dist-standalone/cancellation.d.ts +301 -0
  62. package/dist-standalone/cancellation.js +1 -0
  63. package/dist-standalone/checkpoint.js +1 -186
  64. package/dist-standalone/circuit-breaker.d.ts +351 -0
  65. package/dist-standalone/circuit-breaker.js +1 -0
  66. package/dist-standalone/cjs/agent-call.js +1 -651
  67. package/dist-standalone/cjs/agent-sdk.js +1 -332
  68. package/dist-standalone/cjs/agent.js +1 -1582
  69. package/dist-standalone/cjs/approval.js +1 -199
  70. package/dist-standalone/cjs/async-iterators.js +1 -0
  71. package/dist-standalone/cjs/auth.js +1 -225
  72. package/dist-standalone/cjs/auto-accept.js +1 -233
  73. package/dist-standalone/cjs/backup-config.js +1 -207
  74. package/dist-standalone/cjs/backup.js +1 -0
  75. package/dist-standalone/cjs/batch-operations.js +1 -0
  76. package/dist-standalone/cjs/cancellation.js +1 -0
  77. package/dist-standalone/cjs/checkpoint.js +1 -193
  78. package/dist-standalone/cjs/circuit-breaker.js +1 -0
  79. package/dist-standalone/cjs/cli/init.js +1 -486
  80. package/dist-standalone/cjs/config-validation.js +1 -0
  81. package/dist-standalone/cjs/connect.js +1 -312
  82. package/dist-standalone/cjs/connection-pool.js +1 -0
  83. package/dist-standalone/cjs/correlation-id.js +1 -339
  84. package/dist-standalone/cjs/crypto-utils.js +1 -0
  85. package/dist-standalone/cjs/debug-mode.js +1 -0
  86. package/dist-standalone/cjs/did-document.js +1 -101
  87. package/dist-standalone/cjs/did-privateme.js +1 -130
  88. package/dist-standalone/cjs/did-web.js +1 -201
  89. package/dist-standalone/cjs/discovery.js +1 -462
  90. package/dist-standalone/cjs/dual-mode.js +1 -251
  91. package/dist-standalone/cjs/email-templates.js +1 -313
  92. package/dist-standalone/cjs/email-transport.js +1 -239
  93. package/dist-standalone/cjs/envelope.js +1 -510
  94. package/dist-standalone/cjs/errors.js +1 -826
  95. package/dist-standalone/cjs/event-emitter.js +1 -0
  96. package/dist-standalone/cjs/gateway-state.js +1 -55
  97. package/dist-standalone/cjs/gateway-transport.js +1 -120
  98. package/dist-standalone/cjs/graceful-degradation.js +1 -0
  99. package/dist-standalone/cjs/guardrails.js +1 -223
  100. package/dist-standalone/cjs/health-check.js +1 -0
  101. package/dist-standalone/cjs/http-compat.js +1 -272
  102. package/dist-standalone/cjs/http-status-map.js +1 -571
  103. package/dist-standalone/cjs/identity.js +1 -541
  104. package/dist-standalone/cjs/index.js +1 -237
  105. package/dist-standalone/cjs/invitation.js +1 -421
  106. package/dist-standalone/cjs/invite.js +1 -328
  107. package/dist-standalone/cjs/key-agreement.js +1 -246
  108. package/dist-standalone/cjs/lazy-init.js +1 -300
  109. package/dist-standalone/cjs/logger.js +1 -0
  110. package/dist-standalone/cjs/mdns-discovery.js +1 -202
  111. package/dist-standalone/cjs/nonce-store.js +1 -66
  112. package/dist-standalone/cjs/pairing-manager.js +1 -223
  113. package/dist-standalone/cjs/plugin-system.js +1 -0
  114. package/dist-standalone/cjs/plugins/logging.js +1 -0
  115. package/dist-standalone/cjs/plugins/metrics.js +1 -0
  116. package/dist-standalone/cjs/plugins/validation.js +1 -0
  117. package/dist-standalone/cjs/policy.js +1 -320
  118. package/dist-standalone/cjs/progress-callbacks.js +1 -0
  119. package/dist-standalone/cjs/redis-nonce-store.js +1 -76
  120. package/dist-standalone/cjs/registry-middleware.js +1 -50
  121. package/dist-standalone/cjs/retry-strategies.js +1 -0
  122. package/dist-standalone/cjs/retry-transport.js +1 -102
  123. package/dist-standalone/cjs/runtime/browser.js +1 -0
  124. package/dist-standalone/cjs/runtime/edge.js +1 -0
  125. package/dist-standalone/cjs/runtime/react-native.js +1 -0
  126. package/dist-standalone/cjs/security-policy.js +1 -245
  127. package/dist-standalone/cjs/serialization.js +1 -0
  128. package/dist-standalone/cjs/split-channel.js +1 -177
  129. package/dist-standalone/cjs/subscription-proof.js +1 -230
  130. package/dist-standalone/cjs/succession.js +1 -148
  131. package/dist-standalone/cjs/timeouts.js +1 -0
  132. package/dist-standalone/cjs/trace-context.js +1 -0
  133. package/dist-standalone/cjs/trace-spans.js +1 -0
  134. package/dist-standalone/cjs/transport.js +1 -63
  135. package/dist-standalone/cjs/trust-registry.js +1 -742
  136. package/dist-standalone/cjs/types/error-response.js +1 -56
  137. package/dist-standalone/cjs/vault-auth.js +1 -0
  138. package/dist-standalone/cjs/vault-store-loader.js +1 -0
  139. package/dist-standalone/cjs/verify.js +1 -25
  140. package/dist-standalone/cjs/version-info.js +1 -0
  141. package/dist-standalone/cjs/xfetch.js +1 -252
  142. package/dist-standalone/cli/init.js +1 -449
  143. package/dist-standalone/cli/setup.js +1 -514
  144. package/dist-standalone/cli/types.js +1 -27
  145. package/dist-standalone/cli/xbind.js +1 -148
  146. package/dist-standalone/config-validation.d.ts +185 -0
  147. package/dist-standalone/config-validation.js +1 -0
  148. package/dist-standalone/connect.js +1 -274
  149. package/dist-standalone/connection-pool.d.ts +251 -0
  150. package/dist-standalone/connection-pool.js +1 -0
  151. package/dist-standalone/correlation-id.js +1 -326
  152. package/dist-standalone/crypto-utils.d.ts +60 -0
  153. package/dist-standalone/crypto-utils.js +1 -0
  154. package/dist-standalone/debug-mode.d.ts +286 -0
  155. package/dist-standalone/debug-mode.js +1 -0
  156. package/dist-standalone/did-document.js +1 -96
  157. package/dist-standalone/did-privateme.js +1 -121
  158. package/dist-standalone/did-web.js +1 -196
  159. package/dist-standalone/discovery.js +1 -458
  160. package/dist-standalone/dual-mode.js +1 -247
  161. package/dist-standalone/email-templates.js +1 -309
  162. package/dist-standalone/email-transport.js +1 -232
  163. package/dist-standalone/envelope.d.ts +29 -1
  164. package/dist-standalone/envelope.js +1 -497
  165. package/dist-standalone/errors.d.ts +10 -0
  166. package/dist-standalone/errors.js +1 -811
  167. package/dist-standalone/event-emitter.d.ts +395 -0
  168. package/dist-standalone/event-emitter.js +1 -0
  169. package/dist-standalone/gateway-state.js +1 -51
  170. package/dist-standalone/gateway-transport.js +1 -116
  171. package/dist-standalone/graceful-degradation.d.ts +246 -0
  172. package/dist-standalone/graceful-degradation.js +1 -0
  173. package/dist-standalone/guardrails.js +1 -216
  174. package/dist-standalone/health-check.d.ts +150 -0
  175. package/dist-standalone/health-check.js +1 -0
  176. package/dist-standalone/http-compat.js +1 -267
  177. package/dist-standalone/http-status-map.js +1 -561
  178. package/dist-standalone/identity.d.ts +64 -1
  179. package/dist-standalone/identity.js +1 -516
  180. package/dist-standalone/index.d.ts +45 -3
  181. package/dist-standalone/index.js +1 -52
  182. package/dist-standalone/invitation.js +1 -415
  183. package/dist-standalone/invite.js +1 -324
  184. package/dist-standalone/key-agreement.d.ts +61 -13
  185. package/dist-standalone/key-agreement.js +1 -236
  186. package/dist-standalone/lazy-init.js +1 -295
  187. package/dist-standalone/logger.d.ts +77 -0
  188. package/dist-standalone/logger.js +1 -0
  189. package/dist-standalone/mdns-discovery.js +1 -195
  190. package/dist-standalone/nonce-store.d.ts +16 -3
  191. package/dist-standalone/nonce-store.js +1 -62
  192. package/dist-standalone/package.json +0 -1
  193. package/dist-standalone/pairing-manager.js +1 -219
  194. package/dist-standalone/plugin-system.d.ts +145 -0
  195. package/dist-standalone/plugin-system.js +1 -0
  196. package/dist-standalone/policy.js +1 -315
  197. package/dist-standalone/progress-callbacks.d.ts +394 -0
  198. package/dist-standalone/progress-callbacks.js +1 -0
  199. package/dist-standalone/redis-nonce-store.js +1 -72
  200. package/dist-standalone/registry-middleware.js +1 -47
  201. package/dist-standalone/retry-strategies.d.ts +382 -0
  202. package/dist-standalone/retry-strategies.js +1 -0
  203. package/dist-standalone/retry-transport.js +1 -98
  204. package/dist-standalone/security-policy.js +1 -239
  205. package/dist-standalone/serialization.d.ts +244 -0
  206. package/dist-standalone/serialization.js +1 -0
  207. package/dist-standalone/split-channel.d.ts +49 -1
  208. package/dist-standalone/split-channel.js +1 -171
  209. package/dist-standalone/subscription-proof.js +1 -224
  210. package/dist-standalone/succession.js +1 -142
  211. package/dist-standalone/timeouts.d.ts +275 -0
  212. package/dist-standalone/timeouts.js +1 -0
  213. package/dist-standalone/trace-context.d.ts +252 -0
  214. package/dist-standalone/trace-context.js +1 -0
  215. package/dist-standalone/trace-spans.d.ts +360 -0
  216. package/dist-standalone/trace-spans.js +1 -0
  217. package/dist-standalone/transport.js +1 -59
  218. package/dist-standalone/trust-registry.d.ts +106 -5
  219. package/dist-standalone/trust-registry.js +1 -702
  220. package/dist-standalone/vault-auth.d.ts +91 -0
  221. package/dist-standalone/vault-auth.js +1 -0
  222. package/dist-standalone/vault-store-loader.d.ts +110 -0
  223. package/dist-standalone/vault-store-loader.js +1 -0
  224. package/dist-standalone/verify.js +1 -16
  225. package/dist-standalone/version-info.d.ts +259 -0
  226. package/dist-standalone/version-info.js +1 -0
  227. package/dist-standalone/xfetch.js +1 -247
  228. package/llms.txt +1 -0
  229. package/package.json +66 -5
  230. package/share1.dat +0 -0
  231. package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
  232. package/dist-standalone/_deps/crypto/base64.js +0 -209
  233. package/dist-standalone/_deps/crypto/cjs/base64.js +0 -103
  234. package/dist-standalone/_deps/crypto/cjs/errors.js +0 -119
  235. package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -71
  236. package/dist-standalone/_deps/crypto/cjs/index.js +0 -86
  237. package/dist-standalone/_deps/crypto/cjs/padding.js +0 -57
  238. package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -68
  239. package/dist-standalone/_deps/crypto/cjs/shares.js +0 -152
  240. package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -199
  241. package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -61
  242. package/dist-standalone/_deps/crypto/cjs/verify.js +0 -24
  243. package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -221
  244. package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
  245. package/dist-standalone/_deps/crypto/errors.js +0 -109
  246. package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
  247. package/dist-standalone/_deps/crypto/hmac.js +0 -66
  248. package/dist-standalone/_deps/crypto/index.d.ts +0 -20
  249. package/dist-standalone/_deps/crypto/index.js +0 -45
  250. package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
  251. package/dist-standalone/_deps/crypto/padding.js +0 -53
  252. package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
  253. package/dist-standalone/_deps/crypto/share-header.js +0 -63
  254. package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
  255. package/dist-standalone/_deps/crypto/shares.js +0 -148
  256. package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
  257. package/dist-standalone/_deps/crypto/tlv.js +0 -195
  258. package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
  259. package/dist-standalone/_deps/crypto/uuid.js +0 -56
  260. package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
  261. package/dist-standalone/_deps/crypto/verify.js +0 -15
  262. package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
  263. package/dist-standalone/_deps/crypto/xorida.js +0 -215
  264. package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
  265. package/dist-standalone/_deps/shared/errors.js.map +0 -1
  266. package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
  267. package/dist-standalone/_deps/shared/index.js.map +0 -1
  268. package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
  269. package/dist-standalone/_deps/shared/types.js.map +0 -1
  270. package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
  271. package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
  272. package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
  273. package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
  274. package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
  275. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
  276. package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
  277. package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
  278. package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
  279. package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
  280. package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
  281. package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
  282. package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
  283. package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
  284. package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
  285. package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
  286. package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
  287. package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
  288. package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
  289. package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
  290. package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
  291. package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
  292. package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
  293. package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
  294. package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
  295. package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
  296. package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
  297. package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
  298. package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
  299. package/dist-standalone/_deps/xregistry/index.js.map +0 -1
  300. package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
  301. package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
  302. package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
  303. package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
  304. package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
  305. package/dist-standalone/_deps/xregistry/types.js.map +0 -1
@@ -0,0 +1,246 @@
1
+ /**
2
+ * @module graceful-degradation
3
+ * Enhanced Reliability Capabilities for xBind
4
+ *
5
+ * Provides graceful degradation mechanisms to maintain service continuity
6
+ * when external services (registry, gateway, transport) experience issues.
7
+ *
8
+ * Features:
9
+ * - Quality-of-Service (QoS) tiers for operation prioritization
10
+ * - Fallback mechanisms with exponential backoff
11
+ * - Intelligent caching with TTL
12
+ * - User-friendly error messages with actionable recovery hints
13
+ */
14
+ import type { Result } from '@private.me/shared';
15
+ import type { ACIErrorDetail } from '@private.me/ux-helpers';
16
+ import type { TrustRegistry } from './trust-registry.js';
17
+ import type { XailTransportAdapter } from './transport.js';
18
+ /**
19
+ * Quality of Service levels for operations.
20
+ *
21
+ * Determines behavior when services are degraded:
22
+ * - CRITICAL: Must succeed, fail fast if unavailable
23
+ * - HIGH: Retry with aggressive backoff, use cache if fresh
24
+ * - NORMAL: Retry with standard backoff, use stale cache if necessary
25
+ * - LOW: Skip retries, use cache regardless of staleness
26
+ */
27
+ export type QoSLevel = 'CRITICAL' | 'HIGH' | 'NORMAL' | 'LOW';
28
+ /**
29
+ * Operation metadata for QoS decisions.
30
+ */
31
+ export interface OperationContext {
32
+ /** Unique operation identifier for correlation. */
33
+ readonly operationId: string;
34
+ /** Quality of service level. */
35
+ readonly qos: QoSLevel;
36
+ /** Operation type (e.g., 'registry:lookup', 'gateway:call', 'transport:send'). */
37
+ readonly type: string;
38
+ /** Optional timeout override in milliseconds. */
39
+ readonly timeoutMs?: number;
40
+ }
41
+ /**
42
+ * Cache entry with TTL and staleness tracking.
43
+ */
44
+ interface CacheEntry<T> {
45
+ /** Cached value. */
46
+ readonly value: T;
47
+ /** Timestamp when entry was created (ms since epoch). */
48
+ readonly cachedAt: number;
49
+ /** Time-to-live in milliseconds. */
50
+ readonly ttl: number;
51
+ /** Number of times this stale entry has been used. */
52
+ usageCount: number;
53
+ }
54
+ /**
55
+ * Cache configuration options.
56
+ */
57
+ export interface CacheConfig {
58
+ /** Default TTL for cache entries (milliseconds). Default: 300000 (5 minutes). */
59
+ readonly defaultTTL?: number;
60
+ /** Maximum cache size (entries). Default: 1000. */
61
+ readonly maxSize?: number;
62
+ /** Allow stale cache usage for LOW/NORMAL QoS. Default: true. */
63
+ readonly allowStale?: boolean;
64
+ /** Maximum staleness allowed (milliseconds). Default: 3600000 (1 hour). */
65
+ readonly maxStaleMs?: number;
66
+ }
67
+ /**
68
+ * Retry configuration for fallback operations.
69
+ */
70
+ export interface RetryConfig {
71
+ /** Maximum retry attempts. Default: 3. */
72
+ readonly maxAttempts?: number;
73
+ /** Initial delay in milliseconds. Default: 1000. */
74
+ readonly initialDelayMs?: number;
75
+ /** Backoff multiplier. Default: 2. */
76
+ readonly backoffMultiplier?: number;
77
+ /** Maximum delay in milliseconds. Default: 30000. */
78
+ readonly maxDelayMs?: number;
79
+ /** Enable jitter (randomize delays). Default: true. */
80
+ readonly jitter?: boolean;
81
+ }
82
+ /**
83
+ * Service health status.
84
+ */
85
+ type ServiceHealth = 'HEALTHY' | 'DEGRADED' | 'UNAVAILABLE';
86
+ /**
87
+ * Service health tracking.
88
+ */
89
+ interface ServiceStatus {
90
+ /** Current health status. */
91
+ health: ServiceHealth;
92
+ /** Last successful operation timestamp. */
93
+ lastSuccess: number;
94
+ /** Consecutive failure count. */
95
+ failureCount: number;
96
+ /** Last error encountered. */
97
+ lastError?: string;
98
+ }
99
+ /**
100
+ * Graceful Degradation Manager
101
+ *
102
+ * Coordinates fallback mechanisms, caching, and retry logic across
103
+ * registry, gateway, and transport layers.
104
+ */
105
+ export declare class GracefulDegradationManager {
106
+ private readonly cacheConfig;
107
+ private readonly retryConfig;
108
+ private readonly cache;
109
+ private readonly serviceStatus;
110
+ constructor(cacheConfig?: CacheConfig, retryConfig?: RetryConfig);
111
+ /**
112
+ * Get cached value if available and valid for the given QoS level.
113
+ *
114
+ * @param key Cache key
115
+ * @param qos Quality of service level
116
+ * @returns Cached value or undefined
117
+ */
118
+ getCached<T>(key: string, qos: QoSLevel): T | undefined;
119
+ /**
120
+ * Store value in cache with TTL.
121
+ *
122
+ * @param key Cache key
123
+ * @param value Value to cache
124
+ * @param ttl Time-to-live in milliseconds (uses default if omitted)
125
+ */
126
+ setCached<T>(key: string, value: T, ttl?: number): void;
127
+ /**
128
+ * Invalidate cached entry.
129
+ *
130
+ * @param key Cache key
131
+ */
132
+ invalidate(key: string): void;
133
+ /**
134
+ * Clear all cached entries.
135
+ */
136
+ clearCache(): void;
137
+ /**
138
+ * Get cache statistics.
139
+ */
140
+ getCacheStats(): {
141
+ size: number;
142
+ maxSize: number;
143
+ entries: Array<{
144
+ key: string;
145
+ age: number;
146
+ usageCount: number;
147
+ }>;
148
+ };
149
+ /**
150
+ * Record successful operation for a service.
151
+ *
152
+ * @param serviceName Service identifier (e.g., 'registry', 'gateway', 'transport')
153
+ */
154
+ recordSuccess(serviceName: string): void;
155
+ /**
156
+ * Record failed operation for a service.
157
+ *
158
+ * @param serviceName Service identifier
159
+ * @param error Error message
160
+ */
161
+ recordFailure(serviceName: string, error: string): void;
162
+ /**
163
+ * Get service health status.
164
+ *
165
+ * @param serviceName Service identifier
166
+ * @returns Service health status
167
+ */
168
+ getServiceHealth(serviceName: string): ServiceHealth;
169
+ /**
170
+ * Get detailed service status.
171
+ *
172
+ * @param serviceName Service identifier
173
+ * @returns Service status or undefined if not tracked
174
+ */
175
+ getServiceStatus(serviceName: string): ServiceStatus | undefined;
176
+ /**
177
+ * Reset service health tracking.
178
+ *
179
+ * @param serviceName Service identifier (all services if omitted)
180
+ */
181
+ resetServiceHealth(serviceName?: string): void;
182
+ /**
183
+ * Execute operation with retry logic based on QoS level.
184
+ *
185
+ * @param operation Async operation to execute
186
+ * @param context Operation context with QoS level
187
+ * @returns Result of operation
188
+ */
189
+ withRetry<T, E>(operation: () => Promise<Result<T, E>>, context: OperationContext): Promise<Result<T, E>>;
190
+ /**
191
+ * Get maximum retry attempts for QoS level.
192
+ */
193
+ private getMaxAttempts;
194
+ /**
195
+ * Calculate exponential backoff delay with optional jitter.
196
+ */
197
+ private calculateBackoff;
198
+ /**
199
+ * Sleep for specified milliseconds.
200
+ */
201
+ private sleep;
202
+ }
203
+ /**
204
+ * Registry lookup with graceful degradation.
205
+ *
206
+ * Provides intelligent fallback for DID resolution:
207
+ * 1. Try primary registry lookup
208
+ * 2. On failure, check cache based on QoS level
209
+ * 3. Return user-friendly error if all fallbacks exhausted
210
+ *
211
+ * @param registry Trust registry instance
212
+ * @param did DID to resolve
213
+ * @param context Operation context
214
+ * @param manager Degradation manager
215
+ * @returns Registry lookup result
216
+ */
217
+ export declare function registryLookupWithFallback(registry: TrustRegistry, did: string, context: OperationContext, manager: GracefulDegradationManager): Promise<Result<{
218
+ publicKey: string;
219
+ endpoint?: string;
220
+ }, ACIErrorDetail>>;
221
+ /**
222
+ * Send envelope with transport fallback.
223
+ *
224
+ * Tries multiple transports in order until one succeeds:
225
+ * 1. Primary transport with retry
226
+ * 2. Fallback transports (if configured)
227
+ * 3. Return detailed error if all transports fail
228
+ *
229
+ * @param transports Array of transport adapters
230
+ * @param envelope Envelope to send
231
+ * @param recipientDid Recipient DID
232
+ * @param context Operation context
233
+ * @param manager Degradation manager
234
+ * @returns Send result
235
+ */
236
+ export declare function sendWithTransportFallback(transports: XailTransportAdapter[], envelope: unknown, recipientDid: string, context: OperationContext, manager: GracefulDegradationManager): Promise<Result<void, ACIErrorDetail>>;
237
+ /**
238
+ * Enhance error with QoS-appropriate messaging.
239
+ *
240
+ * @param error Original error
241
+ * @param context Operation context
242
+ * @param manager Degradation manager
243
+ * @returns Enhanced error with actionable hints
244
+ */
245
+ export declare function enhanceError(error: ACIErrorDetail, context: OperationContext, manager: GracefulDegradationManager): ACIErrorDetail;
246
+ export type { CacheEntry, ServiceStatus };
@@ -0,0 +1 @@
1
+ import{ok,err}from"./_deps/shared/index.js";import{createDetailedError}from"./_deps/ux-helpers/index.js";const DEFAULT_CACHE_CONFIG={defaultTTL:3e5,maxSize:1e3,allowStale:!0,maxStaleMs:36e5},DEFAULT_RETRY_CONFIG={maxAttempts:3,initialDelayMs:1e3,backoffMultiplier:2,maxDelayMs:3e4,jitter:!0};export class GracefulDegradationManager{cacheConfig;retryConfig;cache=new Map;serviceStatus=new Map;constructor(e,t){this.cacheConfig={...DEFAULT_CACHE_CONFIG,...e},this.retryConfig={...DEFAULT_RETRY_CONFIG,...t}}getCached(e,t){const r=this.cache.get(e);if(!r)return;const a=Date.now()-r.cachedAt,i=a<=r.ttl,s=a>r.ttl&&a<=this.cacheConfig.maxStaleMs;return"CRITICAL"===t||"HIGH"===t?i?r.value:void 0:this.cacheConfig.allowStale&&(i||s)?(r.usageCount++,r.value):i?r.value:void 0}setCached(e,t,r){if(this.cache.size>=this.cacheConfig.maxSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}this.cache.set(e,{value:t,cachedAt:Date.now(),ttl:r??this.cacheConfig.defaultTTL,usageCount:0})}invalidate(e){this.cache.delete(e)}clearCache(){this.cache.clear()}getCacheStats(){const e=Date.now(),t=Array.from(this.cache.entries()).map(([t,r])=>({key:t,age:e-r.cachedAt,usageCount:r.usageCount}));return{size:this.cache.size,maxSize:this.cacheConfig.maxSize,entries:t}}recordSuccess(e){const t=this.serviceStatus.get(e)||{health:"HEALTHY",lastSuccess:0,failureCount:0};t.health="HEALTHY",t.lastSuccess=Date.now(),t.failureCount=0,t.lastError=void 0,this.serviceStatus.set(e,t)}recordFailure(e,t){const r=this.serviceStatus.get(e)||{health:"HEALTHY",lastSuccess:0,failureCount:0};r.failureCount++,r.lastError=t,r.failureCount>=5?r.health="UNAVAILABLE":r.failureCount>=2&&(r.health="DEGRADED"),this.serviceStatus.set(e,r)}getServiceHealth(e){return this.serviceStatus.get(e)?.health??"HEALTHY"}getServiceStatus(e){return this.serviceStatus.get(e)}resetServiceHealth(e){e?this.serviceStatus.delete(e):this.serviceStatus.clear()}async withRetry(e,t){const r=this.getMaxAttempts(t.qos);let a;for(let t=1;t<=r;t++){const i=await e();if(i.ok)return i;if(a=i.error,t<r){const e=this.calculateBackoff(t);await this.sleep(e)}}return err(a)}getMaxAttempts(e){switch(e){case"CRITICAL":case"LOW":return 1;case"HIGH":return this.retryConfig.maxAttempts;case"NORMAL":return Math.max(1,this.retryConfig.maxAttempts-1)}}calculateBackoff(e){const t=this.retryConfig.initialDelayMs*Math.pow(this.retryConfig.backoffMultiplier,e-1),r=Math.min(t,this.retryConfig.maxDelayMs);if(!this.retryConfig.jitter)return r;const a=new Uint8Array(4);crypto.getRandomValues(a);const i=.75+.5*(new DataView(a.buffer).getUint32(0)/4294967295);return Math.floor(r*i)}sleep(e){return new Promise(t=>setTimeout(t,e))}}export async function registryLookupWithFallback(e,t,r,a){const i=`registry:${t}`;if("LOW"===r.qos){const e=a.getCached(i,r.qos);if(e)return ok(e)}const s=await a.withRetry(()=>e.getEntry(t),r);if(s.ok){a.recordSuccess("registry");const e={publicKey:Buffer.from(s.value.publicKey).toString("base64")};return a.setCached(i,e),ok(e)}if(a.recordFailure("registry",String(s.error)),"HIGH"===r.qos||"NORMAL"===r.qos){const e=a.getCached(i,r.qos);if(e)return ok(e)}return err(createDetailedError("REGISTRY_UNAVAILABLE",`Failed to resolve DID: ${t}`,{hint:"UNAVAILABLE"===a.getServiceHealth("registry")?"Registry service is currently unavailable. Using cached data may help.":"Registry lookup failed after retries. Check network connectivity.",suggested_action:"CRITICAL"===r.qos?"Wait for registry service to recover before retrying critical operations.":"Lower QoS level to NORMAL or LOW to use cached data.",docs:"https://private.me/docs/xbind/registry-fallback",severity:"CRITICAL"===r.qos?"critical":"error"}))}export async function sendWithTransportFallback(e,t,r,a,i){if(0===e.length)return err(createDetailedError("NO_TRANSPORT","No transport adapters configured",{hint:"Configure at least one transport adapter for envelope delivery.",suggested_action:"Add HttpsTransportAdapter or custom transport to agent configuration.",docs:"https://private.me/docs/xbind/transport",severity:"critical"}));const s=[];for(let o=0;o<e.length;o++){const c=e[o];if(!c)continue;const n=await i.withRetry(()=>c.send(t,r),a);if(n.ok)return i.recordSuccess(`transport:${o}`),ok(void 0);s.push({transport:o,error:n.error}),i.recordFailure(`transport:${o}`,n.error)}const o=s[0]?.error??"SEND_FAILED";return err(createDetailedError("TRANSPORT_EXHAUSTED",`All transport adapters failed: ${s.map(e=>e.error).join(", ")}`,{hint:"NETWORK_ERROR"===o?"Network connectivity issue detected. Check internet connection.":"TIMEOUT"===o?"Transport timeout occurred. Recipient may be unreachable or overloaded.":"RECIPIENT_UNREACHABLE"===o?"Recipient DID not found or offline.":"Transport layer failure. Check transport configuration.",suggested_action:"Verify recipient endpoint and network connectivity. Consider increasing timeout or adding fallback transports.",docs:"https://private.me/docs/xbind/transport-fallback",severity:"CRITICAL"===a.qos?"critical":"error"}))}export function enhanceError(e,t,r){const a=r.getServiceHealth(t.type.split(":")[0]??"unknown"),i="DEGRADED"===a?" Service is experiencing degraded performance.":"UNAVAILABLE"===a?" Service is currently unavailable.":"",s="CRITICAL"===t.qos?" Consider retrying when service recovers.":"HIGH"===t.qos?" Cached data may be used if available.":" Using cached data regardless of staleness.";return{...e,hint:(e.hint??"")+i+s}}
@@ -1,216 +1 @@
1
- /**
2
- * @module guardrails
3
- * Enhanced error messages with actionable suggestions for policy violations
4
- *
5
- * When policies deny requests, guardrails provide specific, actionable
6
- * guidance on how to fix the issue or what to request from admins.
7
- */
8
- /**
9
- * Policy violation error with actionable suggestions
10
- */
11
- export class PolicyDenied extends Error {
12
- rule;
13
- allowed;
14
- requested;
15
- suggestion;
16
- details;
17
- constructor(
18
- /** Which policy rule was violated */
19
- rule,
20
- /** What the policy allows */
21
- allowed,
22
- /** What was requested */
23
- requested,
24
- /** Actionable suggestion for how to fix */
25
- suggestion,
26
- /** Full policy violation details */
27
- details) {
28
- super(`Policy violation: ${rule}`);
29
- this.rule = rule;
30
- this.allowed = allowed;
31
- this.requested = requested;
32
- this.suggestion = suggestion;
33
- this.details = details;
34
- this.name = 'PolicyDenied';
35
- }
36
- /**
37
- * Format error for AI agent consumption
38
- *
39
- * Structured format optimized for LLM parsing
40
- */
41
- toAgentFormat() {
42
- return [
43
- `POLICY_VIOLATION: ${this.rule}`,
44
- `REQUESTED: ${JSON.stringify(this.requested)}`,
45
- `ALLOWED: ${JSON.stringify(this.allowed)}`,
46
- `SUGGESTION: ${this.suggestion}`,
47
- ].join('\n');
48
- }
49
- /**
50
- * Format error for human consumption
51
- *
52
- * User-friendly format with clear next steps
53
- */
54
- toUserFormat() {
55
- return [
56
- `❌ Policy Violation: ${this.rule}`,
57
- '',
58
- `You requested: ${this.formatValue(this.requested)}`,
59
- `Policy allows: ${this.formatValue(this.allowed)}`,
60
- '',
61
- `💡 Suggestion: ${this.suggestion}`,
62
- ].join('\n');
63
- }
64
- /**
65
- * Format error for logs (structured JSON)
66
- */
67
- toLogFormat() {
68
- return {
69
- error: 'PolicyDenied',
70
- rule: this.rule,
71
- allowed: this.allowed,
72
- requested: this.requested,
73
- suggestion: this.suggestion,
74
- details: this.details,
75
- timestamp: new Date().toISOString(),
76
- };
77
- }
78
- formatValue(value) {
79
- if (typeof value === 'number') {
80
- // Format currency if it looks like money
81
- if (this.rule.includes('amount') || this.rule.includes('Amount')) {
82
- return `$${value.toLocaleString()}`;
83
- }
84
- return value.toLocaleString();
85
- }
86
- if (Array.isArray(value)) {
87
- return value.map((v) => `"${String(v)}"`).join(', ');
88
- }
89
- return JSON.stringify(value);
90
- }
91
- }
92
- /**
93
- * Guardrail builder - fluent API for creating policy errors with suggestions
94
- *
95
- * @example
96
- * ```typescript
97
- * const error = Guardrails.amountExceeded({
98
- * requested: 5000,
99
- * limit: 1000,
100
- * type: 'per-transaction'
101
- * });
102
- *
103
- * throw error;
104
- * // ❌ Policy Violation: maxAmount
105
- * // You requested: $5,000
106
- * // Policy allows: $1,000
107
- * // 💡 Suggestion: Reduce amount to $1,000 or request policy update from admin
108
- * ```
109
- */
110
- export class Guardrails {
111
- /**
112
- * Amount exceeded (per-transaction limit)
113
- */
114
- static amountExceeded(options) {
115
- let rule;
116
- let suggestion;
117
- switch (options.type) {
118
- case 'per-transaction':
119
- rule = 'maxAmount';
120
- suggestion = `Reduce amount to $${options.limit.toLocaleString()} or request policy update from admin`;
121
- break;
122
- case 'daily':
123
- rule = 'dailyAmount';
124
- suggestion = `Reduce amount to stay within daily limit of $${options.limit.toLocaleString()}, or wait until tomorrow`;
125
- break;
126
- case 'monthly':
127
- rule = 'monthlyAmount';
128
- suggestion = `Reduce amount to stay within monthly limit of $${options.limit.toLocaleString()}, or wait until next month`;
129
- break;
130
- }
131
- return new PolicyDenied(rule, options.limit, options.requested, suggestion);
132
- }
133
- /**
134
- * Rate limit exceeded
135
- */
136
- static rateLimitExceeded(options) {
137
- const windowSec = options.windowMs / 1000;
138
- const suggestion = windowSec >= 60
139
- ? `Wait ${Math.ceil(windowSec / 60)} minutes before making additional calls`
140
- : `Wait ${windowSec} seconds before making additional calls`;
141
- return new PolicyDenied('callsPerMinute', options.limit, options.current + 1, suggestion);
142
- }
143
- /**
144
- * Tool not allowed
145
- */
146
- static toolDenied(options) {
147
- const [service] = options.tool.split(':');
148
- const suggestion = options.allowed.length === 0
149
- ? `No tools are currently allowed. Request approval from admin to enable "${options.tool}"`
150
- : `Request approval from admin to add "${options.tool}" or "${service}:*" to allowed tools`;
151
- return new PolicyDenied('allowedTools', options.allowed, options.tool, suggestion);
152
- }
153
- /**
154
- * Scope not allowed
155
- */
156
- static scopeDenied(options) {
157
- const suggestion = options.allowed.length === 0
158
- ? `No scopes are currently allowed. Request approval from admin to enable "${options.scope}"`
159
- : `Request approval from admin to add "${options.scope}" to allowed scopes`;
160
- return new PolicyDenied('allowedScopes', options.allowed, options.scope, suggestion);
161
- }
162
- /**
163
- * Field filter denied (data access restriction)
164
- */
165
- static fieldDenied(options) {
166
- const suggestion = `Field "${options.field}" is restricted. Contact admin to update policy.fieldFilters for "${options.tool}"`;
167
- return new PolicyDenied('fieldFilters', options.allowed, options.field, suggestion);
168
- }
169
- /**
170
- * Time window restriction
171
- */
172
- static timeWindowDenied(options) {
173
- const suggestion = `Actions are only allowed between ${options.allowedStart} and ${options.allowedEnd}. Current time is ${options.current.toTimeString()}`;
174
- return new PolicyDenied('timeWindow', `${options.allowedStart} - ${options.allowedEnd}`, options.current.toTimeString(), suggestion);
175
- }
176
- /**
177
- * Generic policy violation
178
- */
179
- static custom(options) {
180
- return new PolicyDenied(options.rule, options.allowed, options.requested, options.suggestion);
181
- }
182
- }
183
- /**
184
- * Helper: Extract actionable suggestion from AgentError details
185
- *
186
- * Used by agent.call() to convert PolicyViolationDetails into PolicyDenied
187
- */
188
- export function extractSuggestion(details) {
189
- return details.fix ?? 'Contact administrator to update policy';
190
- }
191
- /**
192
- * Helper: Convert PolicyViolationDetails to PolicyDenied
193
- */
194
- export function toPolicyDenied(details) {
195
- let rule;
196
- switch (details.constraint) {
197
- case 'amountPerTxn':
198
- rule = 'maxAmount';
199
- break;
200
- case 'dailyAmount':
201
- rule = 'dailyAmount';
202
- break;
203
- case 'callsPerMinute':
204
- rule = 'callsPerMinute';
205
- break;
206
- case 'scope':
207
- rule = 'allowedScopes';
208
- break;
209
- case 'tool':
210
- rule = 'allowedTools';
211
- break;
212
- default:
213
- rule = String(details.constraint);
214
- }
215
- return new PolicyDenied(rule, details.allowed, details.requested, details.fix, details);
216
- }
1
+ export class PolicyDenied extends Error{rule;allowed;requested;suggestion;details;constructor(e,t,o,i,l){super(`Policy violation: ${e}`),this.rule=e,this.allowed=t,this.requested=o,this.suggestion=i,this.details=l,this.name="PolicyDenied"}toAgentFormat(){return[`POLICY_VIOLATION: ${this.rule}`,`REQUESTED: ${JSON.stringify(this.requested)}`,`ALLOWED: ${JSON.stringify(this.allowed)}`,`SUGGESTION: ${this.suggestion}`].join("\n")}toUserFormat(){return[`❌ Policy Violation: ${this.rule}`,"",`You requested: ${this.formatValue(this.requested)}`,`Policy allows: ${this.formatValue(this.allowed)}`,"",`💡 Suggestion: ${this.suggestion}`].join("\n")}toLogFormat(){return{error:"PolicyDenied",rule:this.rule,allowed:this.allowed,requested:this.requested,suggestion:this.suggestion,details:this.details,timestamp:(new Date).toISOString()}}formatValue(e){return"number"==typeof e?this.rule.includes("amount")||this.rule.includes("Amount")?`$${e.toLocaleString()}`:e.toLocaleString():Array.isArray(e)?e.map(e=>`"${String(e)}"`).join(", "):JSON.stringify(e)}}export class Guardrails{static amountExceeded(e){let t,o;switch(e.type){case"per-transaction":t="maxAmount",o=`Reduce amount to $${e.limit.toLocaleString()} or request policy update from admin`;break;case"daily":t="dailyAmount",o=`Reduce amount to stay within daily limit of $${e.limit.toLocaleString()}, or wait until tomorrow`;break;case"monthly":t="monthlyAmount",o=`Reduce amount to stay within monthly limit of $${e.limit.toLocaleString()}, or wait until next month`}return new PolicyDenied(t,e.limit,e.requested,o)}static rateLimitExceeded(e){const t=e.windowMs/1e3,o=t>=60?`Wait ${Math.ceil(t/60)} minutes before making additional calls`:`Wait ${t} seconds before making additional calls`;return new PolicyDenied("callsPerMinute",e.limit,e.current+1,o)}static toolDenied(e){const[t]=e.tool.split(":"),o=0===e.allowed.length?`No tools are currently allowed. Request approval from admin to enable "${e.tool}"`:`Request approval from admin to add "${e.tool}" or "${t}:*" to allowed tools`;return new PolicyDenied("allowedTools",e.allowed,e.tool,o)}static scopeDenied(e){const t=0===e.allowed.length?`No scopes are currently allowed. Request approval from admin to enable "${e.scope}"`:`Request approval from admin to add "${e.scope}" to allowed scopes`;return new PolicyDenied("allowedScopes",e.allowed,e.scope,t)}static fieldDenied(e){const t=`Field "${e.field}" is restricted. Contact admin to update policy.fieldFilters for "${e.tool}"`;return new PolicyDenied("fieldFilters",e.allowed,e.field,t)}static timeWindowDenied(e){const t=`Actions are only allowed between ${e.allowedStart} and ${e.allowedEnd}. Current time is ${e.current.toTimeString()}`;return new PolicyDenied("timeWindow",`${e.allowedStart} - ${e.allowedEnd}`,e.current.toTimeString(),t)}static custom(e){return new PolicyDenied(e.rule,e.allowed,e.requested,e.suggestion)}}export function extractSuggestion(e){return e.fix??"Contact administrator to update policy"}export function toPolicyDenied(e){let t;switch(e.constraint){case"amountPerTxn":t="maxAmount";break;case"dailyAmount":t="dailyAmount";break;case"callsPerMinute":t="callsPerMinute";break;case"scope":t="allowedScopes";break;case"tool":t="allowedTools";break;default:t=String(e.constraint)}return new PolicyDenied(t,e.allowed,e.requested,e.fix,e)}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @module health-check
3
+ * Health check endpoint for operational monitoring and load balancer probes.
4
+ *
5
+ * Provides three probe types following Kubernetes health check patterns:
6
+ * - **Startup probe**: Verify initialization complete (one-time check)
7
+ * - **Readiness probe**: Verify dependencies ready (registry, transport)
8
+ * - **Liveness probe**: Verify service responsive (self-check)
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createHealthChecker } from '@private.me/xbind';
13
+ *
14
+ * const checker = createHealthChecker({
15
+ * registry: myTrustRegistry,
16
+ * transport: myTransport
17
+ * });
18
+ *
19
+ * // Load balancer endpoint
20
+ * app.get('/health', async (req, res) => {
21
+ * const status = await checker.check();
22
+ * res.status(status.healthy ? 200 : 503).json(status);
23
+ * });
24
+ * ```
25
+ */
26
+ import type { TrustRegistry } from './trust-registry.js';
27
+ import type { XailTransportAdapter } from './transport.js';
28
+ /**
29
+ * Health check probe type.
30
+ */
31
+ export type ProbeType = 'startup' | 'readiness' | 'liveness';
32
+ /**
33
+ * Health status for individual component.
34
+ */
35
+ export interface ComponentHealth {
36
+ /** Component name (e.g., 'registry', 'transport') */
37
+ readonly name: string;
38
+ /** Component status: 'healthy', 'degraded', 'unhealthy' */
39
+ readonly status: 'healthy' | 'degraded' | 'unhealthy';
40
+ /** Human-readable status message */
41
+ readonly message?: string;
42
+ /** Response time in milliseconds */
43
+ readonly latencyMs?: number;
44
+ /** Last check timestamp (ISO 8601) */
45
+ readonly lastCheck: string;
46
+ }
47
+ /**
48
+ * Overall health check result.
49
+ */
50
+ export interface HealthStatus {
51
+ /** Overall health: true if all components healthy, false otherwise */
52
+ readonly healthy: boolean;
53
+ /** Timestamp of health check (ISO 8601) */
54
+ readonly timestamp: string;
55
+ /** Uptime in seconds since checker created */
56
+ readonly uptimeSeconds: number;
57
+ /** Individual component health statuses */
58
+ readonly components: ComponentHealth[];
59
+ /** Additional metadata */
60
+ readonly metadata?: {
61
+ /** Service name */
62
+ readonly service?: string;
63
+ /** Service version */
64
+ readonly version?: string;
65
+ /** Environment (development, staging, production) */
66
+ readonly environment?: string;
67
+ };
68
+ }
69
+ /**
70
+ * Configuration for health checker.
71
+ */
72
+ export interface HealthCheckerOptions {
73
+ /** Trust registry to check (optional) */
74
+ readonly registry?: TrustRegistry;
75
+ /** Transport adapter to check (optional) */
76
+ readonly transport?: XailTransportAdapter;
77
+ /** Service name for metadata */
78
+ readonly serviceName?: string;
79
+ /** Service version for metadata */
80
+ readonly version?: string;
81
+ /** Environment name for metadata */
82
+ readonly environment?: string;
83
+ /** Timeout for component checks in milliseconds (default: 5000) */
84
+ readonly timeoutMs?: number;
85
+ /** Enable startup probe tracking (default: true) */
86
+ readonly enableStartupProbe?: boolean;
87
+ }
88
+ /**
89
+ * Health checker instance.
90
+ */
91
+ export interface HealthChecker {
92
+ /**
93
+ * Perform health check on all configured components.
94
+ * @param probe - Probe type (defaults to 'readiness')
95
+ */
96
+ check(probe?: ProbeType): Promise<HealthStatus>;
97
+ /**
98
+ * Mark startup probe as complete.
99
+ * Call after initialization finishes (e.g., after agent.register()).
100
+ */
101
+ markStartupComplete(): void;
102
+ /**
103
+ * Check if startup probe is complete.
104
+ */
105
+ isStartupComplete(): boolean;
106
+ }
107
+ /**
108
+ * Create a health checker instance.
109
+ *
110
+ * @param options - Configuration options
111
+ * @returns Health checker instance
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * const checker = createHealthChecker({
116
+ * registry: agent.registry,
117
+ * transport: agent.transport,
118
+ * serviceName: 'my-service',
119
+ * version: '1.0.0'
120
+ * });
121
+ *
122
+ * // Mark startup complete after initialization
123
+ * await agent.register();
124
+ * checker.markStartupComplete();
125
+ *
126
+ * // Check health
127
+ * const status = await checker.check('readiness');
128
+ * console.log(status.healthy ? 'Ready' : 'Not ready');
129
+ * ```
130
+ */
131
+ export declare function createHealthChecker(options?: HealthCheckerOptions): HealthChecker;
132
+ /**
133
+ * Express/Fastify middleware helper for health endpoint.
134
+ *
135
+ * @param checker - Health checker instance
136
+ * @param probe - Probe type (default: 'readiness')
137
+ * @returns Request handler
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const checker = createHealthChecker({ registry, transport });
142
+ *
143
+ * // Express
144
+ * app.get('/health', healthEndpoint(checker));
145
+ * app.get('/health/startup', healthEndpoint(checker, 'startup'));
146
+ * app.get('/health/liveness', healthEndpoint(checker, 'liveness'));
147
+ * app.get('/health/readiness', healthEndpoint(checker, 'readiness'));
148
+ * ```
149
+ */
150
+ export declare function healthEndpoint(checker: HealthChecker, probe?: ProbeType): (req: any, res: any) => Promise<void>;
@@ -0,0 +1 @@
1
+ class HealthCheckerImpl{startTime;registry;transport;serviceName;version;environment;timeoutMs;enableStartupProbe;startupComplete=!1;constructor(t={}){this.startTime=Date.now(),this.registry=t.registry,this.transport=t.transport,this.serviceName=t.serviceName,this.version=t.version,this.environment=t.environment,this.timeoutMs=t.timeoutMs??5e3,this.enableStartupProbe=t.enableStartupProbe??!0}markStartupComplete(){this.startupComplete=!0}isStartupComplete(){return this.startupComplete}async check(t="readiness"){const e=(new Date).toISOString(),s=Math.floor((Date.now()-this.startTime)/1e3);if("startup"===t&&this.enableStartupProbe){const t=[{name:"startup",status:this.startupComplete?"healthy":"unhealthy",message:this.startupComplete?"Initialization complete":"Waiting for startup completion (call markStartupComplete())",lastCheck:e}];return{healthy:this.startupComplete,timestamp:e,uptimeSeconds:s,components:t,metadata:this.buildMetadata()}}if("liveness"===t){return{healthy:!0,timestamp:e,uptimeSeconds:s,components:[{name:"self",status:"healthy",message:"Service responsive",lastCheck:e}],metadata:this.buildMetadata()}}const a=[];if(this.registry){const t=await this.checkRegistry();a.push(t)}if(this.transport){const t=await this.checkTransport();a.push(t)}0===a.length&&a.push({name:"self",status:"healthy",message:"No dependencies configured",lastCheck:e});return{healthy:a.every(t=>"healthy"===t.status),timestamp:e,uptimeSeconds:s,components:a,metadata:this.buildMetadata()}}buildMetadata(){const t={};return this.serviceName&&(t.service=this.serviceName),this.version&&(t.version=this.version),this.environment&&(t.environment=this.environment),Object.keys(t).length>0?t:void 0}async checkRegistry(){const t=Date.now(),e=(new Date).toISOString();try{if(!this.registry)return{name:"registry",status:"unhealthy",message:"Registry not configured",lastCheck:e};const s=await this.withTimeout(this.registry.resolve("did:xail:health_check_probe"),this.timeoutMs),a=Date.now()-t;if(s&&"object"==typeof s&&"ok"in s){if(s.ok)return{name:"registry",status:"healthy",message:"Registry responding",latencyMs:a,lastCheck:e};{const t="string"==typeof s.error?s.error:"UNKNOWN";return t.includes("NOT_FOUND")?{name:"registry",status:"healthy",message:"Registry responding",latencyMs:a,lastCheck:e}:{name:"registry",status:"unhealthy",message:`Registry error: ${t}`,latencyMs:a,lastCheck:e}}}return{name:"registry",status:"healthy",message:"Registry responding",latencyMs:a,lastCheck:e}}catch(s){const a=Date.now()-t,r=s instanceof Error?s.message:"Unknown error";return r.toLowerCase().includes("timeout")?{name:"registry",status:"degraded",message:`Registry slow (>${this.timeoutMs}ms)`,latencyMs:a,lastCheck:e}:{name:"registry",status:"unhealthy",message:`Registry error: ${r}`,latencyMs:a,lastCheck:e}}}async checkTransport(){const t=Date.now(),e=(new Date).toISOString();try{if(!this.transport)return{name:"transport",status:"unhealthy",message:"Transport not configured",lastCheck:e};return{name:"transport",status:"healthy",message:"Transport adapter available",latencyMs:Date.now()-t,lastCheck:e}}catch(s){const a=Date.now()-t;return{name:"transport",status:"unhealthy",message:`Transport error: ${s instanceof Error?s.message:"Unknown error"}`,latencyMs:a,lastCheck:e}}}async withTimeout(t,e){return Promise.race([t,new Promise((t,s)=>setTimeout(()=>s(new Error("Health check timeout")),e))])}}export function createHealthChecker(t){return new HealthCheckerImpl(t)}export function healthEndpoint(t,e="readiness"){return async(s,a)=>{try{const s=await t.check(e),r=s.healthy?200:503;a.status(r).json(s)}catch(t){const e=t instanceof Error?t.message:"Unknown error";a.status(503).json({healthy:!1,timestamp:(new Date).toISOString(),uptimeSeconds:0,components:[{name:"health-check",status:"unhealthy",message:`Health check failed: ${e}`,lastCheck:(new Date).toISOString()}]})}}}