@private.me/xbind 3.0.1 → 3.0.2

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 (210) hide show
  1. package/README.md +55 -14
  2. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
  3. package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
  4. package/dist-standalone/_deps/shared/cjs/index.js +463 -1
  5. package/dist-standalone/_deps/shared/cjs/types.js +315 -1
  6. package/dist-standalone/_deps/shared/errors.js +244 -1
  7. package/dist-standalone/_deps/shared/index.js +72 -1
  8. package/dist-standalone/_deps/shared/types.js +86 -1
  9. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  10. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  20. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  21. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  28. package/dist-standalone/_deps/xchange/errors.js +1 -1
  29. package/dist-standalone/_deps/xchange/index.js +1 -1
  30. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  31. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  32. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  33. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  34. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  40. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  41. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  42. package/dist-standalone/_deps/xregistry/index.js +1 -1
  43. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  44. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  45. package/dist-standalone/_deps/xregistry/types.js +1 -1
  46. package/dist-standalone/agent-call.js +659 -1
  47. package/dist-standalone/agent-sdk.js +328 -1
  48. package/dist-standalone/agent.js +1800 -1
  49. package/dist-standalone/approval.js +193 -1
  50. package/dist-standalone/async-iterators.js +382 -1
  51. package/dist-standalone/auth.js +219 -1
  52. package/dist-standalone/auto-accept.js +229 -1
  53. package/dist-standalone/backup-config.js +201 -1
  54. package/dist-standalone/backup.js +326 -1
  55. package/dist-standalone/batch-operations.js +388 -1
  56. package/dist-standalone/cancellation.js +477 -1
  57. package/dist-standalone/checkpoint.js +186 -1
  58. package/dist-standalone/circuit-breaker.js +468 -1
  59. package/dist-standalone/cjs/agent-call.js +701 -1
  60. package/dist-standalone/cjs/agent-sdk.js +332 -1
  61. package/dist-standalone/cjs/agent.js +1837 -1
  62. package/dist-standalone/cjs/approval.js +199 -1
  63. package/dist-standalone/cjs/async-iterators.js +392 -1
  64. package/dist-standalone/cjs/auth.js +225 -1
  65. package/dist-standalone/cjs/auto-accept.js +233 -1
  66. package/dist-standalone/cjs/backup-config.js +207 -1
  67. package/dist-standalone/cjs/backup.js +330 -1
  68. package/dist-standalone/cjs/batch-operations.js +397 -1
  69. package/dist-standalone/cjs/cancellation.js +490 -1
  70. package/dist-standalone/cjs/checkpoint.js +193 -1
  71. package/dist-standalone/cjs/circuit-breaker.js +476 -1
  72. package/dist-standalone/cjs/cli/init.js +492 -1
  73. package/dist-standalone/cjs/config-validation.js +522 -1
  74. package/dist-standalone/cjs/connect.js +312 -1
  75. package/dist-standalone/cjs/connection-pool.js +506 -1
  76. package/dist-standalone/cjs/correlation-id.js +339 -1
  77. package/dist-standalone/cjs/crypto-utils.js +176 -1
  78. package/dist-standalone/cjs/debug-mode.js +534 -1
  79. package/dist-standalone/cjs/did-document.js +101 -1
  80. package/dist-standalone/cjs/did-privateme.js +130 -1
  81. package/dist-standalone/cjs/did-web.js +201 -1
  82. package/dist-standalone/cjs/discovery.js +462 -1
  83. package/dist-standalone/cjs/dual-mode.js +251 -1
  84. package/dist-standalone/cjs/email-templates.js +313 -1
  85. package/dist-standalone/cjs/email-transport.js +239 -1
  86. package/dist-standalone/cjs/envelope.js +538 -1
  87. package/dist-standalone/cjs/errors.js +913 -1
  88. package/dist-standalone/cjs/event-emitter.js +461 -1
  89. package/dist-standalone/cjs/gateway-state.js +55 -1
  90. package/dist-standalone/cjs/gateway-transport.js +120 -1
  91. package/dist-standalone/cjs/graceful-degradation.js +403 -1
  92. package/dist-standalone/cjs/guardrails.js +223 -1
  93. package/dist-standalone/cjs/health-check.js +336 -1
  94. package/dist-standalone/cjs/http-compat.js +272 -1
  95. package/dist-standalone/cjs/http-status-map.js +571 -1
  96. package/dist-standalone/cjs/identity.js +645 -1
  97. package/dist-standalone/cjs/index.js +406 -1
  98. package/dist-standalone/cjs/invitation.js +421 -1
  99. package/dist-standalone/cjs/invite.js +328 -1
  100. package/dist-standalone/cjs/key-agreement.js +335 -1
  101. package/dist-standalone/cjs/lazy-init.js +300 -1
  102. package/dist-standalone/cjs/logger.js +291 -1
  103. package/dist-standalone/cjs/mdns-discovery.js +202 -1
  104. package/dist-standalone/cjs/nonce-store.js +80 -1
  105. package/dist-standalone/cjs/pairing-manager.js +223 -1
  106. package/dist-standalone/cjs/plugin-system.js +264 -1
  107. package/dist-standalone/cjs/plugins/logging.js +168 -1
  108. package/dist-standalone/cjs/plugins/metrics.js +181 -1
  109. package/dist-standalone/cjs/plugins/validation.js +302 -1
  110. package/dist-standalone/cjs/policy.js +320 -1
  111. package/dist-standalone/cjs/progress-callbacks.js +583 -1
  112. package/dist-standalone/cjs/redis-nonce-store.js +76 -1
  113. package/dist-standalone/cjs/registry-middleware.js +50 -1
  114. package/dist-standalone/cjs/retry-strategies.js +544 -1
  115. package/dist-standalone/cjs/retry-transport.js +102 -1
  116. package/dist-standalone/cjs/runtime/browser.js +533 -1
  117. package/dist-standalone/cjs/runtime/edge.js +526 -1
  118. package/dist-standalone/cjs/runtime/react-native.js +394 -1
  119. package/dist-standalone/cjs/security-policy.js +245 -1
  120. package/dist-standalone/cjs/serialization.js +1040 -1
  121. package/dist-standalone/cjs/split-channel.js +225 -1
  122. package/dist-standalone/cjs/subscription-proof.js +230 -1
  123. package/dist-standalone/cjs/succession.js +148 -1
  124. package/dist-standalone/cjs/timeouts.js +412 -1
  125. package/dist-standalone/cjs/trace-context.js +424 -1
  126. package/dist-standalone/cjs/trace-spans.js +495 -1
  127. package/dist-standalone/cjs/transport.js +63 -1
  128. package/dist-standalone/cjs/trust-registry.js +991 -1
  129. package/dist-standalone/cjs/types/error-response.js +56 -1
  130. package/dist-standalone/cjs/vault-auth.js +178 -1
  131. package/dist-standalone/cjs/vault-store-loader.js +194 -1
  132. package/dist-standalone/cjs/verify.js +25 -1
  133. package/dist-standalone/cjs/version-info.js +543 -1
  134. package/dist-standalone/cjs/xfetch.js +340 -1
  135. package/dist-standalone/cli/init.js +455 -1
  136. package/dist-standalone/cli/setup.js +514 -1
  137. package/dist-standalone/cli/types.js +27 -1
  138. package/dist-standalone/cli/xbind.js +148 -1
  139. package/dist-standalone/config-validation.js +513 -1
  140. package/dist-standalone/connect.js +274 -1
  141. package/dist-standalone/connection-pool.js +500 -1
  142. package/dist-standalone/correlation-id.js +326 -1
  143. package/dist-standalone/crypto-utils.js +157 -1
  144. package/dist-standalone/debug-mode.js +510 -1
  145. package/dist-standalone/did-document.js +96 -1
  146. package/dist-standalone/did-privateme.js +121 -1
  147. package/dist-standalone/did-web.js +196 -1
  148. package/dist-standalone/discovery.js +458 -1
  149. package/dist-standalone/dual-mode.js +247 -1
  150. package/dist-standalone/email-templates.js +309 -1
  151. package/dist-standalone/email-transport.js +232 -1
  152. package/dist-standalone/envelope.js +525 -1
  153. package/dist-standalone/errors.js +896 -1
  154. package/dist-standalone/event-emitter.js +456 -1
  155. package/dist-standalone/gateway-state.js +51 -1
  156. package/dist-standalone/gateway-transport.js +116 -1
  157. package/dist-standalone/graceful-degradation.js +396 -1
  158. package/dist-standalone/guardrails.js +216 -1
  159. package/dist-standalone/health-check.js +332 -1
  160. package/dist-standalone/http-compat.js +267 -1
  161. package/dist-standalone/http-status-map.js +561 -1
  162. package/dist-standalone/identity.js +619 -1
  163. package/dist-standalone/index.js +78 -1
  164. package/dist-standalone/invitation.js +415 -1
  165. package/dist-standalone/invite.js +324 -1
  166. package/dist-standalone/key-agreement.js +325 -1
  167. package/dist-standalone/lazy-init.js +295 -1
  168. package/dist-standalone/logger.js +285 -1
  169. package/dist-standalone/mdns-discovery.js +195 -1
  170. package/dist-standalone/nonce-store.js +76 -1
  171. package/dist-standalone/pairing-manager.js +219 -1
  172. package/dist-standalone/plugin-system.js +257 -1
  173. package/dist-standalone/plugins/logging.js +163 -1
  174. package/dist-standalone/plugins/metrics.js +176 -1
  175. package/dist-standalone/plugins/validation.js +297 -1
  176. package/dist-standalone/policy.js +315 -1
  177. package/dist-standalone/progress-callbacks.js +576 -1
  178. package/dist-standalone/redis-nonce-store.js +72 -1
  179. package/dist-standalone/registry-middleware.js +47 -1
  180. package/dist-standalone/retry-strategies.js +534 -1
  181. package/dist-standalone/retry-transport.js +98 -1
  182. package/dist-standalone/runtime/browser.js +516 -1
  183. package/dist-standalone/runtime/edge.js +511 -1
  184. package/dist-standalone/runtime/react-native.js +383 -1
  185. package/dist-standalone/security-policy.js +239 -1
  186. package/dist-standalone/serialization.js +1031 -1
  187. package/dist-standalone/split-channel.js +219 -1
  188. package/dist-standalone/subscription-proof.js +224 -1
  189. package/dist-standalone/succession.js +142 -1
  190. package/dist-standalone/timeouts.js +398 -1
  191. package/dist-standalone/trace-context.js +414 -1
  192. package/dist-standalone/trace-spans.js +488 -1
  193. package/dist-standalone/transport.js +59 -1
  194. package/dist-standalone/trust-registry.js +950 -1
  195. package/dist-standalone/types/error-response.js +52 -1
  196. package/dist-standalone/vault-auth.js +174 -1
  197. package/dist-standalone/vault-store-loader.js +187 -1
  198. package/dist-standalone/verify.js +16 -1
  199. package/dist-standalone/version-info.js +530 -1
  200. package/dist-standalone/xfetch.js +335 -1
  201. package/package.json +4 -13
  202. package/share1.dat +0 -0
  203. package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
  204. package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
  205. package/dist-standalone/_deps/shared/cjs/package.json +0 -1
  206. package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
  207. package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
  208. package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
  209. package/dist-standalone/cjs/package.json +0 -3
  210. package/dist-standalone/package.json +0 -10
@@ -1 +1,506 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ConnectionPool=void 0,exports.getGlobalPool=getGlobalPool,exports.resetGlobalPool=resetGlobalPool;class ConnectionPool{options;connections=new Map;metrics;cleanupInterval=null;constructor(t={}){this.options={maxConnections:t.maxConnections??10,minConnections:t.minConnections??2,keepAliveTimeout:t.keepAliveTimeout??3e4,idleTimeout:t.idleTimeout??6e4,requestTimeout:t.requestTimeout??3e4,enableMetrics:t.enableMetrics??!0,retryOnFailure:t.retryOnFailure??!0,maxRetries:t.maxRetries??3},this.metrics={totalRequests:0,reuseCount:0,failedRequests:0,requestDurations:[],byOrigin:new Map},this.startCleanup()}async fetch(t,e){const s=Date.now(),n=this.getOrigin(t);try{const o=await this.acquireConnection(n);this.options.enableMetrics&&(this.metrics.totalRequests++,o.requestCount>0&&this.metrics.reuseCount++);const i={...e,signal:e?.signal??o.controller.signal,keepalive:!0},r=await fetch(t,i);if(o.requestCount++,o.lastUsedAt=Date.now(),o.state="idle",this.options.enableMetrics){const t=Date.now()-s;this.recordMetrics(n,t)}return r}catch(s){if(this.options.enableMetrics&&this.metrics.failedRequests++,this.options.retryOnFailure&&!0!==e?.signal?.aborted)return this.retryRequest(t,e,1);throw s}}async retryRequest(t,e,s){if(s>this.options.maxRetries)throw new Error(`Request failed after ${this.options.maxRetries} retries: ${t}`);const n=100*Math.pow(2,s-1);await new Promise(t=>setTimeout(t,n));const o=this.getOrigin(t);try{const s=await this.acquireConnection(o),n={...e,signal:e?.signal??s.controller.signal,keepalive:!0},i=await fetch(t,n);return s.requestCount++,s.lastUsedAt=Date.now(),s.state="idle",i}catch(n){if(s>=this.options.maxRetries)throw new Error(`Request failed after ${this.options.maxRetries} retries: ${t}`);return this.retryRequest(t,e,s+1)}}async acquireConnection(t){let e=this.connections.get(t);e||(e=[],this.connections.set(t,e));const s=e.find(t=>"idle"===t.state);if(s)return s.state="active",s;if(e.length<this.options.maxConnections){const s=this.createConnection(t);return e.push(s),s}return this.waitForConnection(t)}async waitForConnection(t){const e=Date.now(),s=this.options.requestTimeout;for(;Date.now()-e<s;){const e=this.connections.get(t);if(!e)throw new Error(`Connection pool for ${t} disappeared`);const s=e.find(t=>"idle"===t.state);if(s)return s.state="active",s;await new Promise(t=>setTimeout(t,10))}throw new Error(`Timeout waiting for connection to ${t} after ${s}ms`)}createConnection(t){const e=new Uint8Array(8);crypto.getRandomValues(e);return{id:`conn_${Array.from(e).map(t=>t.toString(16).padStart(2,"0")).join("")}`,origin:t,createdAt:Date.now(),lastUsedAt:Date.now(),requestCount:0,state:"active",controller:new AbortController}}getOrigin(t){try{const e=new URL(t);return`${e.protocol}//${e.host}`}catch(e){throw new Error(`Invalid URL: ${t}`)}}recordMetrics(t,e){this.metrics.requestDurations.push(e),this.metrics.requestDurations.length>1e3&&this.metrics.requestDurations.shift();let s=this.metrics.byOrigin.get(t);s||(s={requests:0,durations:[]},this.metrics.byOrigin.set(t,s)),s.requests++,s.durations.push(e),s.durations.length>100&&s.durations.shift()}getMetrics(){let t=0,e=0,s=0;const n=new Map;for(const[o,i]of this.connections.entries()){t+=i.length,e+=i.filter(t=>"active"===t.state).length,s+=i.filter(t=>"idle"===t.state).length;const r=this.metrics.byOrigin.get(o);if(r){const t=r.durations.length>0?r.durations.reduce((t,e)=>t+e,0)/r.durations.length:0;n.set(o,{connections:i.length,requests:r.requests,avgDuration:Math.round(t)})}}const o=this.metrics.requestDurations.length>0?Math.round(this.metrics.requestDurations.reduce((t,e)=>t+e,0)/this.metrics.requestDurations.length):0,i=this.metrics.totalRequests>0?this.metrics.reuseCount/this.metrics.totalRequests:0;return{totalConnections:t,activeConnections:e,idleConnections:s,totalRequests:this.metrics.totalRequests,reuseCount:this.metrics.reuseCount,failedRequests:this.metrics.failedRequests,avgRequestDuration:o,hitRate:i,byOrigin:n}}resetMetrics(){this.metrics={totalRequests:0,reuseCount:0,failedRequests:0,requestDurations:[],byOrigin:new Map}}startCleanup(){this.cleanupInterval=setInterval(()=>{this.cleanup()},1e4)}cleanup(){const t=Date.now();for(const[e,s]of this.connections.entries()){const n=s.filter(e=>t-e.lastUsedAt>this.options.idleTimeout&&"idle"===e.state?(e.controller.abort(),e.state="closed",!1):"closed"!==e.state);for(;n.length<this.options.minConnections&&n.length<this.options.maxConnections;){const t=this.createConnection(e);t.state="idle",n.push(t)}n.length>0?this.connections.set(e,n):this.connections.delete(e)}}cleanupNow(){this.cleanup()}async close(){this.cleanupInterval&&(clearInterval(this.cleanupInterval),this.cleanupInterval=null);for(const[t,e]of this.connections.entries())for(const t of e)t.controller.abort(),t.state="closed";this.connections.clear()}getConnectionCount(t){const e=this.connections.get(t);return e?e.length:0}getOrigins(){return Array.from(this.connections.keys())}isHealthy(){const t=this.getMetrics();return!((t.totalRequests>0?t.failedRequests/t.totalRequests:0)>.1)&&!(t.avgRequestDuration>5e3)}}exports.ConnectionPool=ConnectionPool;let globalPool=null;function getGlobalPool(t){return globalPool||(globalPool=new ConnectionPool(t)),globalPool}async function resetGlobalPool(){globalPool&&(await globalPool.close(),globalPool=null)}
1
+ "use strict";
2
+ /**
3
+ * @module connection-pool
4
+ * HTTP connection pooling for xBind registry/gateway requests
5
+ *
6
+ * Provides connection reuse, keep-alive support, and automatic cleanup
7
+ * to enhance performance for applications making frequent requests.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const pool = new ConnectionPool({
12
+ * maxConnections: 50,
13
+ * keepAliveTimeout: 30000,
14
+ * });
15
+ *
16
+ * const response = await pool.fetch('https://api.example.com/data');
17
+ * const data = await response.json();
18
+ * ```
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.ConnectionPool = void 0;
22
+ exports.getGlobalPool = getGlobalPool;
23
+ exports.resetGlobalPool = resetGlobalPool;
24
+ /**
25
+ * Connection pool for HTTP requests
26
+ *
27
+ * Manages connection lifecycle, keep-alive, and automatic cleanup
28
+ * to optimize performance for frequent requests to the same origins.
29
+ */
30
+ class ConnectionPool {
31
+ options;
32
+ connections = new Map();
33
+ metrics;
34
+ cleanupInterval = null;
35
+ /**
36
+ * Create a new connection pool
37
+ *
38
+ * @param options - Pool configuration options
39
+ */
40
+ constructor(options = {}) {
41
+ this.options = {
42
+ maxConnections: options.maxConnections ?? 10,
43
+ minConnections: options.minConnections ?? 2,
44
+ keepAliveTimeout: options.keepAliveTimeout ?? 30000,
45
+ idleTimeout: options.idleTimeout ?? 60000,
46
+ requestTimeout: options.requestTimeout ?? 30000,
47
+ enableMetrics: options.enableMetrics ?? true,
48
+ retryOnFailure: options.retryOnFailure ?? true,
49
+ maxRetries: options.maxRetries ?? 3,
50
+ };
51
+ this.metrics = {
52
+ totalRequests: 0,
53
+ reuseCount: 0,
54
+ failedRequests: 0,
55
+ requestDurations: [],
56
+ byOrigin: new Map(),
57
+ };
58
+ // Start automatic cleanup
59
+ this.startCleanup();
60
+ }
61
+ /**
62
+ * Make an HTTP request using the connection pool
63
+ *
64
+ * @param url - Target URL
65
+ * @param init - Fetch options
66
+ * @returns Response promise
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const response = await pool.fetch('https://api.example.com/data', {
71
+ * method: 'POST',
72
+ * headers: { 'Content-Type': 'application/json' },
73
+ * body: JSON.stringify({ key: 'value' }),
74
+ * });
75
+ * ```
76
+ */
77
+ async fetch(url, init) {
78
+ const startTime = Date.now();
79
+ const origin = this.getOrigin(url);
80
+ try {
81
+ // Get or create connection
82
+ const connection = await this.acquireConnection(origin);
83
+ // Update metrics
84
+ if (this.options.enableMetrics) {
85
+ this.metrics.totalRequests++;
86
+ if (connection.requestCount > 0) {
87
+ this.metrics.reuseCount++;
88
+ }
89
+ }
90
+ // Make request with keep-alive
91
+ const requestInit = {
92
+ ...init,
93
+ signal: init?.signal ?? connection.controller.signal,
94
+ // @ts-ignore - keepalive is a valid fetch option
95
+ keepalive: true,
96
+ };
97
+ const response = await fetch(url, requestInit);
98
+ // Update connection state
99
+ connection.requestCount++;
100
+ connection.lastUsedAt = Date.now();
101
+ connection.state = 'idle';
102
+ // Record metrics
103
+ if (this.options.enableMetrics) {
104
+ const duration = Date.now() - startTime;
105
+ this.recordMetrics(origin, duration);
106
+ }
107
+ return response;
108
+ }
109
+ catch (error) {
110
+ // Record failure
111
+ if (this.options.enableMetrics) {
112
+ this.metrics.failedRequests++;
113
+ }
114
+ // Retry if enabled
115
+ if (this.options.retryOnFailure && init?.signal?.aborted !== true) {
116
+ return this.retryRequest(url, init, 1);
117
+ }
118
+ throw error;
119
+ }
120
+ }
121
+ /**
122
+ * Retry a failed request with exponential backoff
123
+ */
124
+ async retryRequest(url, init, attempt) {
125
+ if (attempt > this.options.maxRetries) {
126
+ throw new Error(`Request failed after ${this.options.maxRetries} retries: ${url}`);
127
+ }
128
+ // Exponential backoff: 100ms, 200ms, 400ms...
129
+ const delay = 100 * Math.pow(2, attempt - 1);
130
+ await new Promise(resolve => setTimeout(resolve, delay));
131
+ const origin = this.getOrigin(url);
132
+ try {
133
+ // Get or create connection
134
+ const connection = await this.acquireConnection(origin);
135
+ // Make request with keep-alive
136
+ const requestInit = {
137
+ ...init,
138
+ signal: init?.signal ?? connection.controller.signal,
139
+ // @ts-ignore - keepalive is a valid fetch option
140
+ keepalive: true,
141
+ };
142
+ const response = await fetch(url, requestInit);
143
+ // Update connection state
144
+ connection.requestCount++;
145
+ connection.lastUsedAt = Date.now();
146
+ connection.state = 'idle';
147
+ return response;
148
+ }
149
+ catch (error) {
150
+ if (attempt >= this.options.maxRetries) {
151
+ throw new Error(`Request failed after ${this.options.maxRetries} retries: ${url}`);
152
+ }
153
+ return this.retryRequest(url, init, attempt + 1);
154
+ }
155
+ }
156
+ /**
157
+ * Acquire a connection from the pool or create a new one
158
+ */
159
+ async acquireConnection(origin) {
160
+ let pool = this.connections.get(origin);
161
+ if (!pool) {
162
+ pool = [];
163
+ this.connections.set(origin, pool);
164
+ }
165
+ // Find idle connection
166
+ const idleConnection = pool.find(conn => conn.state === 'idle');
167
+ if (idleConnection) {
168
+ idleConnection.state = 'active';
169
+ return idleConnection;
170
+ }
171
+ // Check if we can create a new connection
172
+ if (pool.length < this.options.maxConnections) {
173
+ const connection = this.createConnection(origin);
174
+ pool.push(connection);
175
+ return connection;
176
+ }
177
+ // Wait for an available connection
178
+ return this.waitForConnection(origin);
179
+ }
180
+ /**
181
+ * Wait for a connection to become available
182
+ */
183
+ async waitForConnection(origin) {
184
+ const startTime = Date.now();
185
+ const timeout = this.options.requestTimeout;
186
+ while (Date.now() - startTime < timeout) {
187
+ const pool = this.connections.get(origin);
188
+ if (!pool) {
189
+ throw new Error(`Connection pool for ${origin} disappeared`);
190
+ }
191
+ const idleConnection = pool.find(conn => conn.state === 'idle');
192
+ if (idleConnection) {
193
+ idleConnection.state = 'active';
194
+ return idleConnection;
195
+ }
196
+ // Wait a bit before checking again
197
+ await new Promise(resolve => setTimeout(resolve, 10));
198
+ }
199
+ throw new Error(`Timeout waiting for connection to ${origin} after ${timeout}ms`);
200
+ }
201
+ /**
202
+ * Create a new connection
203
+ */
204
+ createConnection(origin) {
205
+ const randomBytes = new Uint8Array(8);
206
+ crypto.getRandomValues(randomBytes);
207
+ const id = Array.from(randomBytes)
208
+ .map(b => b.toString(16).padStart(2, '0'))
209
+ .join('');
210
+ return {
211
+ id: `conn_${id}`,
212
+ origin,
213
+ createdAt: Date.now(),
214
+ lastUsedAt: Date.now(),
215
+ requestCount: 0,
216
+ state: 'active',
217
+ controller: new AbortController(),
218
+ };
219
+ }
220
+ /**
221
+ * Get origin from URL
222
+ */
223
+ getOrigin(url) {
224
+ try {
225
+ const parsed = new URL(url);
226
+ return `${parsed.protocol}//${parsed.host}`;
227
+ }
228
+ catch (error) {
229
+ throw new Error(`Invalid URL: ${url}`);
230
+ }
231
+ }
232
+ /**
233
+ * Record request metrics
234
+ */
235
+ recordMetrics(origin, duration) {
236
+ this.metrics.requestDurations.push(duration);
237
+ // Limit stored durations to last 1000 requests
238
+ if (this.metrics.requestDurations.length > 1000) {
239
+ this.metrics.requestDurations.shift();
240
+ }
241
+ // Record origin-specific metrics
242
+ let originMetrics = this.metrics.byOrigin.get(origin);
243
+ if (!originMetrics) {
244
+ originMetrics = { requests: 0, durations: [] };
245
+ this.metrics.byOrigin.set(origin, originMetrics);
246
+ }
247
+ originMetrics.requests++;
248
+ originMetrics.durations.push(duration);
249
+ // Limit origin durations
250
+ if (originMetrics.durations.length > 100) {
251
+ originMetrics.durations.shift();
252
+ }
253
+ }
254
+ /**
255
+ * Get connection pool metrics
256
+ *
257
+ * @returns Current metrics snapshot
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * const metrics = pool.getMetrics();
262
+ * console.log(`Hit rate: ${(metrics.hitRate * 100).toFixed(1)}%`);
263
+ * console.log(`Avg duration: ${metrics.avgRequestDuration}ms`);
264
+ * ```
265
+ */
266
+ getMetrics() {
267
+ let totalConnections = 0;
268
+ let activeConnections = 0;
269
+ let idleConnections = 0;
270
+ const byOrigin = new Map();
271
+ for (const [origin, pool] of this.connections.entries()) {
272
+ totalConnections += pool.length;
273
+ activeConnections += pool.filter(c => c.state === 'active').length;
274
+ idleConnections += pool.filter(c => c.state === 'idle').length;
275
+ const originMetrics = this.metrics.byOrigin.get(origin);
276
+ if (originMetrics) {
277
+ const avgDuration = originMetrics.durations.length > 0
278
+ ? originMetrics.durations.reduce((a, b) => a + b, 0) /
279
+ originMetrics.durations.length
280
+ : 0;
281
+ byOrigin.set(origin, {
282
+ connections: pool.length,
283
+ requests: originMetrics.requests,
284
+ avgDuration: Math.round(avgDuration),
285
+ });
286
+ }
287
+ }
288
+ const avgRequestDuration = this.metrics.requestDurations.length > 0
289
+ ? Math.round(this.metrics.requestDurations.reduce((a, b) => a + b, 0) /
290
+ this.metrics.requestDurations.length)
291
+ : 0;
292
+ const hitRate = this.metrics.totalRequests > 0
293
+ ? this.metrics.reuseCount / this.metrics.totalRequests
294
+ : 0;
295
+ return {
296
+ totalConnections,
297
+ activeConnections,
298
+ idleConnections,
299
+ totalRequests: this.metrics.totalRequests,
300
+ reuseCount: this.metrics.reuseCount,
301
+ failedRequests: this.metrics.failedRequests,
302
+ avgRequestDuration,
303
+ hitRate,
304
+ byOrigin,
305
+ };
306
+ }
307
+ /**
308
+ * Reset metrics counters
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * pool.resetMetrics();
313
+ * // Start fresh measurements
314
+ * ```
315
+ */
316
+ resetMetrics() {
317
+ this.metrics = {
318
+ totalRequests: 0,
319
+ reuseCount: 0,
320
+ failedRequests: 0,
321
+ requestDurations: [],
322
+ byOrigin: new Map(),
323
+ };
324
+ }
325
+ /**
326
+ * Start automatic connection cleanup
327
+ */
328
+ startCleanup() {
329
+ // Run cleanup every 10 seconds
330
+ this.cleanupInterval = setInterval(() => {
331
+ this.cleanup();
332
+ }, 10000);
333
+ }
334
+ /**
335
+ * Clean up idle and expired connections
336
+ */
337
+ cleanup() {
338
+ const now = Date.now();
339
+ for (const [origin, pool] of this.connections.entries()) {
340
+ // Remove expired connections
341
+ const activePool = pool.filter(conn => {
342
+ const isExpired = now - conn.lastUsedAt > this.options.idleTimeout &&
343
+ conn.state === 'idle';
344
+ if (isExpired) {
345
+ conn.controller.abort();
346
+ conn.state = 'closed';
347
+ return false;
348
+ }
349
+ return conn.state !== 'closed';
350
+ });
351
+ // Ensure minimum connections are maintained
352
+ while (activePool.length < this.options.minConnections &&
353
+ activePool.length < this.options.maxConnections) {
354
+ const conn = this.createConnection(origin);
355
+ conn.state = 'idle';
356
+ activePool.push(conn);
357
+ }
358
+ if (activePool.length > 0) {
359
+ this.connections.set(origin, activePool);
360
+ }
361
+ else {
362
+ this.connections.delete(origin);
363
+ }
364
+ }
365
+ }
366
+ /**
367
+ * Manually trigger connection cleanup
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * // Force cleanup of idle connections
372
+ * pool.cleanupNow();
373
+ * ```
374
+ */
375
+ cleanupNow() {
376
+ this.cleanup();
377
+ }
378
+ /**
379
+ * Close all connections and shutdown the pool
380
+ *
381
+ * @example
382
+ * ```typescript
383
+ * // Cleanup on application shutdown
384
+ * await pool.close();
385
+ * ```
386
+ */
387
+ async close() {
388
+ // Stop cleanup interval
389
+ if (this.cleanupInterval) {
390
+ clearInterval(this.cleanupInterval);
391
+ this.cleanupInterval = null;
392
+ }
393
+ // Close all connections
394
+ for (const [_origin, pool] of this.connections.entries()) {
395
+ for (const conn of pool) {
396
+ conn.controller.abort();
397
+ conn.state = 'closed';
398
+ }
399
+ }
400
+ this.connections.clear();
401
+ }
402
+ /**
403
+ * Get the number of connections for a specific origin
404
+ *
405
+ * @param origin - Target origin (e.g., "https://api.example.com")
406
+ * @returns Number of connections
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const count = pool.getConnectionCount('https://api.example.com');
411
+ * console.log(`Active connections: ${count}`);
412
+ * ```
413
+ */
414
+ getConnectionCount(origin) {
415
+ const pool = this.connections.get(origin);
416
+ return pool ? pool.length : 0;
417
+ }
418
+ /**
419
+ * Get all origins with active connections
420
+ *
421
+ * @returns Array of origins
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * const origins = pool.getOrigins();
426
+ * console.log(`Connected to ${origins.length} origins`);
427
+ * ```
428
+ */
429
+ getOrigins() {
430
+ return Array.from(this.connections.keys());
431
+ }
432
+ /**
433
+ * Check if the pool is healthy
434
+ *
435
+ * @returns True if pool is operating normally
436
+ *
437
+ * @example
438
+ * ```typescript
439
+ * if (!pool.isHealthy()) {
440
+ * console.warn('Connection pool degraded');
441
+ * }
442
+ * ```
443
+ */
444
+ isHealthy() {
445
+ const metrics = this.getMetrics();
446
+ // Check if failure rate is too high
447
+ const failureRate = metrics.totalRequests > 0
448
+ ? metrics.failedRequests / metrics.totalRequests
449
+ : 0;
450
+ if (failureRate > 0.1) {
451
+ // More than 10% failure rate
452
+ return false;
453
+ }
454
+ // Check if we have reasonable response times
455
+ if (metrics.avgRequestDuration > 5000) {
456
+ // Average over 5 seconds
457
+ return false;
458
+ }
459
+ return true;
460
+ }
461
+ }
462
+ exports.ConnectionPool = ConnectionPool;
463
+ /**
464
+ * Create a global connection pool instance
465
+ *
466
+ * Useful for sharing a single pool across your application.
467
+ *
468
+ * @param options - Pool configuration
469
+ * @returns Singleton connection pool instance
470
+ *
471
+ * @example
472
+ * ```typescript
473
+ * import { getGlobalPool } from '@private.me/xbind/connection-pool';
474
+ *
475
+ * const pool = getGlobalPool({
476
+ * maxConnections: 50,
477
+ * keepAliveTimeout: 30000,
478
+ * });
479
+ *
480
+ * const response = await pool.fetch('https://api.example.com/data');
481
+ * ```
482
+ */
483
+ let globalPool = null;
484
+ function getGlobalPool(options) {
485
+ if (!globalPool) {
486
+ globalPool = new ConnectionPool(options);
487
+ }
488
+ return globalPool;
489
+ }
490
+ /**
491
+ * Reset the global connection pool
492
+ *
493
+ * Closes the current pool and allows a new one to be created.
494
+ *
495
+ * @example
496
+ * ```typescript
497
+ * await resetGlobalPool();
498
+ * // Next getGlobalPool() call will create a fresh instance
499
+ * ```
500
+ */
501
+ async function resetGlobalPool() {
502
+ if (globalPool) {
503
+ await globalPool.close();
504
+ globalPool = null;
505
+ }
506
+ }