@enbox/dwn-clients 0.0.5 → 0.0.7

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 (53) hide show
  1. package/dist/esm/dwn-registrar.js +87 -0
  2. package/dist/esm/dwn-registrar.js.map +1 -1
  3. package/dist/esm/http-dwn-rpc-client.js +145 -6
  4. package/dist/esm/http-dwn-rpc-client.js.map +1 -1
  5. package/dist/esm/index.js +2 -0
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/esm/json-rpc-socket.js +187 -35
  8. package/dist/esm/json-rpc-socket.js.map +1 -1
  9. package/dist/esm/json-rpc.js +13 -0
  10. package/dist/esm/json-rpc.js.map +1 -1
  11. package/dist/esm/provider-directory-types.js +2 -0
  12. package/dist/esm/provider-directory-types.js.map +1 -0
  13. package/dist/esm/rate-limit-error.js +14 -0
  14. package/dist/esm/rate-limit-error.js.map +1 -0
  15. package/dist/esm/rpc-client.js +1 -1
  16. package/dist/esm/rpc-client.js.map +1 -1
  17. package/dist/esm/web-socket-clients.js +102 -16
  18. package/dist/esm/web-socket-clients.js.map +1 -1
  19. package/dist/types/dwn-registrar.d.ts +29 -0
  20. package/dist/types/dwn-registrar.d.ts.map +1 -1
  21. package/dist/types/dwn-rpc-types.d.ts +54 -4
  22. package/dist/types/dwn-rpc-types.d.ts.map +1 -1
  23. package/dist/types/http-dwn-rpc-client.d.ts +24 -2
  24. package/dist/types/http-dwn-rpc-client.d.ts.map +1 -1
  25. package/dist/types/index.d.ts +2 -0
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/json-rpc-socket.d.ts +63 -0
  28. package/dist/types/json-rpc-socket.d.ts.map +1 -1
  29. package/dist/types/json-rpc.d.ts +7 -1
  30. package/dist/types/json-rpc.d.ts.map +1 -1
  31. package/dist/types/provider-directory-types.d.ts +40 -0
  32. package/dist/types/provider-directory-types.d.ts.map +1 -0
  33. package/dist/types/rate-limit-error.d.ts +12 -0
  34. package/dist/types/rate-limit-error.d.ts.map +1 -0
  35. package/dist/types/registration-types.d.ts +46 -2
  36. package/dist/types/registration-types.d.ts.map +1 -1
  37. package/dist/types/server-info-types.d.ts +26 -0
  38. package/dist/types/server-info-types.d.ts.map +1 -1
  39. package/dist/types/web-socket-clients.d.ts +12 -2
  40. package/dist/types/web-socket-clients.d.ts.map +1 -1
  41. package/package.json +4 -4
  42. package/src/dwn-registrar.ts +106 -3
  43. package/src/dwn-rpc-types.ts +69 -4
  44. package/src/http-dwn-rpc-client.ts +182 -6
  45. package/src/index.ts +2 -0
  46. package/src/json-rpc-socket.ts +244 -36
  47. package/src/json-rpc.ts +17 -0
  48. package/src/provider-directory-types.ts +41 -0
  49. package/src/rate-limit-error.ts +16 -0
  50. package/src/registration-types.ts +50 -3
  51. package/src/rpc-client.ts +1 -1
  52. package/src/server-info-types.ts +27 -0
  53. package/src/web-socket-clients.ts +156 -20
@@ -27,6 +27,7 @@ export class DwnRegistrar {
27
27
  // fetch the terms-of-service
28
28
  const termsOfServiceGetResponse = yield fetch(termsOfUseEndpoint, {
29
29
  method: 'GET',
30
+ signal: AbortSignal.timeout(30000),
30
31
  });
31
32
  if (termsOfServiceGetResponse.status !== 200) {
32
33
  const statusCode = termsOfServiceGetResponse.status;
@@ -38,6 +39,7 @@ export class DwnRegistrar {
38
39
  // fetch the proof-of-work challenge
39
40
  const proofOfWorkChallengeGetResponse = yield fetch(proofOfWorkEndpoint, {
40
41
  method: 'GET',
42
+ signal: AbortSignal.timeout(30000),
41
43
  });
42
44
  const { challengeNonce, maximumAllowedHashValue } = yield proofOfWorkChallengeGetResponse.json();
43
45
  // create registration data based on the hash of the terms-of-service and the DID
@@ -63,6 +65,7 @@ export class DwnRegistrar {
63
65
  method: 'POST',
64
66
  headers: { 'Content-Type': 'application/json' },
65
67
  body: JSON.stringify(registrationRequest),
68
+ signal: AbortSignal.timeout(30000),
66
69
  });
67
70
  if (registrationResponse.status !== 200) {
68
71
  const statusCode = registrationResponse.status;
@@ -115,5 +118,89 @@ export class DwnRegistrar {
115
118
  return hexString;
116
119
  });
117
120
  }
121
+ /**
122
+ * Exchange an authorization code (from the provider's auth redirect) for a registration token.
123
+ * The wallet calls this after the user completes the provider's auth flow.
124
+ *
125
+ * @param tokenUrl - The provider's token endpoint URL (from ServerInfo.providerAuth.tokenUrl)
126
+ * @param code - The authorization code from the provider redirect
127
+ * @param redirectUri - The redirect URI used in the authorize request (must match exactly)
128
+ * @returns Token exchange response containing the registration token and optional refresh token
129
+ */
130
+ static exchangeAuthCode(tokenUrl, code, redirectUri) {
131
+ return __awaiter(this, void 0, void 0, function* () {
132
+ const response = yield fetch(tokenUrl, {
133
+ method: 'POST',
134
+ headers: { 'Content-Type': 'application/json' },
135
+ body: JSON.stringify({
136
+ grantType: 'authorization_code',
137
+ code,
138
+ redirectUri,
139
+ }),
140
+ signal: AbortSignal.timeout(30000),
141
+ });
142
+ if (response.status !== 200) {
143
+ const errorText = yield response.text();
144
+ throw new Error(`DwnRegistrar: Token exchange failed (${response.status}): ${errorText}`);
145
+ }
146
+ return response.json();
147
+ });
148
+ }
149
+ /**
150
+ * Refresh an expired registration token using a refresh token.
151
+ *
152
+ * @param refreshUrl - The provider's refresh endpoint URL (from ServerInfo.providerAuth.refreshUrl)
153
+ * @param refreshToken - The refresh token from a previous token exchange
154
+ * @returns New token exchange response with fresh registration token
155
+ */
156
+ static refreshRegistrationToken(refreshUrl, refreshToken) {
157
+ return __awaiter(this, void 0, void 0, function* () {
158
+ const response = yield fetch(refreshUrl, {
159
+ method: 'POST',
160
+ headers: { 'Content-Type': 'application/json' },
161
+ body: JSON.stringify({
162
+ grantType: 'refresh_token',
163
+ refreshToken,
164
+ }),
165
+ signal: AbortSignal.timeout(30000),
166
+ });
167
+ if (response.status !== 200) {
168
+ const errorText = yield response.text();
169
+ throw new Error(`DwnRegistrar: Token refresh failed (${response.status}): ${errorText}`);
170
+ }
171
+ return response.json();
172
+ });
173
+ }
174
+ /**
175
+ * Register a DID as a tenant on a DWN server using a provider auth registration token.
176
+ * This is the paid-tier alternative to the PoW-based {@link registerTenant}.
177
+ *
178
+ * @param dwnEndpoint - The DWN server base URL
179
+ * @param did - The DID to register as a tenant
180
+ * @param registrationToken - The opaque registration token from the provider
181
+ * @param termsOfServiceHash - Optional ToS hash if the server requires it alongside provider auth
182
+ */
183
+ static registerTenantWithToken(dwnEndpoint, did, registrationToken, termsOfServiceHash) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ const registrationEndpoint = concatenateUrl(dwnEndpoint, 'registration');
186
+ const registrationRequest = {
187
+ providerAuth: { registrationToken },
188
+ registrationData: {
189
+ did,
190
+ termsOfServiceHash,
191
+ },
192
+ };
193
+ const response = yield fetch(registrationEndpoint, {
194
+ method: 'POST',
195
+ headers: { 'Content-Type': 'application/json' },
196
+ body: JSON.stringify(registrationRequest),
197
+ signal: AbortSignal.timeout(30000),
198
+ });
199
+ if (response.status !== 200) {
200
+ const errorText = yield response.text();
201
+ throw new Error(`DwnRegistrar: Provider auth registration failed (${response.status}): ${errorText}`);
202
+ }
203
+ });
204
+ }
118
205
  }
119
206
  //# sourceMappingURL=dwn-registrar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dwn-registrar.js","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dwn-registrar.js","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":"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"}
@@ -10,17 +10,88 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { CryptoUtils } from '@enbox/crypto';
11
11
  import { DataStream } from '@enbox/dwn-sdk-js';
12
12
  import { DwnServerInfoCacheMemory } from './dwn-server-info-cache-memory.js';
13
- import { createJsonRpcRequest, parseJson } from './json-rpc.js';
13
+ import { RateLimitError } from './rate-limit-error.js';
14
+ import { createJsonRpcRequest, JsonRpcErrorCodes, parseJson } from './json-rpc.js';
15
+ // ---------------------------------------------------------------------------
16
+ // Retry configuration
17
+ // ---------------------------------------------------------------------------
18
+ /** Default number of retry attempts for transient HTTP failures. */
19
+ const DEFAULT_MAX_RETRIES = 3;
20
+ /** Base delay in milliseconds for exponential backoff. */
21
+ const DEFAULT_BASE_DELAY_MS = 500;
22
+ /** Maximum backoff delay in milliseconds. */
23
+ const DEFAULT_MAX_DELAY_MS = 10000;
24
+ /** Per-request timeout in milliseconds (prevents hung connections / SSRF). */
25
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
26
+ /** HTTP status codes that are considered retryable. */
27
+ const RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);
14
28
  /**
15
- * HTTP client that can be used to communicate with Dwn Servers
29
+ * Determines whether a fetch error or HTTP response warrants a retry.
30
+ * Network errors (TypeError from fetch) and specific HTTP status codes are retryable.
31
+ */
32
+ function isRetryable(error, response) {
33
+ if (error instanceof TypeError) {
34
+ // TypeError is thrown by fetch for network-level failures (DNS, connection refused, etc.).
35
+ return true;
36
+ }
37
+ if (response) {
38
+ return RETRYABLE_STATUS_CODES.has(response.status);
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Computes the backoff delay with jitter for a given attempt.
44
+ * Uses exponential backoff: `min(baseDelay * 2^attempt, maxDelay) * jitter`.
45
+ */
46
+ function computeBackoffDelay(attempt, baseDelayMs, maxDelayMs) {
47
+ const exponentialDelay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
48
+ const jitter = 0.5 + Math.random() * 0.5;
49
+ return exponentialDelay * jitter;
50
+ }
51
+ /**
52
+ * Parses a `retry-after` header value into milliseconds.
53
+ * Supports both delay-seconds (e.g. "120") and HTTP-date formats.
54
+ * Returns `undefined` if the header is absent or unparseable.
55
+ */
56
+ function parseRetryAfterMs(response) {
57
+ const retryAfter = response.headers.get('retry-after');
58
+ if (retryAfter === null) {
59
+ return undefined;
60
+ }
61
+ // Try as integer seconds first.
62
+ const seconds = Number(retryAfter);
63
+ if (!Number.isNaN(seconds) && seconds >= 0) {
64
+ return seconds * 1000;
65
+ }
66
+ // Try as HTTP-date.
67
+ const date = new Date(retryAfter);
68
+ if (!Number.isNaN(date.getTime())) {
69
+ const delayMs = date.getTime() - Date.now();
70
+ return delayMs > 0 ? delayMs : 0;
71
+ }
72
+ return undefined;
73
+ }
74
+ /**
75
+ * HTTP client that can be used to communicate with Dwn Servers.
76
+ *
77
+ * Supports automatic retry with exponential backoff and jitter for transient
78
+ * network errors and retryable HTTP status codes (408, 429, 500, 502, 503, 504).
79
+ * Respects the `Retry-After` response header when present.
16
80
  */
17
81
  export class HttpDwnRpcClient {
18
- constructor(serverInfoCache) {
82
+ constructor(serverInfoCache, retryOptions) {
83
+ var _a, _b, _c;
19
84
  this.serverInfoCache = serverInfoCache !== null && serverInfoCache !== void 0 ? serverInfoCache : new DwnServerInfoCacheMemory();
85
+ this._retryOptions = {
86
+ maxRetries: (_a = retryOptions === null || retryOptions === void 0 ? void 0 : retryOptions.maxRetries) !== null && _a !== void 0 ? _a : DEFAULT_MAX_RETRIES,
87
+ baseDelayMs: (_b = retryOptions === null || retryOptions === void 0 ? void 0 : retryOptions.baseDelayMs) !== null && _b !== void 0 ? _b : DEFAULT_BASE_DELAY_MS,
88
+ maxDelayMs: (_c = retryOptions === null || retryOptions === void 0 ? void 0 : retryOptions.maxDelayMs) !== null && _c !== void 0 ? _c : DEFAULT_MAX_DELAY_MS,
89
+ };
20
90
  }
21
91
  get transportProtocols() { return ['http:', 'https:']; }
22
92
  sendDwnRequest(request) {
23
93
  return __awaiter(this, void 0, void 0, function* () {
94
+ var _a, _b, _c;
24
95
  const requestId = CryptoUtils.randomUuid();
25
96
  const jsonRpcRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', {
26
97
  target: request.targetDid,
@@ -41,7 +112,14 @@ export class HttpDwnRpcClient {
41
112
  // TypeScript's built-in RequestInit does not include `duplex` yet.
42
113
  fetchOpts.duplex = 'half';
43
114
  }
44
- const resp = yield fetch(request.dwnUrl, fetchOpts);
115
+ const resp = yield this.fetchWithRetry(request.dwnUrl, fetchOpts);
116
+ // After retries are exhausted, a 429 means we're still rate-limited.
117
+ // Per-IP 429s return plain JSON (not a JSON-RPC envelope), so we must
118
+ // check the status before attempting JSON-RPC parsing.
119
+ if (resp.status === 429) {
120
+ const retryAfter = parseInt((_a = resp.headers.get('retry-after')) !== null && _a !== void 0 ? _a : '1', 10);
121
+ throw new RateLimitError(retryAfter);
122
+ }
45
123
  let dwnRpcResponse;
46
124
  // When the server streams record data back, the JSON-RPC envelope is in the
47
125
  // `dwn-response` header and the body is the raw data stream. Otherwise the
@@ -56,10 +134,18 @@ export class HttpDwnRpcClient {
56
134
  }
57
135
  else {
58
136
  const responseBody = yield resp.text();
59
- dwnRpcResponse = JSON.parse(responseBody);
137
+ const jsonRpcResponse = parseJson(responseBody);
138
+ if (jsonRpcResponse == null) {
139
+ throw new Error(`failed to parse json rpc response. dwn url: ${request.dwnUrl}, status: ${resp.status}`);
140
+ }
141
+ dwnRpcResponse = jsonRpcResponse;
60
142
  }
61
143
  if (dwnRpcResponse.error) {
62
144
  const { code, message } = dwnRpcResponse.error;
145
+ if (code === JsonRpcErrorCodes.TooManyRequests) {
146
+ const retryAfter = (_c = (_b = dwnRpcResponse.error.data) === null || _b === void 0 ? void 0 : _b.retryAfterSec) !== null && _c !== void 0 ? _c : 1;
147
+ throw new RateLimitError(retryAfter);
148
+ }
63
149
  throw new Error(`(${code}) - ${message}`);
64
150
  }
65
151
  // Materialise the response body before attaching to the reply.
@@ -84,6 +170,7 @@ export class HttpDwnRpcClient {
84
170
  }
85
171
  getServerInfo(dwnUrl) {
86
172
  return __awaiter(this, void 0, void 0, function* () {
173
+ var _a;
87
174
  const serverInfo = yield this.serverInfoCache.get(dwnUrl);
88
175
  if (serverInfo) {
89
176
  return serverInfo;
@@ -92,7 +179,11 @@ export class HttpDwnRpcClient {
92
179
  // add `/info` to the dwn server url path
93
180
  url.pathname.endsWith('/') ? url.pathname += 'info' : url.pathname += '/info';
94
181
  try {
95
- const response = yield fetch(url.toString());
182
+ const response = yield this.fetchWithRetry(url.toString());
183
+ if (response.status === 429) {
184
+ const retryAfter = parseInt((_a = response.headers.get('retry-after')) !== null && _a !== void 0 ? _a : '1', 10);
185
+ throw new RateLimitError(retryAfter);
186
+ }
96
187
  if (response.ok) {
97
188
  const results = yield response.json();
98
189
  const serverInfo = {
@@ -116,5 +207,53 @@ export class HttpDwnRpcClient {
116
207
  }
117
208
  });
118
209
  }
210
+ // ---------------------------------------------------------------------------
211
+ // Retry logic
212
+ // ---------------------------------------------------------------------------
213
+ /**
214
+ * Wrapper around `fetch()` that retries on transient network errors and
215
+ * retryable HTTP status codes with exponential backoff and jitter.
216
+ * Honours the `Retry-After` response header when present.
217
+ */
218
+ fetchWithRetry(url, init) {
219
+ return __awaiter(this, void 0, void 0, function* () {
220
+ const { maxRetries, baseDelayMs, maxDelayMs } = this._retryOptions;
221
+ let lastError;
222
+ let lastResponse;
223
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
224
+ try {
225
+ // Apply a per-attempt timeout to prevent hung connections / SSRF.
226
+ // If the caller already supplied a signal, combine it with the timeout
227
+ // via AbortSignal.any(); otherwise create a fresh timeout signal.
228
+ const timeoutSignal = AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS);
229
+ const attemptInit = Object.assign(Object.assign({}, init), { signal: (init === null || init === void 0 ? void 0 : init.signal)
230
+ ? AbortSignal.any([init.signal, timeoutSignal])
231
+ : timeoutSignal });
232
+ const response = yield fetch(url, attemptInit);
233
+ if (!RETRYABLE_STATUS_CODES.has(response.status) || attempt === maxRetries) {
234
+ return response;
235
+ }
236
+ // Retryable status — back off and try again.
237
+ lastResponse = response;
238
+ }
239
+ catch (error) {
240
+ if (!isRetryable(error) || attempt === maxRetries) {
241
+ throw error;
242
+ }
243
+ lastError = error;
244
+ }
245
+ // Compute the delay, preferring Retry-After when available.
246
+ const retryAfterMs = lastResponse ? parseRetryAfterMs(lastResponse) : undefined;
247
+ const backoffMs = computeBackoffDelay(attempt, baseDelayMs, maxDelayMs);
248
+ const delayMs = retryAfterMs !== undefined ? Math.max(retryAfterMs, backoffMs) : backoffMs;
249
+ yield new Promise((resolve) => { setTimeout(resolve, delayMs); });
250
+ }
251
+ // Should not reach here, but satisfy the compiler.
252
+ if (lastResponse) {
253
+ return lastResponse;
254
+ }
255
+ throw lastError;
256
+ });
257
+ }
119
258
  }
120
259
  //# sourceMappingURL=http-dwn-rpc-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-dwn-rpc-client.js","sourceRoot":"","sources":["../../src/http-dwn-rpc-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http-dwn-rpc-client.js","sourceRoot":"","sources":["../../src/http-dwn-rpc-client.ts"],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -4,6 +4,8 @@ export * from './dwn-server-info-cache-memory.js';
4
4
  export * from './http-dwn-rpc-client.js';
5
5
  export * from './json-rpc.js';
6
6
  export * from './json-rpc-socket.js';
7
+ export * from './provider-directory-types.js';
8
+ export * from './rate-limit-error.js';
7
9
  export * from './registration-types.js';
8
10
  export * from './rpc-client.js';
9
11
  export * from './server-info-types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,0BAA0B,CAAC;AACzC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC"}
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { CryptoUtils } from '@enbox/crypto';
11
- import { createJsonRpcSubscriptionRequest, parseJson } from './json-rpc.js';
11
+ import { createJsonRpcSubscriptionRequest, JsonRpcErrorCodes, parseJson } from './json-rpc.js';
12
12
  /**
13
13
  * Converts WebSocket message data to a string.
14
14
  * Bun's native WebSocket delivers `event.data` as an `ArrayBuffer`,
@@ -30,55 +30,71 @@ function toText(data) {
30
30
  // These were arbitrarily chosen, but can be modified via connect options
31
31
  const CONNECT_TIMEOUT = 3000;
32
32
  const RESPONSE_TIMEOUT = 30000;
33
+ /** Default reconnection settings. */
34
+ const DEFAULT_BASE_RECONNECT_DELAY = 1000;
35
+ const DEFAULT_MAX_RECONNECT_DELAY = 30000;
36
+ const DEFAULT_MAX_RECONNECT_ATTEMPTS = Infinity;
33
37
  /**
34
38
  * JSON RPC Socket Client for WebSocket request/response and long-running subscriptions.
39
+ *
40
+ * Supports automatic reconnection with exponential backoff when the connection
41
+ * drops unexpectedly. Subscription message handlers survive reconnection — they
42
+ * are re-registered on the new underlying socket automatically.
35
43
  */
36
44
  export class JsonRpcSocket {
37
- constructor(socket, responseTimeout) {
45
+ constructor(socket, responseTimeout, url, options) {
38
46
  this.socket = socket;
39
47
  this.responseTimeout = responseTimeout;
48
+ /**
49
+ * Map of JSON-RPC id → message handler. For one-shot `request()` calls, the
50
+ * handler is added before sending and removed on response or timeout.
51
+ * For subscriptions, the handler lives until explicitly closed.
52
+ */
40
53
  this.messageHandlers = new Map();
54
+ /**
55
+ * Set of JSON-RPC ids that belong to subscription handlers (as opposed to
56
+ * one-shot request handlers). Subscription handlers survive reconnection;
57
+ * one-shot handlers are rejected on unexpected close.
58
+ */
59
+ this.subscriptionHandlerIds = new Set();
60
+ /** Whether `close()` was called intentionally by the user. */
61
+ this.closedByUser = false;
62
+ /** Whether a reconnection attempt is currently in progress. */
63
+ this.reconnecting = false;
64
+ /** Whether the socket is currently connected. */
65
+ this._isConnected = false;
66
+ this.url = url;
67
+ this.options = options;
68
+ this._isConnected = true;
69
+ }
70
+ /** Whether the socket is currently connected. */
71
+ get isConnected() {
72
+ return this._isConnected;
41
73
  }
42
74
  static connect(url_1) {
43
75
  return __awaiter(this, arguments, void 0, function* (url, options = {}) {
44
- const { connectTimeout = CONNECT_TIMEOUT, responseTimeout = RESPONSE_TIMEOUT, onclose, onerror } = options;
45
- const socket = new WebSocket(url);
46
- if (!onclose) {
47
- socket.onclose = () => {
48
- console.info(`JSON RPC Socket close ${url}`);
49
- };
50
- }
51
- else {
52
- socket.onclose = onclose;
76
+ var _a;
77
+ const { connectTimeout = CONNECT_TIMEOUT, responseTimeout = RESPONSE_TIMEOUT } = options;
78
+ let socket;
79
+ try {
80
+ socket = yield JsonRpcSocket.createWebSocket(url, connectTimeout);
53
81
  }
54
- if (!onerror) {
55
- socket.onerror = (error) => {
56
- console.error(`JSON RPC Socket error ${url}`, error);
57
- };
58
- }
59
- else {
60
- socket.onerror = onerror;
82
+ catch (error) {
83
+ // Notify the onerror handler if one was provided, even for connection-time errors.
84
+ (_a = options.onerror) === null || _a === void 0 ? void 0 : _a.call(options, error);
85
+ throw error;
61
86
  }
62
- return new Promise((resolve, reject) => {
63
- socket.addEventListener('open', () => {
64
- const jsonRpcSocket = new JsonRpcSocket(socket, responseTimeout);
65
- socket.addEventListener('message', (event) => {
66
- const jsonRpcResponse = parseJson(toText(event.data));
67
- const handler = jsonRpcSocket.messageHandlers.get(jsonRpcResponse.id);
68
- if (handler) {
69
- handler(event);
70
- }
71
- });
72
- resolve(jsonRpcSocket);
73
- });
74
- socket.addEventListener('error', (error) => {
75
- reject(error);
76
- });
77
- setTimeout(() => reject(new Error('connect timed out')), connectTimeout);
78
- });
87
+ const jsonRpcSocket = new JsonRpcSocket(socket, responseTimeout, url, options);
88
+ jsonRpcSocket.wireSocket(socket);
89
+ return jsonRpcSocket;
79
90
  });
80
91
  }
92
+ /**
93
+ * Closes the socket and stops reconnection attempts.
94
+ */
81
95
  close() {
96
+ this.closedByUser = true;
97
+ this._isConnected = false;
82
98
  this.socket.close();
83
99
  }
84
100
  /**
@@ -130,6 +146,7 @@ export class JsonRpcSocket {
130
146
  if (jsonRpcResponse.error !== undefined) {
131
147
  // remove the event listener upon receipt of a JSON RPC Error.
132
148
  this.messageHandlers.delete(subscriptionId);
149
+ this.subscriptionHandlerIds.delete(subscriptionId);
133
150
  this.closeSubscription(subscriptionId).catch(() => {
134
151
  // swallow timeout errors; the subscription is already cleaned up locally.
135
152
  });
@@ -138,6 +155,7 @@ export class JsonRpcSocket {
138
155
  }
139
156
  };
140
157
  this.messageHandlers.set(subscriptionId, socketEventListener);
158
+ this.subscriptionHandlerIds.add(subscriptionId);
141
159
  const response = yield this.request(request);
142
160
  if (response.error) {
143
161
  // Restore the previous handler if one existed, otherwise clean up.
@@ -146,12 +164,14 @@ export class JsonRpcSocket {
146
164
  }
147
165
  else {
148
166
  this.messageHandlers.delete(subscriptionId);
167
+ this.subscriptionHandlerIds.delete(subscriptionId);
149
168
  }
150
169
  return { response };
151
170
  }
152
171
  // clean up listener and create a `rpc.subscribe.close` message to use when closing this JSON RPC subscription
153
172
  const close = () => __awaiter(this, void 0, void 0, function* () {
154
173
  this.messageHandlers.delete(subscriptionId);
174
+ this.subscriptionHandlerIds.delete(subscriptionId);
155
175
  yield this.closeSubscription(subscriptionId);
156
176
  });
157
177
  return {
@@ -171,5 +191,137 @@ export class JsonRpcSocket {
171
191
  send(request) {
172
192
  this.socket.send(JSON.stringify(request));
173
193
  }
194
+ // ---------------------------------------------------------------------------
195
+ // Internal: socket wiring and reconnection
196
+ // ---------------------------------------------------------------------------
197
+ /**
198
+ * Creates and connects a raw WebSocket, resolving when `open` fires.
199
+ */
200
+ static createWebSocket(url, connectTimeout) {
201
+ return new Promise((resolve, reject) => {
202
+ const ws = new WebSocket(url);
203
+ const onOpen = () => {
204
+ cleanup();
205
+ resolve(ws);
206
+ };
207
+ const onError = (error) => {
208
+ cleanup();
209
+ reject(error);
210
+ };
211
+ const timer = setTimeout(() => {
212
+ cleanup();
213
+ ws.close();
214
+ reject(new Error('connect timed out'));
215
+ }, connectTimeout);
216
+ const cleanup = () => {
217
+ clearTimeout(timer);
218
+ ws.removeEventListener('open', onOpen);
219
+ ws.removeEventListener('error', onError);
220
+ };
221
+ ws.addEventListener('open', onOpen);
222
+ ws.addEventListener('error', onError);
223
+ });
224
+ }
225
+ /**
226
+ * Wires the `onmessage`, `onclose`, and `onerror` handlers for a given
227
+ * WebSocket instance. Called both on initial connect and on reconnect.
228
+ */
229
+ wireSocket(ws) {
230
+ ws.addEventListener('message', (event) => {
231
+ const jsonRpcResponse = parseJson(toText(event.data));
232
+ if (jsonRpcResponse === null) {
233
+ return;
234
+ }
235
+ const handler = this.messageHandlers.get(jsonRpcResponse.id);
236
+ if (handler) {
237
+ handler(event);
238
+ }
239
+ });
240
+ ws.addEventListener('close', () => {
241
+ var _a, _b, _c, _d, _e;
242
+ this._isConnected = false;
243
+ if (this.closedByUser) {
244
+ (_b = (_a = this.options).onclose) === null || _b === void 0 ? void 0 : _b.call(_a);
245
+ return;
246
+ }
247
+ // Reject all pending one-shot request handlers (non-subscription).
248
+ this.rejectPendingRequests();
249
+ // Notify the user handler if present.
250
+ (_d = (_c = this.options).onclose) === null || _d === void 0 ? void 0 : _d.call(_c);
251
+ // Attempt reconnection if enabled.
252
+ const autoReconnect = (_e = this.options.autoReconnect) !== null && _e !== void 0 ? _e : true;
253
+ if (autoReconnect && !this.reconnecting) {
254
+ this.attemptReconnect();
255
+ }
256
+ });
257
+ ws.addEventListener('error', (error) => {
258
+ var _a, _b;
259
+ (_b = (_a = this.options).onerror) === null || _b === void 0 ? void 0 : _b.call(_a, error);
260
+ });
261
+ }
262
+ /**
263
+ * Rejects all pending one-shot request handlers (those not in `subscriptionHandlerIds`)
264
+ * by synthesizing a transport error event.
265
+ */
266
+ rejectPendingRequests() {
267
+ for (const [id, handler] of this.messageHandlers) {
268
+ if (!this.subscriptionHandlerIds.has(id)) {
269
+ // Synthesize an error response to reject the pending promise.
270
+ const errorData = JSON.stringify({
271
+ jsonrpc: '2.0',
272
+ id,
273
+ error: { code: JsonRpcErrorCodes.TransportError, message: 'WebSocket connection closed unexpectedly' },
274
+ });
275
+ handler({ data: errorData });
276
+ this.messageHandlers.delete(id);
277
+ }
278
+ }
279
+ }
280
+ /**
281
+ * Exponential backoff reconnection loop with jitter.
282
+ */
283
+ attemptReconnect() {
284
+ var _a, _b, _c, _d;
285
+ this.reconnecting = true;
286
+ const baseDelay = (_a = this.options.baseReconnectDelay) !== null && _a !== void 0 ? _a : DEFAULT_BASE_RECONNECT_DELAY;
287
+ const maxDelay = (_b = this.options.maxReconnectDelay) !== null && _b !== void 0 ? _b : DEFAULT_MAX_RECONNECT_DELAY;
288
+ const maxAttempts = (_c = this.options.maxReconnectAttempts) !== null && _c !== void 0 ? _c : DEFAULT_MAX_RECONNECT_ATTEMPTS;
289
+ const connectTimeout = (_d = this.options.connectTimeout) !== null && _d !== void 0 ? _d : CONNECT_TIMEOUT;
290
+ let attempt = 0;
291
+ const tryReconnect = () => __awaiter(this, void 0, void 0, function* () {
292
+ var _a, _b, _c, _d;
293
+ if (this.closedByUser) {
294
+ this.reconnecting = false;
295
+ return;
296
+ }
297
+ attempt++;
298
+ if (attempt > maxAttempts) {
299
+ this.reconnecting = false;
300
+ return;
301
+ }
302
+ (_b = (_a = this.options).onreconnecting) === null || _b === void 0 ? void 0 : _b.call(_a, attempt);
303
+ // Exponential backoff with jitter: delay = min(baseDelay * 2^(attempt-1), maxDelay) * (0.5 + random*0.5)
304
+ const expDelay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
305
+ const jitteredDelay = expDelay * (0.5 + Math.random() * 0.5);
306
+ yield new Promise(resolve => setTimeout(resolve, jitteredDelay));
307
+ if (this.closedByUser) {
308
+ this.reconnecting = false;
309
+ return;
310
+ }
311
+ try {
312
+ const newSocket = yield JsonRpcSocket.createWebSocket(this.url, connectTimeout);
313
+ this.socket = newSocket;
314
+ this._isConnected = true;
315
+ this.reconnecting = false;
316
+ this.wireSocket(newSocket);
317
+ (_d = (_c = this.options).onreconnected) === null || _d === void 0 ? void 0 : _d.call(_c);
318
+ }
319
+ catch (_e) {
320
+ // Connection failed — retry.
321
+ yield tryReconnect();
322
+ }
323
+ });
324
+ tryReconnect();
325
+ }
174
326
  }
175
327
  //# sourceMappingURL=json-rpc-socket.js.map