@enbox/dwn-clients 0.0.6 → 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.
- package/dist/esm/dwn-registrar.js +6 -0
- package/dist/esm/dwn-registrar.js.map +1 -1
- package/dist/esm/http-dwn-rpc-client.js +140 -5
- package/dist/esm/http-dwn-rpc-client.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/json-rpc.js +1 -0
- package/dist/esm/json-rpc.js.map +1 -1
- package/dist/esm/rate-limit-error.js +14 -0
- package/dist/esm/rate-limit-error.js.map +1 -0
- package/dist/esm/rpc-client.js +1 -1
- package/dist/esm/rpc-client.js.map +1 -1
- package/dist/types/dwn-registrar.d.ts.map +1 -1
- package/dist/types/http-dwn-rpc-client.d.ts +24 -2
- package/dist/types/http-dwn-rpc-client.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/json-rpc.d.ts +2 -1
- package/dist/types/json-rpc.d.ts.map +1 -1
- package/dist/types/rate-limit-error.d.ts +12 -0
- package/dist/types/rate-limit-error.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/dwn-registrar.ts +8 -2
- package/src/http-dwn-rpc-client.ts +175 -5
- package/src/index.ts +1 -0
- package/src/json-rpc.ts +1 -0
- package/src/rate-limit-error.ts +16 -0
- package/src/rpc-client.ts +1 -1
|
@@ -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;
|
|
@@ -134,6 +137,7 @@ export class DwnRegistrar {
|
|
|
134
137
|
code,
|
|
135
138
|
redirectUri,
|
|
136
139
|
}),
|
|
140
|
+
signal: AbortSignal.timeout(30000),
|
|
137
141
|
});
|
|
138
142
|
if (response.status !== 200) {
|
|
139
143
|
const errorText = yield response.text();
|
|
@@ -158,6 +162,7 @@ export class DwnRegistrar {
|
|
|
158
162
|
grantType: 'refresh_token',
|
|
159
163
|
refreshToken,
|
|
160
164
|
}),
|
|
165
|
+
signal: AbortSignal.timeout(30000),
|
|
161
166
|
});
|
|
162
167
|
if (response.status !== 200) {
|
|
163
168
|
const errorText = yield response.text();
|
|
@@ -189,6 +194,7 @@ export class DwnRegistrar {
|
|
|
189
194
|
method: 'POST',
|
|
190
195
|
headers: { 'Content-Type': 'application/json' },
|
|
191
196
|
body: JSON.stringify(registrationRequest),
|
|
197
|
+
signal: AbortSignal.timeout(30000),
|
|
192
198
|
});
|
|
193
199
|
if (response.status !== 200) {
|
|
194
200
|
const errorText = yield response.text();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dwn-registrar.js","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":";;;;;;;;;AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEpD;;GAEG;AACH,MAAM,OAAO,YAAY;IACvB;;;;OAIG;IACI,MAAM,CAAO,cAAc,CAAC,WAAmB,EAAE,GAAW;;YAEjE,MAAM,oBAAoB,GAAG,cAAc,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;YACzE,MAAM,kBAAkB,GAAG,cAAc,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;YACpF,MAAM,mBAAmB,GAAG,cAAc,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;YAElF,6BAA6B;YAC7B,MAAM,yBAAyB,GAAG,MAAM,KAAK,CAAC,kBAAkB,EAAE;gBAChE,MAAM,
|
|
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 {
|
|
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
|
-
*
|
|
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
|
|
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
|
|
@@ -64,6 +142,10 @@ export class HttpDwnRpcClient {
|
|
|
64
142
|
}
|
|
65
143
|
if (dwnRpcResponse.error) {
|
|
66
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
|
+
}
|
|
67
149
|
throw new Error(`(${code}) - ${message}`);
|
|
68
150
|
}
|
|
69
151
|
// Materialise the response body before attaching to the reply.
|
|
@@ -88,6 +170,7 @@ export class HttpDwnRpcClient {
|
|
|
88
170
|
}
|
|
89
171
|
getServerInfo(dwnUrl) {
|
|
90
172
|
return __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
var _a;
|
|
91
174
|
const serverInfo = yield this.serverInfoCache.get(dwnUrl);
|
|
92
175
|
if (serverInfo) {
|
|
93
176
|
return serverInfo;
|
|
@@ -96,7 +179,11 @@ export class HttpDwnRpcClient {
|
|
|
96
179
|
// add `/info` to the dwn server url path
|
|
97
180
|
url.pathname.endsWith('/') ? url.pathname += 'info' : url.pathname += '/info';
|
|
98
181
|
try {
|
|
99
|
-
const response = yield
|
|
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
|
+
}
|
|
100
187
|
if (response.ok) {
|
|
101
188
|
const results = yield response.json();
|
|
102
189
|
const serverInfo = {
|
|
@@ -120,5 +207,53 @@ export class HttpDwnRpcClient {
|
|
|
120
207
|
}
|
|
121
208
|
});
|
|
122
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
|
+
}
|
|
123
258
|
}
|
|
124
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":";;;;;;;;;AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;
|
|
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
|
@@ -5,6 +5,7 @@ export * from './http-dwn-rpc-client.js';
|
|
|
5
5
|
export * from './json-rpc.js';
|
|
6
6
|
export * from './json-rpc-socket.js';
|
|
7
7
|
export * from './provider-directory-types.js';
|
|
8
|
+
export * from './rate-limit-error.js';
|
|
8
9
|
export * from './registration-types.js';
|
|
9
10
|
export * from './rpc-client.js';
|
|
10
11
|
export * from './server-info-types.js';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -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,+BAA+B,CAAC;AAC9C,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"}
|
package/dist/esm/json-rpc.js
CHANGED
|
@@ -12,6 +12,7 @@ export var JsonRpcErrorCodes;
|
|
|
12
12
|
JsonRpcErrorCodes[JsonRpcErrorCodes["Unauthorized"] = -50401] = "Unauthorized";
|
|
13
13
|
JsonRpcErrorCodes[JsonRpcErrorCodes["Forbidden"] = -50403] = "Forbidden";
|
|
14
14
|
JsonRpcErrorCodes[JsonRpcErrorCodes["Conflict"] = -50409] = "Conflict";
|
|
15
|
+
JsonRpcErrorCodes[JsonRpcErrorCodes["TooManyRequests"] = -50429] = "TooManyRequests";
|
|
15
16
|
})(JsonRpcErrorCodes || (JsonRpcErrorCodes = {}));
|
|
16
17
|
export const createJsonRpcErrorResponse = (id, code, message, data) => {
|
|
17
18
|
const error = { code, message };
|
package/dist/esm/json-rpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-rpc.js","sourceRoot":"","sources":["../../src/json-rpc.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"json-rpc.js","sourceRoot":"","sources":["../../src/json-rpc.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAN,IAAY,iBAeX;AAfD,WAAY,iBAAiB;IAC3B,kCAAkC;IAClC,kFAAuB,CAAA;IACvB,kFAAuB,CAAA;IACvB,gFAAsB,CAAA;IACtB,gFAAsB,CAAA;IACtB,0EAAmB,CAAA;IACnB,kFAAuB,CAAA;IAEvB,qBAAqB;IACrB,0EAAmB,CAAA;IACnB,8EAAqB,CAAA;IACrB,wEAAkB,CAAA;IAClB,sEAAiB,CAAA;IACjB,oFAAwB,CAAA;AAC1B,CAAC,EAfW,iBAAiB,KAAjB,iBAAiB,QAe5B;AAkBD,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,EAAa,EACb,IAAuB,EACvB,OAAe,EACf,IAAU,EACY,EAAE;IACxB,MAAM,KAAK,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC9C,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IACpB,CAAC;IACD,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,KAAK;KACN,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,MAAc,EACd,MAAY,EACI,EAAE;IAClB,OAAO;QACL,OAAO,EAAE,KAAK;QACd,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,EAAa,EACb,MAAc,EACd,MAAsB,EACN,EAAE;IAClB,OAAO;QACL,OAAO,EAAE,KAAK;QACd,EAAE;QACF,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAC9C,EAAa,EACb,MAAc,EACd,MAAY,EACZ,cAA0B,EACV,EAAE;IAClB,OAAO;QACL,OAAO,EAAQ,KAAK;QACpB,EAAE;QACF,MAAM;QACN,MAAM;QACN,YAAY,EAAG;YACb,EAAE,EAAE,cAAc,aAAd,cAAc,cAAd,cAAc,GAAI,IAAI;SAC3B;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,cAAyB,EACzB,MAAc,EACE,EAAE;IAClB,OAAO;QACL,OAAO,EAAQ,KAAK;QACpB,MAAM,EAAS,SAAS;QACxB,MAAM,EAAS,EAAE,MAAM,EAAE;QACzB,YAAY,EAAG,EAAE,EAAE,EAAE,cAAc,EAAE;KACtC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,EAAa,EACb,MAAY,EACY,EAAE;IAC1B,OAAO;QACL,OAAO,EAAG,KAAK;QACf,EAAE;QACF,MAAM,EAAI,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,IAAI;KACzB,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a DWN server rejects a request due to rate limiting (HTTP 429).
|
|
3
|
+
*
|
|
4
|
+
* Consumers can catch this error to implement retry logic using the
|
|
5
|
+
* {@link retryAfterSec} value provided by the server.
|
|
6
|
+
*/
|
|
7
|
+
export class RateLimitError extends Error {
|
|
8
|
+
constructor(retryAfterSec, message) {
|
|
9
|
+
super(message !== null && message !== void 0 ? message : `Rate limit exceeded, retry after ${retryAfterSec}s`);
|
|
10
|
+
this.name = 'RateLimitError';
|
|
11
|
+
this.retryAfterSec = retryAfterSec;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=rate-limit-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-error.js","sourceRoot":"","sources":["../../src/rate-limit-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAIvC,YAAY,aAAqB,EAAE,OAAgB;QACjD,KAAK,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,oCAAoC,aAAa,GAAG,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;CACF"}
|
package/dist/esm/rpc-client.js
CHANGED
|
@@ -88,7 +88,7 @@ export class HttpWeb5RpcClient extends HttpDwnRpcClient {
|
|
|
88
88
|
});
|
|
89
89
|
let jsonRpcResponse;
|
|
90
90
|
try {
|
|
91
|
-
const response = yield fetch(httpRequest);
|
|
91
|
+
const response = yield fetch(httpRequest, { signal: AbortSignal.timeout(30000) });
|
|
92
92
|
if (response.ok) {
|
|
93
93
|
jsonRpcResponse = yield response.json();
|
|
94
94
|
// If the response is an error, throw an error.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/rpc-client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/rpc-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dwn-registrar.d.ts","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAMvI;;GAEG;AACH,qBAAa,YAAY;IACvB;;;;OAIG;WACiB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"dwn-registrar.d.ts","sourceRoot":"","sources":["../../src/dwn-registrar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAMvI;;GAEG;AACH,qBAAa,YAAY;IACvB;;;;OAIG;WACiB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEnF;;OAEG;WACiB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMnE;;OAEG;WACiB,0BAA0B,CAAC,KAAK,EAAE;QACpD,uBAAuB,EAAE,MAAM,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,MAAM,CAAC;IA2BnB;;OAEG;WACiB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAMpD;;;;;;;;OAQG;WACiB,gBAAgB,CAClC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,qBAAqB,CAAC;IAoBjC;;;;;;OAMG;WACiB,wBAAwB,CAC1C,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,qBAAqB,CAAC;IAmBjC;;;;;;;;OAQG;WACiB,uBAAuB,CACzC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,iBAAiB,EAAE,MAAM,EACzB,kBAAkB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,IAAI,CAAC;CAuBjB"}
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import type { DwnRpc, DwnRpcRequest, DwnRpcResponse } from './dwn-rpc-types.js';
|
|
2
2
|
import type { DwnServerInfoCache, ServerInfo } from './server-info-types.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Options for controlling HTTP retry behaviour.
|
|
5
|
+
*/
|
|
6
|
+
export type HttpRetryOptions = {
|
|
7
|
+
/** Maximum number of retry attempts (0 = no retries). Default: 3. */
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
/** Base delay in milliseconds for exponential backoff. Default: 500. */
|
|
10
|
+
baseDelayMs?: number;
|
|
11
|
+
/** Maximum backoff delay in milliseconds. Default: 10 000. */
|
|
12
|
+
maxDelayMs?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* HTTP client that can be used to communicate with Dwn Servers.
|
|
16
|
+
*
|
|
17
|
+
* Supports automatic retry with exponential backoff and jitter for transient
|
|
18
|
+
* network errors and retryable HTTP status codes (408, 429, 500, 502, 503, 504).
|
|
19
|
+
* Respects the `Retry-After` response header when present.
|
|
5
20
|
*/
|
|
6
21
|
export declare class HttpDwnRpcClient implements DwnRpc {
|
|
7
22
|
private serverInfoCache;
|
|
8
|
-
|
|
23
|
+
private _retryOptions;
|
|
24
|
+
constructor(serverInfoCache?: DwnServerInfoCache, retryOptions?: HttpRetryOptions);
|
|
9
25
|
get transportProtocols(): string[];
|
|
10
26
|
sendDwnRequest(request: DwnRpcRequest): Promise<DwnRpcResponse>;
|
|
11
27
|
getServerInfo(dwnUrl: string): Promise<ServerInfo>;
|
|
28
|
+
/**
|
|
29
|
+
* Wrapper around `fetch()` that retries on transient network errors and
|
|
30
|
+
* retryable HTTP status codes with exponential backoff and jitter.
|
|
31
|
+
* Honours the `Retry-After` response header when present.
|
|
32
|
+
*/
|
|
33
|
+
private fetchWithRetry;
|
|
12
34
|
}
|
|
13
35
|
//# sourceMappingURL=http-dwn-rpc-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-dwn-rpc-client.d.ts","sourceRoot":"","sources":["../../src/http-dwn-rpc-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-dwn-rpc-client.d.ts","sourceRoot":"","sources":["../../src/http-dwn-rpc-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2B7E;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAsDF;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,MAAM;IAC7C,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,eAAe,CAAC,EAAE,kBAAkB,EAAE,YAAY,CAAC,EAAE,gBAAgB;IASjF,IAAI,kBAAkB,IAAI,MAAM,EAAE,CAAgC;IAE5D,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IA2F/D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA4CxD;;;;OAIG;YACW,cAAc;CAgD7B"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './http-dwn-rpc-client.js';
|
|
|
5
5
|
export * from './json-rpc.js';
|
|
6
6
|
export * from './json-rpc-socket.js';
|
|
7
7
|
export * from './provider-directory-types.js';
|
|
8
|
+
export * from './rate-limit-error.js';
|
|
8
9
|
export * from './registration-types.js';
|
|
9
10
|
export * from './rpc-client.js';
|
|
10
11
|
export * from './server-info-types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","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,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","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"}
|
package/dist/types/json-rpc.d.ts
CHANGED
|
@@ -31,7 +31,8 @@ export declare enum JsonRpcErrorCodes {
|
|
|
31
31
|
BadRequest = -50400,// equivalent to HTTP Status 400
|
|
32
32
|
Unauthorized = -50401,// equivalent to HTTP Status 401
|
|
33
33
|
Forbidden = -50403,// equivalent to HTTP Status 403
|
|
34
|
-
Conflict = -50409
|
|
34
|
+
Conflict = -50409,// equivalent to HTTP Status 409
|
|
35
|
+
TooManyRequests = -50429
|
|
35
36
|
}
|
|
36
37
|
export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
|
|
37
38
|
export interface JsonRpcSuccessResponse {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-rpc.d.ts","sourceRoot":"","sources":["../../src/json-rpc.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAC/C,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,iDAAiD;IACjD,YAAY,CAAC,EAAE;QAAE,EAAE,EAAE,SAAS,CAAA;KAAE,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,oBAAY,iBAAiB;IAE3B,cAAc,SAAS;IACvB,cAAc,SAAS;IACvB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,UAAU,SAAS;IACnB,cAAc,SAAS;IAGvB,UAAU,SAAS,CAAE,gCAAgC;IACrD,YAAY,SAAS,CAAE,gCAAgC;IACvD,SAAS,SAAS,CAAE,gCAAgC;IACpD,QAAQ,SAAS;
|
|
1
|
+
{"version":3,"file":"json-rpc.d.ts","sourceRoot":"","sources":["../../src/json-rpc.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAC/C,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,CAAC,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,iDAAiD;IACjD,YAAY,CAAC,EAAE;QAAE,EAAE,EAAE,SAAS,CAAA;KAAE,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,oBAAY,iBAAiB;IAE3B,cAAc,SAAS;IACvB,cAAc,SAAS;IACvB,aAAa,SAAS;IACtB,aAAa,SAAS;IACtB,UAAU,SAAS;IACnB,cAAc,SAAS;IAGvB,UAAU,SAAS,CAAE,gCAAgC;IACrD,YAAY,SAAS,CAAE,gCAAgC;IACvD,SAAS,SAAS,CAAE,gCAAgC;IACpD,QAAQ,SAAS,CAAE,gCAAgC;IACnD,eAAe,SAAS;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,sBAAsB,GAAG,oBAAoB,CAAC;AAE5E,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,EAAE,GAAG,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,cAAc,CAAC;IACxB,EAAE,EAAE,SAAS,CAAC;IACd,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,eAAO,MAAM,0BAA0B,OACjC,SAAS,QACP,iBAAiB,WACd,MAAM,SACR,GAAG,KACT,oBAUF,CAAC;AAEF,eAAO,MAAM,yBAAyB,WAC5B,MAAM,WACL,GAAG,KACX,cAMF,CAAC;AAEF,eAAO,MAAM,oBAAoB,OAC3B,SAAS,UACL,MAAM,WACL,aAAa,KACrB,cAOF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,OACvC,SAAS,UACL,MAAM,WACL,GAAG,mBACK,SAAS,KACzB,cAUF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,mBACX,SAAS,UACjB,MAAM,KACb,cAOF,CAAC;AAEF,eAAO,MAAM,4BAA4B,OACnC,SAAS,WACJ,GAAG,KACX,sBAMF,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMrD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a DWN server rejects a request due to rate limiting (HTTP 429).
|
|
3
|
+
*
|
|
4
|
+
* Consumers can catch this error to implement retry logic using the
|
|
5
|
+
* {@link retryAfterSec} value provided by the server.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RateLimitError extends Error {
|
|
8
|
+
/** Seconds the client should wait before retrying, as reported by the server. */
|
|
9
|
+
readonly retryAfterSec: number;
|
|
10
|
+
constructor(retryAfterSec: number, message?: string);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=rate-limit-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-error.d.ts","sourceRoot":"","sources":["../../src/rate-limit-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,iFAAiF;IACjF,SAAgB,aAAa,EAAE,MAAM,CAAC;gBAE1B,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAKpD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enbox/dwn-clients",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"bun": ">=1.0.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@enbox/common": "0.0.
|
|
50
|
-
"@enbox/crypto": "0.0.
|
|
51
|
-
"@enbox/dwn-sdk-js": "0.0
|
|
49
|
+
"@enbox/common": "0.0.5",
|
|
50
|
+
"@enbox/crypto": "0.0.6",
|
|
51
|
+
"@enbox/dwn-sdk-js": "0.1.0",
|
|
52
52
|
"ms": "2.1.3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
package/src/dwn-registrar.ts
CHANGED
|
@@ -21,7 +21,8 @@ export class DwnRegistrar {
|
|
|
21
21
|
|
|
22
22
|
// fetch the terms-of-service
|
|
23
23
|
const termsOfServiceGetResponse = await fetch(termsOfUseEndpoint, {
|
|
24
|
-
method: 'GET',
|
|
24
|
+
method : 'GET',
|
|
25
|
+
signal : AbortSignal.timeout(30_000),
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
if (termsOfServiceGetResponse.status !== 200) {
|
|
@@ -34,7 +35,8 @@ export class DwnRegistrar {
|
|
|
34
35
|
|
|
35
36
|
// fetch the proof-of-work challenge
|
|
36
37
|
const proofOfWorkChallengeGetResponse = await fetch(proofOfWorkEndpoint, {
|
|
37
|
-
method: 'GET',
|
|
38
|
+
method : 'GET',
|
|
39
|
+
signal : AbortSignal.timeout(30_000),
|
|
38
40
|
});
|
|
39
41
|
const { challengeNonce, maximumAllowedHashValue }: ProofOfWorkChallengeModel =
|
|
40
42
|
await proofOfWorkChallengeGetResponse.json();
|
|
@@ -65,6 +67,7 @@ export class DwnRegistrar {
|
|
|
65
67
|
method : 'POST',
|
|
66
68
|
headers : { 'Content-Type': 'application/json' },
|
|
67
69
|
body : JSON.stringify(registrationRequest),
|
|
70
|
+
signal : AbortSignal.timeout(30_000),
|
|
68
71
|
});
|
|
69
72
|
|
|
70
73
|
if (registrationResponse.status !== 200) {
|
|
@@ -149,6 +152,7 @@ export class DwnRegistrar {
|
|
|
149
152
|
code,
|
|
150
153
|
redirectUri,
|
|
151
154
|
}),
|
|
155
|
+
signal: AbortSignal.timeout(30_000),
|
|
152
156
|
});
|
|
153
157
|
|
|
154
158
|
if (response.status !== 200) {
|
|
@@ -177,6 +181,7 @@ export class DwnRegistrar {
|
|
|
177
181
|
grantType: 'refresh_token',
|
|
178
182
|
refreshToken,
|
|
179
183
|
}),
|
|
184
|
+
signal: AbortSignal.timeout(30_000),
|
|
180
185
|
});
|
|
181
186
|
|
|
182
187
|
if (response.status !== 200) {
|
|
@@ -216,6 +221,7 @@ export class DwnRegistrar {
|
|
|
216
221
|
method : 'POST',
|
|
217
222
|
headers : { 'Content-Type': 'application/json' },
|
|
218
223
|
body : JSON.stringify(registrationRequest),
|
|
224
|
+
signal : AbortSignal.timeout(30_000),
|
|
219
225
|
});
|
|
220
226
|
|
|
221
227
|
if (response.status !== 200) {
|
|
@@ -5,15 +5,110 @@ import type { DwnServerInfoCache, ServerInfo } from './server-info-types.js';
|
|
|
5
5
|
import { CryptoUtils } from '@enbox/crypto';
|
|
6
6
|
import { DataStream } from '@enbox/dwn-sdk-js';
|
|
7
7
|
import { DwnServerInfoCacheMemory } from './dwn-server-info-cache-memory.js';
|
|
8
|
-
import {
|
|
8
|
+
import { RateLimitError } from './rate-limit-error.js';
|
|
9
|
+
import { createJsonRpcRequest, JsonRpcErrorCodes, parseJson } from './json-rpc.js';
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Retry configuration
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
/** Default number of retry attempts for transient HTTP failures. */
|
|
16
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
17
|
+
|
|
18
|
+
/** Base delay in milliseconds for exponential backoff. */
|
|
19
|
+
const DEFAULT_BASE_DELAY_MS = 500;
|
|
20
|
+
|
|
21
|
+
/** Maximum backoff delay in milliseconds. */
|
|
22
|
+
const DEFAULT_MAX_DELAY_MS = 10_000;
|
|
23
|
+
|
|
24
|
+
/** Per-request timeout in milliseconds (prevents hung connections / SSRF). */
|
|
25
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
26
|
+
|
|
27
|
+
/** HTTP status codes that are considered retryable. */
|
|
28
|
+
const RETRYABLE_STATUS_CODES = new Set([408, 429, 500, 502, 503, 504]);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for controlling HTTP retry behaviour.
|
|
32
|
+
*/
|
|
33
|
+
export type HttpRetryOptions = {
|
|
34
|
+
/** Maximum number of retry attempts (0 = no retries). Default: 3. */
|
|
35
|
+
maxRetries?: number;
|
|
36
|
+
/** Base delay in milliseconds for exponential backoff. Default: 500. */
|
|
37
|
+
baseDelayMs?: number;
|
|
38
|
+
/** Maximum backoff delay in milliseconds. Default: 10 000. */
|
|
39
|
+
maxDelayMs?: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Determines whether a fetch error or HTTP response warrants a retry.
|
|
44
|
+
* Network errors (TypeError from fetch) and specific HTTP status codes are retryable.
|
|
45
|
+
*/
|
|
46
|
+
function isRetryable(error?: unknown, response?: Response): boolean {
|
|
47
|
+
if (error instanceof TypeError) {
|
|
48
|
+
// TypeError is thrown by fetch for network-level failures (DNS, connection refused, etc.).
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (response) {
|
|
52
|
+
return RETRYABLE_STATUS_CODES.has(response.status);
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
9
56
|
|
|
10
57
|
/**
|
|
11
|
-
*
|
|
58
|
+
* Computes the backoff delay with jitter for a given attempt.
|
|
59
|
+
* Uses exponential backoff: `min(baseDelay * 2^attempt, maxDelay) * jitter`.
|
|
60
|
+
*/
|
|
61
|
+
function computeBackoffDelay(attempt: number, baseDelayMs: number, maxDelayMs: number): number {
|
|
62
|
+
const exponentialDelay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
63
|
+
const jitter = 0.5 + Math.random() * 0.5;
|
|
64
|
+
return exponentialDelay * jitter;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parses a `retry-after` header value into milliseconds.
|
|
69
|
+
* Supports both delay-seconds (e.g. "120") and HTTP-date formats.
|
|
70
|
+
* Returns `undefined` if the header is absent or unparseable.
|
|
71
|
+
*/
|
|
72
|
+
function parseRetryAfterMs(response: Response): number | undefined {
|
|
73
|
+
const retryAfter = response.headers.get('retry-after');
|
|
74
|
+
if (retryAfter === null) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Try as integer seconds first.
|
|
79
|
+
const seconds = Number(retryAfter);
|
|
80
|
+
if (!Number.isNaN(seconds) && seconds >= 0) {
|
|
81
|
+
return seconds * 1000;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Try as HTTP-date.
|
|
85
|
+
const date = new Date(retryAfter);
|
|
86
|
+
if (!Number.isNaN(date.getTime())) {
|
|
87
|
+
const delayMs = date.getTime() - Date.now();
|
|
88
|
+
return delayMs > 0 ? delayMs : 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* HTTP client that can be used to communicate with Dwn Servers.
|
|
96
|
+
*
|
|
97
|
+
* Supports automatic retry with exponential backoff and jitter for transient
|
|
98
|
+
* network errors and retryable HTTP status codes (408, 429, 500, 502, 503, 504).
|
|
99
|
+
* Respects the `Retry-After` response header when present.
|
|
12
100
|
*/
|
|
13
101
|
export class HttpDwnRpcClient implements DwnRpc {
|
|
14
102
|
private serverInfoCache: DwnServerInfoCache;
|
|
15
|
-
|
|
103
|
+
private _retryOptions: Required<HttpRetryOptions>;
|
|
104
|
+
|
|
105
|
+
constructor(serverInfoCache?: DwnServerInfoCache, retryOptions?: HttpRetryOptions) {
|
|
16
106
|
this.serverInfoCache = serverInfoCache ?? new DwnServerInfoCacheMemory();
|
|
107
|
+
this._retryOptions = {
|
|
108
|
+
maxRetries : retryOptions?.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
109
|
+
baseDelayMs : retryOptions?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
|
|
110
|
+
maxDelayMs : retryOptions?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
|
|
111
|
+
};
|
|
17
112
|
}
|
|
18
113
|
|
|
19
114
|
get transportProtocols(): string[] { return ['http:', 'https:']; }
|
|
@@ -44,7 +139,16 @@ export class HttpDwnRpcClient implements DwnRpc {
|
|
|
44
139
|
(fetchOpts as Record<string, unknown>).duplex = 'half';
|
|
45
140
|
}
|
|
46
141
|
|
|
47
|
-
const resp = await
|
|
142
|
+
const resp = await this.fetchWithRetry(request.dwnUrl, fetchOpts);
|
|
143
|
+
|
|
144
|
+
// After retries are exhausted, a 429 means we're still rate-limited.
|
|
145
|
+
// Per-IP 429s return plain JSON (not a JSON-RPC envelope), so we must
|
|
146
|
+
// check the status before attempting JSON-RPC parsing.
|
|
147
|
+
if (resp.status === 429) {
|
|
148
|
+
const retryAfter = parseInt(resp.headers.get('retry-after') ?? '1', 10);
|
|
149
|
+
throw new RateLimitError(retryAfter);
|
|
150
|
+
}
|
|
151
|
+
|
|
48
152
|
let dwnRpcResponse: JsonRpcResponse;
|
|
49
153
|
|
|
50
154
|
// When the server streams record data back, the JSON-RPC envelope is in the
|
|
@@ -73,6 +177,10 @@ export class HttpDwnRpcClient implements DwnRpc {
|
|
|
73
177
|
|
|
74
178
|
if (dwnRpcResponse.error) {
|
|
75
179
|
const { code, message } = dwnRpcResponse.error;
|
|
180
|
+
if (code === JsonRpcErrorCodes.TooManyRequests) {
|
|
181
|
+
const retryAfter = dwnRpcResponse.error.data?.retryAfterSec ?? 1;
|
|
182
|
+
throw new RateLimitError(retryAfter);
|
|
183
|
+
}
|
|
76
184
|
throw new Error(`(${code}) - ${message}`);
|
|
77
185
|
}
|
|
78
186
|
|
|
@@ -108,7 +216,11 @@ export class HttpDwnRpcClient implements DwnRpc {
|
|
|
108
216
|
url.pathname.endsWith('/') ? url.pathname += 'info' : url.pathname += '/info';
|
|
109
217
|
|
|
110
218
|
try {
|
|
111
|
-
const response = await
|
|
219
|
+
const response = await this.fetchWithRetry(url.toString());
|
|
220
|
+
if (response.status === 429) {
|
|
221
|
+
const retryAfter = parseInt(response.headers.get('retry-after') ?? '1', 10);
|
|
222
|
+
throw new RateLimitError(retryAfter);
|
|
223
|
+
}
|
|
112
224
|
if (response.ok) {
|
|
113
225
|
const results = await response.json() as ServerInfo;
|
|
114
226
|
|
|
@@ -131,4 +243,62 @@ export class HttpDwnRpcClient implements DwnRpc {
|
|
|
131
243
|
throw new Error(`Error encountered while processing response from ${url.toString()}: ${error.message}`);
|
|
132
244
|
}
|
|
133
245
|
}
|
|
246
|
+
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// Retry logic
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Wrapper around `fetch()` that retries on transient network errors and
|
|
253
|
+
* retryable HTTP status codes with exponential backoff and jitter.
|
|
254
|
+
* Honours the `Retry-After` response header when present.
|
|
255
|
+
*/
|
|
256
|
+
private async fetchWithRetry(url: string, init?: RequestInit): Promise<Response> {
|
|
257
|
+
const { maxRetries, baseDelayMs, maxDelayMs } = this._retryOptions;
|
|
258
|
+
|
|
259
|
+
let lastError: unknown;
|
|
260
|
+
let lastResponse: Response | undefined;
|
|
261
|
+
|
|
262
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
263
|
+
try {
|
|
264
|
+
// Apply a per-attempt timeout to prevent hung connections / SSRF.
|
|
265
|
+
// If the caller already supplied a signal, combine it with the timeout
|
|
266
|
+
// via AbortSignal.any(); otherwise create a fresh timeout signal.
|
|
267
|
+
const timeoutSignal = AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS);
|
|
268
|
+
const attemptInit: RequestInit = {
|
|
269
|
+
...init,
|
|
270
|
+
signal: init?.signal
|
|
271
|
+
? AbortSignal.any([init.signal, timeoutSignal])
|
|
272
|
+
: timeoutSignal,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const response = await fetch(url, attemptInit);
|
|
276
|
+
|
|
277
|
+
if (!RETRYABLE_STATUS_CODES.has(response.status) || attempt === maxRetries) {
|
|
278
|
+
return response;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Retryable status — back off and try again.
|
|
282
|
+
lastResponse = response;
|
|
283
|
+
} catch (error: unknown) {
|
|
284
|
+
if (!isRetryable(error) || attempt === maxRetries) {
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
lastError = error;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Compute the delay, preferring Retry-After when available.
|
|
291
|
+
const retryAfterMs = lastResponse ? parseRetryAfterMs(lastResponse) : undefined;
|
|
292
|
+
const backoffMs = computeBackoffDelay(attempt, baseDelayMs, maxDelayMs);
|
|
293
|
+
const delayMs = retryAfterMs !== undefined ? Math.max(retryAfterMs, backoffMs) : backoffMs;
|
|
294
|
+
|
|
295
|
+
await new Promise<void>((resolve): void => { setTimeout(resolve, delayMs); });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Should not reach here, but satisfy the compiler.
|
|
299
|
+
if (lastResponse) {
|
|
300
|
+
return lastResponse;
|
|
301
|
+
}
|
|
302
|
+
throw lastError;
|
|
303
|
+
}
|
|
134
304
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './http-dwn-rpc-client.js';
|
|
|
5
5
|
export * from './json-rpc.js';
|
|
6
6
|
export * from './json-rpc-socket.js';
|
|
7
7
|
export * from './provider-directory-types.js';
|
|
8
|
+
export * from './rate-limit-error.js';
|
|
8
9
|
export * from './registration-types.js';
|
|
9
10
|
export * from './rpc-client.js';
|
|
10
11
|
export * from './server-info-types.js';
|
package/src/json-rpc.ts
CHANGED
|
@@ -37,6 +37,7 @@ export enum JsonRpcErrorCodes {
|
|
|
37
37
|
Unauthorized = -50401, // equivalent to HTTP Status 401
|
|
38
38
|
Forbidden = -50403, // equivalent to HTTP Status 403
|
|
39
39
|
Conflict = -50409, // equivalent to HTTP Status 409
|
|
40
|
+
TooManyRequests = -50429, // equivalent to HTTP Status 429
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a DWN server rejects a request due to rate limiting (HTTP 429).
|
|
3
|
+
*
|
|
4
|
+
* Consumers can catch this error to implement retry logic using the
|
|
5
|
+
* {@link retryAfterSec} value provided by the server.
|
|
6
|
+
*/
|
|
7
|
+
export class RateLimitError extends Error {
|
|
8
|
+
/** Seconds the client should wait before retrying, as reported by the server. */
|
|
9
|
+
public readonly retryAfterSec: number;
|
|
10
|
+
|
|
11
|
+
constructor(retryAfterSec: number, message?: string) {
|
|
12
|
+
super(message ?? `Rate limit exceeded, retry after ${retryAfterSec}s`);
|
|
13
|
+
this.name = 'RateLimitError';
|
|
14
|
+
this.retryAfterSec = retryAfterSec;
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/rpc-client.ts
CHANGED
|
@@ -128,7 +128,7 @@ export class HttpWeb5RpcClient extends HttpDwnRpcClient implements Web5Rpc {
|
|
|
128
128
|
let jsonRpcResponse: JsonRpcResponse;
|
|
129
129
|
|
|
130
130
|
try {
|
|
131
|
-
const response = await fetch(httpRequest);
|
|
131
|
+
const response = await fetch(httpRequest, { signal: AbortSignal.timeout(30_000) });
|
|
132
132
|
|
|
133
133
|
if (response.ok) {
|
|
134
134
|
jsonRpcResponse = await response.json();
|