@ensnode/ensrainbow-sdk 1.10.1 → 1.11.1
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/client.d.ts +88 -8
- package/dist/client.js +87 -17
- package/dist/client.js.map +1 -1
- package/dist/consts.d.ts +1 -0
- package/dist/consts.js +2 -1
- package/dist/consts.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -17
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { LabelHash, EncodedLabelHash, Label } from 'enssdk';
|
|
2
2
|
import { EnsRainbowClientLabelSet, EnsRainbowPublicConfig } from '@ensnode/ensnode-sdk';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Error thrown by {@link EnsRainbowApiClient} methods when the ENSRainbow service responds
|
|
6
|
+
* with a non-2xx HTTP status code.
|
|
7
|
+
*
|
|
8
|
+
* Carries the HTTP status code as a structured property (rather than only embedding it in the
|
|
9
|
+
* error message) so callers can branch their retry/abort logic on the status — e.g. retry on
|
|
10
|
+
* `503 Service Unavailable` while ENSRainbow bootstraps, but abort immediately on `404`/`500`,
|
|
11
|
+
* which usually indicate a misconfigured base URL or a hard server failure.
|
|
12
|
+
*
|
|
13
|
+
* Network-level failures (DNS, ECONNREFUSED, fetch parse errors) are *not* wrapped in this
|
|
14
|
+
* class — they propagate as their original `Error` (typically a `TypeError` from `fetch`),
|
|
15
|
+
* because such failures are commonly transient during cold start and should remain retryable
|
|
16
|
+
* by callers.
|
|
17
|
+
*/
|
|
18
|
+
declare class EnsRainbowHttpError extends Error {
|
|
19
|
+
readonly name = "EnsRainbowHttpError";
|
|
20
|
+
/**
|
|
21
|
+
* The HTTP status code returned by the ENSRainbow service.
|
|
22
|
+
*/
|
|
23
|
+
readonly status: number;
|
|
24
|
+
/**
|
|
25
|
+
* The HTTP status text returned by the ENSRainbow service, if any.
|
|
26
|
+
*/
|
|
27
|
+
readonly statusText: string;
|
|
28
|
+
constructor(message: string, status: number, statusText?: string);
|
|
29
|
+
}
|
|
4
30
|
declare namespace EnsRainbow {
|
|
5
31
|
export type ApiClientOptions = EnsRainbowApiClientOptions;
|
|
6
32
|
export interface ApiClient {
|
|
@@ -17,6 +43,13 @@ declare namespace EnsRainbow {
|
|
|
17
43
|
*/
|
|
18
44
|
heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;
|
|
19
45
|
health(): Promise<HealthResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Check whether the ENSRainbow service has finished bootstrapping and is ready to serve requests.
|
|
48
|
+
*
|
|
49
|
+
* Throws when the service is not ready (e.g. 503 while the database is still being downloaded
|
|
50
|
+
* or validated) so callers can retry.
|
|
51
|
+
*/
|
|
52
|
+
ready(): Promise<ReadyResponse>;
|
|
20
53
|
getOptions(): Readonly<EnsRainbowApiClientOptions>;
|
|
21
54
|
}
|
|
22
55
|
type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];
|
|
@@ -24,6 +57,21 @@ declare namespace EnsRainbow {
|
|
|
24
57
|
export interface HealthResponse {
|
|
25
58
|
status: "ok";
|
|
26
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Response returned by `GET /ready` when the ENSRainbow service is ready to serve requests.
|
|
62
|
+
*/
|
|
63
|
+
export interface ReadyResponse {
|
|
64
|
+
status: "ok";
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generic error shape used by endpoints that return 503 Service Unavailable while the
|
|
68
|
+
* database is still bootstrapping (downloading, extracting, or validating).
|
|
69
|
+
*/
|
|
70
|
+
export interface ServiceUnavailableError {
|
|
71
|
+
status: typeof StatusCode.Error;
|
|
72
|
+
error: string;
|
|
73
|
+
errorCode: typeof ErrorCode.ServiceUnavailable;
|
|
74
|
+
}
|
|
27
75
|
export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {
|
|
28
76
|
status: Status;
|
|
29
77
|
label?: Label | never;
|
|
@@ -54,12 +102,18 @@ declare namespace EnsRainbow {
|
|
|
54
102
|
error: string;
|
|
55
103
|
errorCode: typeof ErrorCode.BadRequest;
|
|
56
104
|
}
|
|
57
|
-
export
|
|
105
|
+
export interface HealServiceUnavailableError extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {
|
|
106
|
+
status: typeof StatusCode.Error;
|
|
107
|
+
label?: never;
|
|
108
|
+
error: string;
|
|
109
|
+
errorCode: typeof ErrorCode.ServiceUnavailable;
|
|
110
|
+
}
|
|
111
|
+
export type HealResponse = HealSuccess | HealNotFoundError | HealServerError | HealBadRequestError | HealServiceUnavailableError;
|
|
58
112
|
export type HealError = Exclude<HealResponse, HealSuccess>;
|
|
59
113
|
/**
|
|
60
|
-
* Server errors should not be cached.
|
|
114
|
+
* Server errors and transient bootstrap errors should not be cached.
|
|
61
115
|
*/
|
|
62
|
-
export type CacheableHealResponse = Exclude<HealResponse, HealServerError>;
|
|
116
|
+
export type CacheableHealResponse = Exclude<HealResponse, HealServerError | HealServiceUnavailableError>;
|
|
63
117
|
export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {
|
|
64
118
|
status: Status;
|
|
65
119
|
count?: number | never;
|
|
@@ -83,7 +137,14 @@ declare namespace EnsRainbow {
|
|
|
83
137
|
error: string;
|
|
84
138
|
errorCode: typeof ErrorCode.ServerError;
|
|
85
139
|
}
|
|
86
|
-
export
|
|
140
|
+
export interface CountServiceUnavailableError extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {
|
|
141
|
+
status: typeof StatusCode.Error;
|
|
142
|
+
count?: never;
|
|
143
|
+
timestamp?: never;
|
|
144
|
+
error: string;
|
|
145
|
+
errorCode: typeof ErrorCode.ServiceUnavailable;
|
|
146
|
+
}
|
|
147
|
+
export type CountResponse = CountSuccess | CountServerError | CountServiceUnavailableError;
|
|
87
148
|
/**
|
|
88
149
|
* Complete public configuration object for ENSRainbow.
|
|
89
150
|
*
|
|
@@ -105,7 +166,7 @@ interface EnsRainbowApiClientOptions {
|
|
|
105
166
|
*/
|
|
106
167
|
endpointUrl: URL;
|
|
107
168
|
/**
|
|
108
|
-
* Optional label set preferences that the ENSRainbow server at endpointUrl is expected to
|
|
169
|
+
* Optional client label set preferences that the ENSRainbow server at endpointUrl is expected to
|
|
109
170
|
* support. If provided, enables deterministic heal results across time, such that only
|
|
110
171
|
* labels from label sets with versions less than or equal to this value will be returned.
|
|
111
172
|
* Therefore, even if the ENSRainbow server later ingests label sets with greater versions
|
|
@@ -119,7 +180,7 @@ interface EnsRainbowApiClientOptions {
|
|
|
119
180
|
* will be returned.
|
|
120
181
|
* When `labelSetVersion` is defined, `labelSetId` must also be defined.
|
|
121
182
|
*/
|
|
122
|
-
|
|
183
|
+
clientLabelSet?: EnsRainbowClientLabelSet;
|
|
123
184
|
}
|
|
124
185
|
/**
|
|
125
186
|
* ENSRainbow API client
|
|
@@ -137,7 +198,7 @@ interface EnsRainbowApiClientOptions {
|
|
|
137
198
|
declare class EnsRainbowApiClient implements EnsRainbow.ApiClient {
|
|
138
199
|
private readonly options;
|
|
139
200
|
private readonly cache;
|
|
140
|
-
private readonly
|
|
201
|
+
private readonly clientLabelSetSearchParams;
|
|
141
202
|
static readonly DEFAULT_CACHE_CAPACITY = 1000;
|
|
142
203
|
/**
|
|
143
204
|
* Create default client options.
|
|
@@ -229,8 +290,27 @@ declare class EnsRainbowApiClient implements EnsRainbow.ApiClient {
|
|
|
229
290
|
* // }
|
|
230
291
|
*/
|
|
231
292
|
health(): Promise<EnsRainbow.HealthResponse>;
|
|
293
|
+
/**
|
|
294
|
+
* Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).
|
|
295
|
+
*
|
|
296
|
+
* Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon
|
|
297
|
+
* as the HTTP server is accepting requests, `ready()` only resolves once the service has finished
|
|
298
|
+
* bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should
|
|
299
|
+
* poll this method instead of `health()` during startup.
|
|
300
|
+
*
|
|
301
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown
|
|
302
|
+
* error carries the HTTP `status` so callers can distinguish the retryable bootstrap case
|
|
303
|
+
* (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures
|
|
304
|
+
* (e.g. `404`, `500`) and abort retries early in the latter cases.
|
|
305
|
+
* @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error
|
|
306
|
+
* type and should generally remain retryable, since they are common during cold start before
|
|
307
|
+
* the ENSRainbow HTTP server has bound its port.
|
|
308
|
+
*/
|
|
309
|
+
ready(): Promise<EnsRainbow.ReadyResponse>;
|
|
232
310
|
/**
|
|
233
311
|
* Get the public configuration of the ENSRainbow service.
|
|
312
|
+
*
|
|
313
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.
|
|
234
314
|
*/
|
|
235
315
|
config(): Promise<EnsRainbow.ENSRainbowPublicConfig>;
|
|
236
316
|
/**
|
|
@@ -257,4 +337,4 @@ declare const isHealError: (response: EnsRainbow.HealResponse) => response is En
|
|
|
257
337
|
*/
|
|
258
338
|
declare const isCacheableHealResponse: (response: EnsRainbow.HealResponse) => response is EnsRainbow.CacheableHealResponse;
|
|
259
339
|
|
|
260
|
-
export { EnsRainbow, EnsRainbowApiClient, type EnsRainbowApiClientOptions, isCacheableHealResponse, isHealError };
|
|
340
|
+
export { EnsRainbow, EnsRainbowApiClient, type EnsRainbowApiClientOptions, EnsRainbowHttpError, isCacheableHealResponse, isHealError };
|
package/dist/client.js
CHANGED
|
@@ -14,14 +14,31 @@ var StatusCode = {
|
|
|
14
14
|
var ErrorCode = {
|
|
15
15
|
BadRequest: 400,
|
|
16
16
|
NotFound: 404,
|
|
17
|
-
ServerError: 500
|
|
17
|
+
ServerError: 500,
|
|
18
|
+
ServiceUnavailable: 503
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
// src/client.ts
|
|
22
|
+
var EnsRainbowHttpError = class extends Error {
|
|
23
|
+
name = "EnsRainbowHttpError";
|
|
24
|
+
/**
|
|
25
|
+
* The HTTP status code returned by the ENSRainbow service.
|
|
26
|
+
*/
|
|
27
|
+
status;
|
|
28
|
+
/**
|
|
29
|
+
* The HTTP status text returned by the ENSRainbow service, if any.
|
|
30
|
+
*/
|
|
31
|
+
statusText;
|
|
32
|
+
constructor(message, status, statusText = "") {
|
|
33
|
+
super(message);
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.statusText = statusText;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
21
38
|
var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
22
39
|
options;
|
|
23
40
|
cache;
|
|
24
|
-
|
|
41
|
+
clientLabelSetSearchParams;
|
|
25
42
|
static DEFAULT_CACHE_CAPACITY = 1e3;
|
|
26
43
|
/**
|
|
27
44
|
* Create default client options.
|
|
@@ -32,32 +49,35 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
32
49
|
return {
|
|
33
50
|
endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),
|
|
34
51
|
cacheCapacity: _EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,
|
|
35
|
-
|
|
52
|
+
clientLabelSet: buildEnsRainbowClientLabelSet()
|
|
36
53
|
};
|
|
37
54
|
}
|
|
38
55
|
constructor(options = {}) {
|
|
39
|
-
const {
|
|
56
|
+
const { clientLabelSet: optionsClientLabelSet, ...rest } = options;
|
|
40
57
|
const defaultOptions = _EnsRainbowApiClient.defaultOptions();
|
|
41
58
|
const copiedLabelSet = buildEnsRainbowClientLabelSet(
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
optionsClientLabelSet?.labelSetId,
|
|
60
|
+
optionsClientLabelSet?.labelSetVersion
|
|
44
61
|
);
|
|
45
62
|
this.options = {
|
|
46
63
|
...defaultOptions,
|
|
47
64
|
...rest,
|
|
48
|
-
|
|
65
|
+
clientLabelSet: copiedLabelSet
|
|
49
66
|
};
|
|
50
67
|
this.cache = new LruCache(
|
|
51
68
|
this.options.cacheCapacity
|
|
52
69
|
);
|
|
53
|
-
this.
|
|
54
|
-
if (this.options.
|
|
55
|
-
this.
|
|
70
|
+
this.clientLabelSetSearchParams = new URLSearchParams();
|
|
71
|
+
if (this.options.clientLabelSet?.labelSetId !== void 0) {
|
|
72
|
+
this.clientLabelSetSearchParams.append(
|
|
73
|
+
"label_set_id",
|
|
74
|
+
this.options.clientLabelSet.labelSetId
|
|
75
|
+
);
|
|
56
76
|
}
|
|
57
|
-
if (this.options.
|
|
58
|
-
this.
|
|
77
|
+
if (this.options.clientLabelSet?.labelSetVersion !== void 0) {
|
|
78
|
+
this.clientLabelSetSearchParams.append(
|
|
59
79
|
"label_set_version",
|
|
60
|
-
this.options.
|
|
80
|
+
this.options.clientLabelSet.labelSetVersion.toString()
|
|
61
81
|
);
|
|
62
82
|
}
|
|
63
83
|
}
|
|
@@ -120,7 +140,7 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
120
140
|
const cachedResult = this.cache.get(normalizedLabelHash);
|
|
121
141
|
if (cachedResult) return cachedResult;
|
|
122
142
|
const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);
|
|
123
|
-
this.
|
|
143
|
+
this.clientLabelSetSearchParams.forEach((value, key) => {
|
|
124
144
|
url.searchParams.append(key, value);
|
|
125
145
|
});
|
|
126
146
|
const response = await fetch(url);
|
|
@@ -171,15 +191,63 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
171
191
|
*/
|
|
172
192
|
async health() {
|
|
173
193
|
const response = await fetch(new URL("/health", this.options.endpointUrl));
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
throw new EnsRainbowHttpError(
|
|
196
|
+
`ENSRainbow health check failed (HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""})`,
|
|
197
|
+
response.status,
|
|
198
|
+
response.statusText
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return response.json();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).
|
|
205
|
+
*
|
|
206
|
+
* Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon
|
|
207
|
+
* as the HTTP server is accepting requests, `ready()` only resolves once the service has finished
|
|
208
|
+
* bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should
|
|
209
|
+
* poll this method instead of `health()` during startup.
|
|
210
|
+
*
|
|
211
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown
|
|
212
|
+
* error carries the HTTP `status` so callers can distinguish the retryable bootstrap case
|
|
213
|
+
* (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures
|
|
214
|
+
* (e.g. `404`, `500`) and abort retries early in the latter cases.
|
|
215
|
+
* @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error
|
|
216
|
+
* type and should generally remain retryable, since they are common during cold start before
|
|
217
|
+
* the ENSRainbow HTTP server has bound its port.
|
|
218
|
+
*/
|
|
219
|
+
async ready() {
|
|
220
|
+
const response = await fetch(new URL("/ready", this.options.endpointUrl));
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
const statusSuffix = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
223
|
+
if (response.status === 503) {
|
|
224
|
+
throw new EnsRainbowHttpError(
|
|
225
|
+
`ENSRainbow readiness check: service not ready yet (${statusSuffix})`,
|
|
226
|
+
response.status,
|
|
227
|
+
response.statusText
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
throw new EnsRainbowHttpError(
|
|
231
|
+
`ENSRainbow readiness check failed (${statusSuffix}). This usually indicates a non-readiness issue (e.g. wrong base URL, misrouting, or a server error).`,
|
|
232
|
+
response.status,
|
|
233
|
+
response.statusText
|
|
234
|
+
);
|
|
235
|
+
}
|
|
174
236
|
return response.json();
|
|
175
237
|
}
|
|
176
238
|
/**
|
|
177
239
|
* Get the public configuration of the ENSRainbow service.
|
|
240
|
+
*
|
|
241
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.
|
|
178
242
|
*/
|
|
179
243
|
async config() {
|
|
180
244
|
const response = await fetch(new URL("/v1/config", this.options.endpointUrl));
|
|
181
245
|
if (!response.ok) {
|
|
182
|
-
throw new
|
|
246
|
+
throw new EnsRainbowHttpError(
|
|
247
|
+
`Failed to fetch ENSRainbow config: HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`,
|
|
248
|
+
response.status,
|
|
249
|
+
response.statusText
|
|
250
|
+
);
|
|
183
251
|
}
|
|
184
252
|
return response.json();
|
|
185
253
|
}
|
|
@@ -192,7 +260,7 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
192
260
|
const deepCopy = {
|
|
193
261
|
cacheCapacity: this.options.cacheCapacity,
|
|
194
262
|
endpointUrl: new URL(this.options.endpointUrl.href),
|
|
195
|
-
|
|
263
|
+
clientLabelSet: this.options.clientLabelSet ? { ...this.options.clientLabelSet } : void 0
|
|
196
264
|
};
|
|
197
265
|
return Object.freeze(deepCopy);
|
|
198
266
|
}
|
|
@@ -201,10 +269,12 @@ var isHealError = (response) => {
|
|
|
201
269
|
return response.status === StatusCode.Error;
|
|
202
270
|
};
|
|
203
271
|
var isCacheableHealResponse = (response) => {
|
|
204
|
-
|
|
272
|
+
if (response.status === StatusCode.Success) return true;
|
|
273
|
+
return response.errorCode !== ErrorCode.ServerError && response.errorCode !== ErrorCode.ServiceUnavailable;
|
|
205
274
|
};
|
|
206
275
|
export {
|
|
207
276
|
EnsRainbowApiClient,
|
|
277
|
+
EnsRainbowHttpError,
|
|
208
278
|
isCacheableHealResponse,
|
|
209
279
|
isHealError
|
|
210
280
|
};
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/consts.ts"],"sourcesContent":["import type { EncodedLabelHash, Label, LabelHash } from \"enssdk\";\nimport { parseLabelHashOrEncodedLabelHash } from \"enssdk\";\n\nimport {\n buildEnsRainbowClientLabelSet,\n type Cache,\n type EnsRainbowClientLabelSet,\n type EnsRainbowPublicConfig,\n LruCache,\n} from \"@ensnode/ensnode-sdk\";\n\nimport { DEFAULT_ENSRAINBOW_URL, ErrorCode, StatusCode } from \"./consts\";\n\nexport namespace EnsRainbow {\n export type ApiClientOptions = EnsRainbowApiClientOptions;\n\n export interface ApiClient {\n count(): Promise<CountResponse>;\n\n /**\n * Get the public configuration of the ENSRainbow service\n */\n config(): Promise<ENSRainbowPublicConfig>;\n\n /**\n * Heal a labelHash to its original label.\n * Accepts a strict `LabelHash`, an `EncodedLabelHash` (bracket-enclosed), or any string\n * that can be normalized (missing `0x` prefix, uppercase hex chars, or 63-char hex).\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n */\n heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;\n\n health(): Promise<HealthResponse>;\n\n getOptions(): Readonly<EnsRainbowApiClientOptions>;\n }\n\n type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];\n\n type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n export interface HealthResponse {\n status: \"ok\";\n }\n\n export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n label?: Label | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface HealSuccess extends BaseHealResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n label: Label;\n error?: never;\n errorCode?: never;\n }\n\n export interface HealNotFoundError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.NotFound> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.NotFound;\n }\n\n export interface HealServerError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface HealBadRequestError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.BadRequest> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.BadRequest;\n }\n\n export type HealResponse =\n | HealSuccess\n | HealNotFoundError\n | HealServerError\n | HealBadRequestError;\n export type HealError = Exclude<HealResponse, HealSuccess>;\n\n /**\n * Server errors should not be cached.\n */\n export type CacheableHealResponse = Exclude<HealResponse, HealServerError>;\n\n export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n count?: number | never;\n timestamp?: string | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface CountSuccess extends BaseCountResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n /** The total count of labels that can be healed by the ENSRainbow instance. Always a\n * non-negative integer. */\n count: number;\n timestamp: string;\n error?: never;\n errorCode?: never;\n }\n\n export interface CountServerError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export type CountResponse = CountSuccess | CountServerError;\n\n /**\n * Complete public configuration object for ENSRainbow.\n *\n * Contains all public configuration information about the ENSRainbow service instance,\n * including version, label set information, and record counts.\n */\n export type ENSRainbowPublicConfig = EnsRainbowPublicConfig;\n}\n\nexport interface EnsRainbowApiClientOptions {\n /**\n * The maximum number of `HealResponse` values to cache.\n * Must be a non-negative integer.\n * Setting to 0 will disable caching.\n */\n cacheCapacity: number;\n\n /**\n * The URL of an ENSRainbow API endpoint.\n */\n endpointUrl: URL;\n\n /**\n * Optional label set preferences that the ENSRainbow server at endpointUrl is expected to\n * support. If provided, enables deterministic heal results across time, such that only\n * labels from label sets with versions less than or equal to this value will be returned.\n * Therefore, even if the ENSRainbow server later ingests label sets with greater versions\n * than this value, the results returned across time can be deterministic. If\n * provided, heal operations with this EnsRainbowApiClient will validate the ENSRainbow\n * server manages a compatible label set. If not provided no specific labelSetId validation\n * will be performed during heal operations.\n * If `labelSetId` is provided without `labelSetVersion`, the server will use the latest\n * available version.\n * If `labelSetVersion` is defined, only labels from sets less than or equal to this value\n * will be returned.\n * When `labelSetVersion` is defined, `labelSetId` must also be defined.\n */\n labelSet?: EnsRainbowClientLabelSet;\n}\n\n/**\n * ENSRainbow API client\n *\n * @example\n * ```typescript\n * // default options\n * const client = new EnsRainbowApiClient();\n * // custom options\n * const client = new EnsRainbowApiClient({\n * endpointUrl: new URL(\"https://api.ensrainbow.io\"),\n * });\n * ```\n */\nexport class EnsRainbowApiClient implements EnsRainbow.ApiClient {\n private readonly options: EnsRainbowApiClientOptions;\n private readonly cache: Cache<LabelHash, EnsRainbow.CacheableHealResponse>;\n private readonly labelSetSearchParams: URLSearchParams;\n\n public static readonly DEFAULT_CACHE_CAPACITY = 1000;\n\n /**\n * Create default client options.\n *\n * @returns default options\n */\n static defaultOptions(): EnsRainbow.ApiClientOptions {\n return {\n endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),\n cacheCapacity: EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,\n labelSet: buildEnsRainbowClientLabelSet(),\n };\n }\n\n constructor(options: Partial<EnsRainbow.ApiClientOptions> = {}) {\n const { labelSet: optionsLabelSet, ...rest } = options;\n const defaultOptions = EnsRainbowApiClient.defaultOptions();\n\n const copiedLabelSet = buildEnsRainbowClientLabelSet(\n optionsLabelSet?.labelSetId,\n optionsLabelSet?.labelSetVersion,\n );\n\n this.options = {\n ...defaultOptions,\n ...rest,\n labelSet: copiedLabelSet,\n };\n\n this.cache = new LruCache<LabelHash, EnsRainbow.CacheableHealResponse>(\n this.options.cacheCapacity,\n );\n\n // Pre-compute query parameters for label set options\n this.labelSetSearchParams = new URLSearchParams();\n if (this.options.labelSet?.labelSetId !== undefined) {\n this.labelSetSearchParams.append(\"label_set_id\", this.options.labelSet.labelSetId);\n }\n if (this.options.labelSet?.labelSetVersion !== undefined) {\n this.labelSetSearchParams.append(\n \"label_set_version\",\n this.options.labelSet.labelSetVersion.toString(),\n );\n }\n }\n\n /**\n * Attempt to [heal](https://ensnode.io/ensrainbow/concepts/glossary#heal) a labelHash to its original label.\n *\n * Note on returned labels: ENSRainbow returns labels exactly as they are\n * represented in source rainbow table data. This means:\n *\n * - Labels may or may not be ENS-normalized\n * - Labels can contain any valid string, including dots, null bytes, or be empty\n * - Clients should handle all possible string values appropriately\n *\n * @param labelHash - A labelHash to heal, either as a strict `LabelHash`, an `EncodedLabelHash`\n * (bracket-enclosed), or any string that can be normalized (missing `0x` prefix, uppercase hex\n * chars, or 63-char hex are all accepted and normalized automatically).\n * @returns a `HealResponse` indicating the result of the request and the healed label if successful.\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n * ```typescript\n * const response = await client.heal(\n * \"0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc\"\n * );\n *\n * console.log(response);\n *\n * // Output:\n * // {\n * // status: \"success\",\n * // label: \"vitalik\"\n * // }\n *\n * const notFoundResponse = await client.heal(\n * \"0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264\"\n * );\n *\n * console.log(notFoundResponse);\n *\n * // Output:\n * // {\n * // status: \"error\",\n * // error: \"Label not found\",\n * // errorCode: 404\n * // }\n * ```\n */\n async heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<EnsRainbow.HealResponse> {\n let normalizedLabelHash: LabelHash;\n\n try {\n normalizedLabelHash = parseLabelHashOrEncodedLabelHash(labelHash);\n } catch (error) {\n return {\n status: StatusCode.Error,\n error: error instanceof Error ? error.message : String(error),\n errorCode: ErrorCode.BadRequest,\n } as EnsRainbow.HealBadRequestError;\n }\n\n const cachedResult = this.cache.get(normalizedLabelHash);\n if (cachedResult) return cachedResult;\n\n const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);\n\n // Apply pre-computed label set query parameters\n this.labelSetSearchParams.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n const response = await fetch(url);\n const healResponse = (await response.json()) as EnsRainbow.HealResponse;\n\n if (isCacheableHealResponse(healResponse)) {\n this.cache.set(normalizedLabelHash, healResponse);\n }\n\n return healResponse;\n }\n\n /**\n * Get Count of Healable Labels\n *\n * @returns a `CountResponse` indicating the result and the timestamp of the request and the\n * number of healable labels if successful\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n *\n * const response = await client.count();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"success\",\n * // \"count\": 133856894,\n * // \"timestamp\": \"2024-01-30T11:18:56Z\"\n * // }\n *\n */\n async count(): Promise<EnsRainbow.CountResponse> {\n const response = await fetch(new URL(\"/v1/labels/count\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.CountResponse>;\n }\n\n /**\n *\n * Simple verification that the service is running, either in your local setup or for the\n * provided hosted instance.\n * @returns a status of ENS Rainbow service\n * @example\n *\n * const response = await client.health();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"ok\",\n * // }\n */\n async health(): Promise<EnsRainbow.HealthResponse> {\n const response = await fetch(new URL(\"/health\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.HealthResponse>;\n }\n\n /**\n * Get the public configuration of the ENSRainbow service.\n */\n async config(): Promise<EnsRainbow.ENSRainbowPublicConfig> {\n const response = await fetch(new URL(\"/v1/config\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ENSRainbow config: ${response.statusText}`);\n }\n\n return response.json() as Promise<EnsRainbow.ENSRainbowPublicConfig>;\n }\n\n /**\n * Get a copy of the current client options.\n *\n * @returns a copy of the current client options.\n */\n getOptions(): Readonly<EnsRainbowApiClientOptions> {\n // build a deep copy to prevent modification\n const deepCopy = {\n cacheCapacity: this.options.cacheCapacity,\n endpointUrl: new URL(this.options.endpointUrl.href),\n labelSet: this.options.labelSet ? { ...this.options.labelSet } : undefined,\n } satisfies EnsRainbowApiClientOptions;\n\n return Object.freeze(deepCopy);\n }\n}\n\n/**\n * Determine if a heal response is an error.\n *\n * @param response the heal response to check\n * @returns true if the response is an error, false otherwise\n */\nexport const isHealError = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.HealError => {\n return response.status === StatusCode.Error;\n};\n\n/**\n * Determine if a heal response is cacheable.\n *\n * Server errors at not cachable and should be retried.\n *\n * @param response the heal response to check\n * @returns true if the response is cacheable, false otherwise\n */\nexport const isCacheableHealResponse = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.CacheableHealResponse => {\n return response.status === StatusCode.Success || response.errorCode !== ErrorCode.ServerError;\n};\n","export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n} as const;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/consts.ts"],"sourcesContent":["import type { EncodedLabelHash, Label, LabelHash } from \"enssdk\";\nimport { parseLabelHashOrEncodedLabelHash } from \"enssdk\";\n\nimport {\n buildEnsRainbowClientLabelSet,\n type Cache,\n type EnsRainbowClientLabelSet,\n type EnsRainbowPublicConfig,\n LruCache,\n} from \"@ensnode/ensnode-sdk\";\n\nimport { DEFAULT_ENSRAINBOW_URL, ErrorCode, StatusCode } from \"./consts\";\n\n/**\n * Error thrown by {@link EnsRainbowApiClient} methods when the ENSRainbow service responds\n * with a non-2xx HTTP status code.\n *\n * Carries the HTTP status code as a structured property (rather than only embedding it in the\n * error message) so callers can branch their retry/abort logic on the status — e.g. retry on\n * `503 Service Unavailable` while ENSRainbow bootstraps, but abort immediately on `404`/`500`,\n * which usually indicate a misconfigured base URL or a hard server failure.\n *\n * Network-level failures (DNS, ECONNREFUSED, fetch parse errors) are *not* wrapped in this\n * class — they propagate as their original `Error` (typically a `TypeError` from `fetch`),\n * because such failures are commonly transient during cold start and should remain retryable\n * by callers.\n */\nexport class EnsRainbowHttpError extends Error {\n readonly name = \"EnsRainbowHttpError\";\n\n /**\n * The HTTP status code returned by the ENSRainbow service.\n */\n readonly status: number;\n\n /**\n * The HTTP status text returned by the ENSRainbow service, if any.\n */\n readonly statusText: string;\n\n constructor(message: string, status: number, statusText = \"\") {\n super(message);\n this.status = status;\n this.statusText = statusText;\n }\n}\n\nexport namespace EnsRainbow {\n export type ApiClientOptions = EnsRainbowApiClientOptions;\n\n export interface ApiClient {\n count(): Promise<CountResponse>;\n\n /**\n * Get the public configuration of the ENSRainbow service\n */\n config(): Promise<ENSRainbowPublicConfig>;\n\n /**\n * Heal a labelHash to its original label.\n * Accepts a strict `LabelHash`, an `EncodedLabelHash` (bracket-enclosed), or any string\n * that can be normalized (missing `0x` prefix, uppercase hex chars, or 63-char hex).\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n */\n heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;\n\n health(): Promise<HealthResponse>;\n\n /**\n * Check whether the ENSRainbow service has finished bootstrapping and is ready to serve requests.\n *\n * Throws when the service is not ready (e.g. 503 while the database is still being downloaded\n * or validated) so callers can retry.\n */\n ready(): Promise<ReadyResponse>;\n\n getOptions(): Readonly<EnsRainbowApiClientOptions>;\n }\n\n type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];\n\n type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n export interface HealthResponse {\n status: \"ok\";\n }\n\n /**\n * Response returned by `GET /ready` when the ENSRainbow service is ready to serve requests.\n */\n export interface ReadyResponse {\n status: \"ok\";\n }\n\n /**\n * Generic error shape used by endpoints that return 503 Service Unavailable while the\n * database is still bootstrapping (downloading, extracting, or validating).\n */\n export interface ServiceUnavailableError {\n status: typeof StatusCode.Error;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n label?: Label | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface HealSuccess extends BaseHealResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n label: Label;\n error?: never;\n errorCode?: never;\n }\n\n export interface HealNotFoundError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.NotFound> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.NotFound;\n }\n\n export interface HealServerError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface HealBadRequestError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.BadRequest> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.BadRequest;\n }\n\n export interface HealServiceUnavailableError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export type HealResponse =\n | HealSuccess\n | HealNotFoundError\n | HealServerError\n | HealBadRequestError\n | HealServiceUnavailableError;\n export type HealError = Exclude<HealResponse, HealSuccess>;\n\n /**\n * Server errors and transient bootstrap errors should not be cached.\n */\n export type CacheableHealResponse = Exclude<\n HealResponse,\n HealServerError | HealServiceUnavailableError\n >;\n\n export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n count?: number | never;\n timestamp?: string | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface CountSuccess extends BaseCountResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n /** The total count of labels that can be healed by the ENSRainbow instance. Always a\n * non-negative integer. */\n count: number;\n timestamp: string;\n error?: never;\n errorCode?: never;\n }\n\n export interface CountServerError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface CountServiceUnavailableError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export type CountResponse = CountSuccess | CountServerError | CountServiceUnavailableError;\n\n /**\n * Complete public configuration object for ENSRainbow.\n *\n * Contains all public configuration information about the ENSRainbow service instance,\n * including version, label set information, and record counts.\n */\n export type ENSRainbowPublicConfig = EnsRainbowPublicConfig;\n}\n\nexport interface EnsRainbowApiClientOptions {\n /**\n * The maximum number of `HealResponse` values to cache.\n * Must be a non-negative integer.\n * Setting to 0 will disable caching.\n */\n cacheCapacity: number;\n\n /**\n * The URL of an ENSRainbow API endpoint.\n */\n endpointUrl: URL;\n\n /**\n * Optional client label set preferences that the ENSRainbow server at endpointUrl is expected to\n * support. If provided, enables deterministic heal results across time, such that only\n * labels from label sets with versions less than or equal to this value will be returned.\n * Therefore, even if the ENSRainbow server later ingests label sets with greater versions\n * than this value, the results returned across time can be deterministic. If\n * provided, heal operations with this EnsRainbowApiClient will validate the ENSRainbow\n * server manages a compatible label set. If not provided no specific labelSetId validation\n * will be performed during heal operations.\n * If `labelSetId` is provided without `labelSetVersion`, the server will use the latest\n * available version.\n * If `labelSetVersion` is defined, only labels from sets less than or equal to this value\n * will be returned.\n * When `labelSetVersion` is defined, `labelSetId` must also be defined.\n */\n clientLabelSet?: EnsRainbowClientLabelSet;\n}\n\n/**\n * ENSRainbow API client\n *\n * @example\n * ```typescript\n * // default options\n * const client = new EnsRainbowApiClient();\n * // custom options\n * const client = new EnsRainbowApiClient({\n * endpointUrl: new URL(\"https://api.ensrainbow.io\"),\n * });\n * ```\n */\nexport class EnsRainbowApiClient implements EnsRainbow.ApiClient {\n private readonly options: EnsRainbowApiClientOptions;\n private readonly cache: Cache<LabelHash, EnsRainbow.CacheableHealResponse>;\n private readonly clientLabelSetSearchParams: URLSearchParams;\n\n public static readonly DEFAULT_CACHE_CAPACITY = 1000;\n\n /**\n * Create default client options.\n *\n * @returns default options\n */\n static defaultOptions(): EnsRainbow.ApiClientOptions {\n return {\n endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),\n cacheCapacity: EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,\n clientLabelSet: buildEnsRainbowClientLabelSet(),\n };\n }\n\n constructor(options: Partial<EnsRainbow.ApiClientOptions> = {}) {\n const { clientLabelSet: optionsClientLabelSet, ...rest } = options;\n const defaultOptions = EnsRainbowApiClient.defaultOptions();\n\n const copiedLabelSet = buildEnsRainbowClientLabelSet(\n optionsClientLabelSet?.labelSetId,\n optionsClientLabelSet?.labelSetVersion,\n );\n\n this.options = {\n ...defaultOptions,\n ...rest,\n clientLabelSet: copiedLabelSet,\n };\n\n this.cache = new LruCache<LabelHash, EnsRainbow.CacheableHealResponse>(\n this.options.cacheCapacity,\n );\n\n // Pre-compute query parameters for label set options\n this.clientLabelSetSearchParams = new URLSearchParams();\n if (this.options.clientLabelSet?.labelSetId !== undefined) {\n this.clientLabelSetSearchParams.append(\n \"label_set_id\",\n this.options.clientLabelSet.labelSetId,\n );\n }\n if (this.options.clientLabelSet?.labelSetVersion !== undefined) {\n this.clientLabelSetSearchParams.append(\n \"label_set_version\",\n this.options.clientLabelSet.labelSetVersion.toString(),\n );\n }\n }\n\n /**\n * Attempt to [heal](https://ensnode.io/ensrainbow/concepts/glossary#heal) a labelHash to its original label.\n *\n * Note on returned labels: ENSRainbow returns labels exactly as they are\n * represented in source rainbow table data. This means:\n *\n * - Labels may or may not be ENS-normalized\n * - Labels can contain any valid string, including dots, null bytes, or be empty\n * - Clients should handle all possible string values appropriately\n *\n * @param labelHash - A labelHash to heal, either as a strict `LabelHash`, an `EncodedLabelHash`\n * (bracket-enclosed), or any string that can be normalized (missing `0x` prefix, uppercase hex\n * chars, or 63-char hex are all accepted and normalized automatically).\n * @returns a `HealResponse` indicating the result of the request and the healed label if successful.\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n * ```typescript\n * const response = await client.heal(\n * \"0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc\"\n * );\n *\n * console.log(response);\n *\n * // Output:\n * // {\n * // status: \"success\",\n * // label: \"vitalik\"\n * // }\n *\n * const notFoundResponse = await client.heal(\n * \"0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264\"\n * );\n *\n * console.log(notFoundResponse);\n *\n * // Output:\n * // {\n * // status: \"error\",\n * // error: \"Label not found\",\n * // errorCode: 404\n * // }\n * ```\n */\n async heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<EnsRainbow.HealResponse> {\n let normalizedLabelHash: LabelHash;\n\n try {\n normalizedLabelHash = parseLabelHashOrEncodedLabelHash(labelHash);\n } catch (error) {\n return {\n status: StatusCode.Error,\n error: error instanceof Error ? error.message : String(error),\n errorCode: ErrorCode.BadRequest,\n } as EnsRainbow.HealBadRequestError;\n }\n\n const cachedResult = this.cache.get(normalizedLabelHash);\n if (cachedResult) return cachedResult;\n\n const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);\n\n // Apply pre-computed label set query parameters\n this.clientLabelSetSearchParams.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n const response = await fetch(url);\n const healResponse = (await response.json()) as EnsRainbow.HealResponse;\n\n if (isCacheableHealResponse(healResponse)) {\n this.cache.set(normalizedLabelHash, healResponse);\n }\n\n return healResponse;\n }\n\n /**\n * Get Count of Healable Labels\n *\n * @returns a `CountResponse` indicating the result and the timestamp of the request and the\n * number of healable labels if successful\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n *\n * const response = await client.count();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"success\",\n * // \"count\": 133856894,\n * // \"timestamp\": \"2024-01-30T11:18:56Z\"\n * // }\n *\n */\n async count(): Promise<EnsRainbow.CountResponse> {\n const response = await fetch(new URL(\"/v1/labels/count\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.CountResponse>;\n }\n\n /**\n *\n * Simple verification that the service is running, either in your local setup or for the\n * provided hosted instance.\n * @returns a status of ENS Rainbow service\n * @example\n *\n * const response = await client.health();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"ok\",\n * // }\n */\n async health(): Promise<EnsRainbow.HealthResponse> {\n const response = await fetch(new URL(\"/health\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new EnsRainbowHttpError(\n `ENSRainbow health check failed (HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n })`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.HealthResponse>;\n }\n\n /**\n * Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).\n *\n * Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon\n * as the HTTP server is accepting requests, `ready()` only resolves once the service has finished\n * bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should\n * poll this method instead of `health()` during startup.\n *\n * @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown\n * error carries the HTTP `status` so callers can distinguish the retryable bootstrap case\n * (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures\n * (e.g. `404`, `500`) and abort retries early in the latter cases.\n * @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error\n * type and should generally remain retryable, since they are common during cold start before\n * the ENSRainbow HTTP server has bound its port.\n */\n async ready(): Promise<EnsRainbow.ReadyResponse> {\n const response = await fetch(new URL(\"/ready\", this.options.endpointUrl));\n\n if (!response.ok) {\n const statusSuffix = `HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n }`;\n\n if (response.status === 503) {\n throw new EnsRainbowHttpError(\n `ENSRainbow readiness check: service not ready yet (${statusSuffix})`,\n response.status,\n response.statusText,\n );\n }\n\n throw new EnsRainbowHttpError(\n `ENSRainbow readiness check failed (${statusSuffix}). This usually indicates a non-readiness issue (e.g. wrong base URL, misrouting, or a server error).`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.ReadyResponse>;\n }\n\n /**\n * Get the public configuration of the ENSRainbow service.\n *\n * @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.\n */\n async config(): Promise<EnsRainbow.ENSRainbowPublicConfig> {\n const response = await fetch(new URL(\"/v1/config\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new EnsRainbowHttpError(\n `Failed to fetch ENSRainbow config: HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n }`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.ENSRainbowPublicConfig>;\n }\n\n /**\n * Get a copy of the current client options.\n *\n * @returns a copy of the current client options.\n */\n getOptions(): Readonly<EnsRainbowApiClientOptions> {\n // build a deep copy to prevent modification\n const deepCopy = {\n cacheCapacity: this.options.cacheCapacity,\n endpointUrl: new URL(this.options.endpointUrl.href),\n clientLabelSet: this.options.clientLabelSet ? { ...this.options.clientLabelSet } : undefined,\n } satisfies EnsRainbowApiClientOptions;\n\n return Object.freeze(deepCopy);\n }\n}\n\n/**\n * Determine if a heal response is an error.\n *\n * @param response the heal response to check\n * @returns true if the response is an error, false otherwise\n */\nexport const isHealError = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.HealError => {\n return response.status === StatusCode.Error;\n};\n\n/**\n * Determine if a heal response is cacheable.\n *\n * Server errors at not cachable and should be retried.\n *\n * @param response the heal response to check\n * @returns true if the response is cacheable, false otherwise\n */\nexport const isCacheableHealResponse = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.CacheableHealResponse => {\n if (response.status === StatusCode.Success) return true;\n return (\n response.errorCode !== ErrorCode.ServerError &&\n response.errorCode !== ErrorCode.ServiceUnavailable\n );\n};\n","export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n ServiceUnavailable: 503,\n} as const;\n"],"mappings":"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","names":[]}
|
package/dist/consts.d.ts
CHANGED
package/dist/consts.js
CHANGED
package/dist/consts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/consts.ts"],"sourcesContent":["export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n} as const;\n"],"mappings":";AAAO,IAAM,yBAAyB;AAE/B,IAAM,aAAa;AAAA,EACxB,SAAS;AAAA,EACT,OAAO;AACT;AAEO,IAAM,YAAY;AAAA,EACvB,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,aAAa;
|
|
1
|
+
{"version":3,"sources":["../src/consts.ts"],"sourcesContent":["export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n ServiceUnavailable: 503,\n} as const;\n"],"mappings":";AAAO,IAAM,yBAAyB;AAE/B,IAAM,aAAa;AAAA,EACxB,SAAS;AAAA,EACT,OAAO;AACT;AAEO,IAAM,YAAY;AAAA,EACvB,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,aAAa;AAAA,EACb,oBAAoB;AACtB;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { EnsRainbowClientLabelSet, EnsRainbowServerLabelSet, LabelSetId, LabelSetVersion, buildEnsRainbowClientLabelSet } from '@ensnode/ensnode-sdk';
|
|
2
|
-
export { EnsRainbow, EnsRainbowApiClient, EnsRainbowApiClientOptions, isCacheableHealResponse, isHealError } from './client.js';
|
|
2
|
+
export { EnsRainbow, EnsRainbowApiClient, EnsRainbowApiClientOptions, EnsRainbowHttpError, isCacheableHealResponse, isHealError } from './client.js';
|
|
3
3
|
export { DEFAULT_ENSRAINBOW_URL, ErrorCode, StatusCode } from './consts.js';
|
|
4
4
|
import 'enssdk';
|
package/dist/index.js
CHANGED
|
@@ -17,14 +17,31 @@ var StatusCode = {
|
|
|
17
17
|
var ErrorCode = {
|
|
18
18
|
BadRequest: 400,
|
|
19
19
|
NotFound: 404,
|
|
20
|
-
ServerError: 500
|
|
20
|
+
ServerError: 500,
|
|
21
|
+
ServiceUnavailable: 503
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
// src/client.ts
|
|
25
|
+
var EnsRainbowHttpError = class extends Error {
|
|
26
|
+
name = "EnsRainbowHttpError";
|
|
27
|
+
/**
|
|
28
|
+
* The HTTP status code returned by the ENSRainbow service.
|
|
29
|
+
*/
|
|
30
|
+
status;
|
|
31
|
+
/**
|
|
32
|
+
* The HTTP status text returned by the ENSRainbow service, if any.
|
|
33
|
+
*/
|
|
34
|
+
statusText;
|
|
35
|
+
constructor(message, status, statusText = "") {
|
|
36
|
+
super(message);
|
|
37
|
+
this.status = status;
|
|
38
|
+
this.statusText = statusText;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
24
41
|
var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
25
42
|
options;
|
|
26
43
|
cache;
|
|
27
|
-
|
|
44
|
+
clientLabelSetSearchParams;
|
|
28
45
|
static DEFAULT_CACHE_CAPACITY = 1e3;
|
|
29
46
|
/**
|
|
30
47
|
* Create default client options.
|
|
@@ -35,32 +52,35 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
35
52
|
return {
|
|
36
53
|
endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),
|
|
37
54
|
cacheCapacity: _EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,
|
|
38
|
-
|
|
55
|
+
clientLabelSet: buildEnsRainbowClientLabelSet()
|
|
39
56
|
};
|
|
40
57
|
}
|
|
41
58
|
constructor(options = {}) {
|
|
42
|
-
const {
|
|
59
|
+
const { clientLabelSet: optionsClientLabelSet, ...rest } = options;
|
|
43
60
|
const defaultOptions = _EnsRainbowApiClient.defaultOptions();
|
|
44
61
|
const copiedLabelSet = buildEnsRainbowClientLabelSet(
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
optionsClientLabelSet?.labelSetId,
|
|
63
|
+
optionsClientLabelSet?.labelSetVersion
|
|
47
64
|
);
|
|
48
65
|
this.options = {
|
|
49
66
|
...defaultOptions,
|
|
50
67
|
...rest,
|
|
51
|
-
|
|
68
|
+
clientLabelSet: copiedLabelSet
|
|
52
69
|
};
|
|
53
70
|
this.cache = new LruCache(
|
|
54
71
|
this.options.cacheCapacity
|
|
55
72
|
);
|
|
56
|
-
this.
|
|
57
|
-
if (this.options.
|
|
58
|
-
this.
|
|
73
|
+
this.clientLabelSetSearchParams = new URLSearchParams();
|
|
74
|
+
if (this.options.clientLabelSet?.labelSetId !== void 0) {
|
|
75
|
+
this.clientLabelSetSearchParams.append(
|
|
76
|
+
"label_set_id",
|
|
77
|
+
this.options.clientLabelSet.labelSetId
|
|
78
|
+
);
|
|
59
79
|
}
|
|
60
|
-
if (this.options.
|
|
61
|
-
this.
|
|
80
|
+
if (this.options.clientLabelSet?.labelSetVersion !== void 0) {
|
|
81
|
+
this.clientLabelSetSearchParams.append(
|
|
62
82
|
"label_set_version",
|
|
63
|
-
this.options.
|
|
83
|
+
this.options.clientLabelSet.labelSetVersion.toString()
|
|
64
84
|
);
|
|
65
85
|
}
|
|
66
86
|
}
|
|
@@ -123,7 +143,7 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
123
143
|
const cachedResult = this.cache.get(normalizedLabelHash);
|
|
124
144
|
if (cachedResult) return cachedResult;
|
|
125
145
|
const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);
|
|
126
|
-
this.
|
|
146
|
+
this.clientLabelSetSearchParams.forEach((value, key) => {
|
|
127
147
|
url.searchParams.append(key, value);
|
|
128
148
|
});
|
|
129
149
|
const response = await fetch(url);
|
|
@@ -174,15 +194,63 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
174
194
|
*/
|
|
175
195
|
async health() {
|
|
176
196
|
const response = await fetch(new URL("/health", this.options.endpointUrl));
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new EnsRainbowHttpError(
|
|
199
|
+
`ENSRainbow health check failed (HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""})`,
|
|
200
|
+
response.status,
|
|
201
|
+
response.statusText
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return response.json();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).
|
|
208
|
+
*
|
|
209
|
+
* Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon
|
|
210
|
+
* as the HTTP server is accepting requests, `ready()` only resolves once the service has finished
|
|
211
|
+
* bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should
|
|
212
|
+
* poll this method instead of `health()` during startup.
|
|
213
|
+
*
|
|
214
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown
|
|
215
|
+
* error carries the HTTP `status` so callers can distinguish the retryable bootstrap case
|
|
216
|
+
* (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures
|
|
217
|
+
* (e.g. `404`, `500`) and abort retries early in the latter cases.
|
|
218
|
+
* @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error
|
|
219
|
+
* type and should generally remain retryable, since they are common during cold start before
|
|
220
|
+
* the ENSRainbow HTTP server has bound its port.
|
|
221
|
+
*/
|
|
222
|
+
async ready() {
|
|
223
|
+
const response = await fetch(new URL("/ready", this.options.endpointUrl));
|
|
224
|
+
if (!response.ok) {
|
|
225
|
+
const statusSuffix = `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
226
|
+
if (response.status === 503) {
|
|
227
|
+
throw new EnsRainbowHttpError(
|
|
228
|
+
`ENSRainbow readiness check: service not ready yet (${statusSuffix})`,
|
|
229
|
+
response.status,
|
|
230
|
+
response.statusText
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
throw new EnsRainbowHttpError(
|
|
234
|
+
`ENSRainbow readiness check failed (${statusSuffix}). This usually indicates a non-readiness issue (e.g. wrong base URL, misrouting, or a server error).`,
|
|
235
|
+
response.status,
|
|
236
|
+
response.statusText
|
|
237
|
+
);
|
|
238
|
+
}
|
|
177
239
|
return response.json();
|
|
178
240
|
}
|
|
179
241
|
/**
|
|
180
242
|
* Get the public configuration of the ENSRainbow service.
|
|
243
|
+
*
|
|
244
|
+
* @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.
|
|
181
245
|
*/
|
|
182
246
|
async config() {
|
|
183
247
|
const response = await fetch(new URL("/v1/config", this.options.endpointUrl));
|
|
184
248
|
if (!response.ok) {
|
|
185
|
-
throw new
|
|
249
|
+
throw new EnsRainbowHttpError(
|
|
250
|
+
`Failed to fetch ENSRainbow config: HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`,
|
|
251
|
+
response.status,
|
|
252
|
+
response.statusText
|
|
253
|
+
);
|
|
186
254
|
}
|
|
187
255
|
return response.json();
|
|
188
256
|
}
|
|
@@ -195,7 +263,7 @@ var EnsRainbowApiClient = class _EnsRainbowApiClient {
|
|
|
195
263
|
const deepCopy = {
|
|
196
264
|
cacheCapacity: this.options.cacheCapacity,
|
|
197
265
|
endpointUrl: new URL(this.options.endpointUrl.href),
|
|
198
|
-
|
|
266
|
+
clientLabelSet: this.options.clientLabelSet ? { ...this.options.clientLabelSet } : void 0
|
|
199
267
|
};
|
|
200
268
|
return Object.freeze(deepCopy);
|
|
201
269
|
}
|
|
@@ -204,11 +272,13 @@ var isHealError = (response) => {
|
|
|
204
272
|
return response.status === StatusCode.Error;
|
|
205
273
|
};
|
|
206
274
|
var isCacheableHealResponse = (response) => {
|
|
207
|
-
|
|
275
|
+
if (response.status === StatusCode.Success) return true;
|
|
276
|
+
return response.errorCode !== ErrorCode.ServerError && response.errorCode !== ErrorCode.ServiceUnavailable;
|
|
208
277
|
};
|
|
209
278
|
export {
|
|
210
279
|
DEFAULT_ENSRAINBOW_URL,
|
|
211
280
|
EnsRainbowApiClient,
|
|
281
|
+
EnsRainbowHttpError,
|
|
212
282
|
ErrorCode,
|
|
213
283
|
StatusCode,
|
|
214
284
|
buildEnsRainbowClientLabelSet2 as buildEnsRainbowClientLabelSet,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/consts.ts"],"sourcesContent":["// Re-export types from ensnode-sdk that are needed by consumers\nexport type {\n EnsRainbowClientLabelSet,\n EnsRainbowServerLabelSet,\n LabelSetId,\n LabelSetVersion,\n} from \"@ensnode/ensnode-sdk\";\n// Re-export utility functions and classes from ensnode-sdk that are needed by consumers\nexport { buildEnsRainbowClientLabelSet } from \"@ensnode/ensnode-sdk\";\n\nexport * from \"./client\";\nexport * from \"./consts\";\n","import type { EncodedLabelHash, Label, LabelHash } from \"enssdk\";\nimport { parseLabelHashOrEncodedLabelHash } from \"enssdk\";\n\nimport {\n buildEnsRainbowClientLabelSet,\n type Cache,\n type EnsRainbowClientLabelSet,\n type EnsRainbowPublicConfig,\n LruCache,\n} from \"@ensnode/ensnode-sdk\";\n\nimport { DEFAULT_ENSRAINBOW_URL, ErrorCode, StatusCode } from \"./consts\";\n\nexport namespace EnsRainbow {\n export type ApiClientOptions = EnsRainbowApiClientOptions;\n\n export interface ApiClient {\n count(): Promise<CountResponse>;\n\n /**\n * Get the public configuration of the ENSRainbow service\n */\n config(): Promise<ENSRainbowPublicConfig>;\n\n /**\n * Heal a labelHash to its original label.\n * Accepts a strict `LabelHash`, an `EncodedLabelHash` (bracket-enclosed), or any string\n * that can be normalized (missing `0x` prefix, uppercase hex chars, or 63-char hex).\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n */\n heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;\n\n health(): Promise<HealthResponse>;\n\n getOptions(): Readonly<EnsRainbowApiClientOptions>;\n }\n\n type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];\n\n type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n export interface HealthResponse {\n status: \"ok\";\n }\n\n export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n label?: Label | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface HealSuccess extends BaseHealResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n label: Label;\n error?: never;\n errorCode?: never;\n }\n\n export interface HealNotFoundError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.NotFound> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.NotFound;\n }\n\n export interface HealServerError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface HealBadRequestError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.BadRequest> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.BadRequest;\n }\n\n export type HealResponse =\n | HealSuccess\n | HealNotFoundError\n | HealServerError\n | HealBadRequestError;\n export type HealError = Exclude<HealResponse, HealSuccess>;\n\n /**\n * Server errors should not be cached.\n */\n export type CacheableHealResponse = Exclude<HealResponse, HealServerError>;\n\n export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n count?: number | never;\n timestamp?: string | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface CountSuccess extends BaseCountResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n /** The total count of labels that can be healed by the ENSRainbow instance. Always a\n * non-negative integer. */\n count: number;\n timestamp: string;\n error?: never;\n errorCode?: never;\n }\n\n export interface CountServerError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export type CountResponse = CountSuccess | CountServerError;\n\n /**\n * Complete public configuration object for ENSRainbow.\n *\n * Contains all public configuration information about the ENSRainbow service instance,\n * including version, label set information, and record counts.\n */\n export type ENSRainbowPublicConfig = EnsRainbowPublicConfig;\n}\n\nexport interface EnsRainbowApiClientOptions {\n /**\n * The maximum number of `HealResponse` values to cache.\n * Must be a non-negative integer.\n * Setting to 0 will disable caching.\n */\n cacheCapacity: number;\n\n /**\n * The URL of an ENSRainbow API endpoint.\n */\n endpointUrl: URL;\n\n /**\n * Optional label set preferences that the ENSRainbow server at endpointUrl is expected to\n * support. If provided, enables deterministic heal results across time, such that only\n * labels from label sets with versions less than or equal to this value will be returned.\n * Therefore, even if the ENSRainbow server later ingests label sets with greater versions\n * than this value, the results returned across time can be deterministic. If\n * provided, heal operations with this EnsRainbowApiClient will validate the ENSRainbow\n * server manages a compatible label set. If not provided no specific labelSetId validation\n * will be performed during heal operations.\n * If `labelSetId` is provided without `labelSetVersion`, the server will use the latest\n * available version.\n * If `labelSetVersion` is defined, only labels from sets less than or equal to this value\n * will be returned.\n * When `labelSetVersion` is defined, `labelSetId` must also be defined.\n */\n labelSet?: EnsRainbowClientLabelSet;\n}\n\n/**\n * ENSRainbow API client\n *\n * @example\n * ```typescript\n * // default options\n * const client = new EnsRainbowApiClient();\n * // custom options\n * const client = new EnsRainbowApiClient({\n * endpointUrl: new URL(\"https://api.ensrainbow.io\"),\n * });\n * ```\n */\nexport class EnsRainbowApiClient implements EnsRainbow.ApiClient {\n private readonly options: EnsRainbowApiClientOptions;\n private readonly cache: Cache<LabelHash, EnsRainbow.CacheableHealResponse>;\n private readonly labelSetSearchParams: URLSearchParams;\n\n public static readonly DEFAULT_CACHE_CAPACITY = 1000;\n\n /**\n * Create default client options.\n *\n * @returns default options\n */\n static defaultOptions(): EnsRainbow.ApiClientOptions {\n return {\n endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),\n cacheCapacity: EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,\n labelSet: buildEnsRainbowClientLabelSet(),\n };\n }\n\n constructor(options: Partial<EnsRainbow.ApiClientOptions> = {}) {\n const { labelSet: optionsLabelSet, ...rest } = options;\n const defaultOptions = EnsRainbowApiClient.defaultOptions();\n\n const copiedLabelSet = buildEnsRainbowClientLabelSet(\n optionsLabelSet?.labelSetId,\n optionsLabelSet?.labelSetVersion,\n );\n\n this.options = {\n ...defaultOptions,\n ...rest,\n labelSet: copiedLabelSet,\n };\n\n this.cache = new LruCache<LabelHash, EnsRainbow.CacheableHealResponse>(\n this.options.cacheCapacity,\n );\n\n // Pre-compute query parameters for label set options\n this.labelSetSearchParams = new URLSearchParams();\n if (this.options.labelSet?.labelSetId !== undefined) {\n this.labelSetSearchParams.append(\"label_set_id\", this.options.labelSet.labelSetId);\n }\n if (this.options.labelSet?.labelSetVersion !== undefined) {\n this.labelSetSearchParams.append(\n \"label_set_version\",\n this.options.labelSet.labelSetVersion.toString(),\n );\n }\n }\n\n /**\n * Attempt to [heal](https://ensnode.io/ensrainbow/concepts/glossary#heal) a labelHash to its original label.\n *\n * Note on returned labels: ENSRainbow returns labels exactly as they are\n * represented in source rainbow table data. This means:\n *\n * - Labels may or may not be ENS-normalized\n * - Labels can contain any valid string, including dots, null bytes, or be empty\n * - Clients should handle all possible string values appropriately\n *\n * @param labelHash - A labelHash to heal, either as a strict `LabelHash`, an `EncodedLabelHash`\n * (bracket-enclosed), or any string that can be normalized (missing `0x` prefix, uppercase hex\n * chars, or 63-char hex are all accepted and normalized automatically).\n * @returns a `HealResponse` indicating the result of the request and the healed label if successful.\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n * ```typescript\n * const response = await client.heal(\n * \"0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc\"\n * );\n *\n * console.log(response);\n *\n * // Output:\n * // {\n * // status: \"success\",\n * // label: \"vitalik\"\n * // }\n *\n * const notFoundResponse = await client.heal(\n * \"0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264\"\n * );\n *\n * console.log(notFoundResponse);\n *\n * // Output:\n * // {\n * // status: \"error\",\n * // error: \"Label not found\",\n * // errorCode: 404\n * // }\n * ```\n */\n async heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<EnsRainbow.HealResponse> {\n let normalizedLabelHash: LabelHash;\n\n try {\n normalizedLabelHash = parseLabelHashOrEncodedLabelHash(labelHash);\n } catch (error) {\n return {\n status: StatusCode.Error,\n error: error instanceof Error ? error.message : String(error),\n errorCode: ErrorCode.BadRequest,\n } as EnsRainbow.HealBadRequestError;\n }\n\n const cachedResult = this.cache.get(normalizedLabelHash);\n if (cachedResult) return cachedResult;\n\n const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);\n\n // Apply pre-computed label set query parameters\n this.labelSetSearchParams.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n const response = await fetch(url);\n const healResponse = (await response.json()) as EnsRainbow.HealResponse;\n\n if (isCacheableHealResponse(healResponse)) {\n this.cache.set(normalizedLabelHash, healResponse);\n }\n\n return healResponse;\n }\n\n /**\n * Get Count of Healable Labels\n *\n * @returns a `CountResponse` indicating the result and the timestamp of the request and the\n * number of healable labels if successful\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n *\n * const response = await client.count();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"success\",\n * // \"count\": 133856894,\n * // \"timestamp\": \"2024-01-30T11:18:56Z\"\n * // }\n *\n */\n async count(): Promise<EnsRainbow.CountResponse> {\n const response = await fetch(new URL(\"/v1/labels/count\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.CountResponse>;\n }\n\n /**\n *\n * Simple verification that the service is running, either in your local setup or for the\n * provided hosted instance.\n * @returns a status of ENS Rainbow service\n * @example\n *\n * const response = await client.health();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"ok\",\n * // }\n */\n async health(): Promise<EnsRainbow.HealthResponse> {\n const response = await fetch(new URL(\"/health\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.HealthResponse>;\n }\n\n /**\n * Get the public configuration of the ENSRainbow service.\n */\n async config(): Promise<EnsRainbow.ENSRainbowPublicConfig> {\n const response = await fetch(new URL(\"/v1/config\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ENSRainbow config: ${response.statusText}`);\n }\n\n return response.json() as Promise<EnsRainbow.ENSRainbowPublicConfig>;\n }\n\n /**\n * Get a copy of the current client options.\n *\n * @returns a copy of the current client options.\n */\n getOptions(): Readonly<EnsRainbowApiClientOptions> {\n // build a deep copy to prevent modification\n const deepCopy = {\n cacheCapacity: this.options.cacheCapacity,\n endpointUrl: new URL(this.options.endpointUrl.href),\n labelSet: this.options.labelSet ? { ...this.options.labelSet } : undefined,\n } satisfies EnsRainbowApiClientOptions;\n\n return Object.freeze(deepCopy);\n }\n}\n\n/**\n * Determine if a heal response is an error.\n *\n * @param response the heal response to check\n * @returns true if the response is an error, false otherwise\n */\nexport const isHealError = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.HealError => {\n return response.status === StatusCode.Error;\n};\n\n/**\n * Determine if a heal response is cacheable.\n *\n * Server errors at not cachable and should be retried.\n *\n * @param response the heal response to check\n * @returns true if the response is cacheable, false otherwise\n */\nexport const isCacheableHealResponse = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.CacheableHealResponse => {\n return response.status === StatusCode.Success || response.errorCode !== ErrorCode.ServerError;\n};\n","export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n} as const;\n"],"mappings":"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","names":["buildEnsRainbowClientLabelSet"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/consts.ts"],"sourcesContent":["// Re-export types from ensnode-sdk that are needed by consumers\nexport type {\n EnsRainbowClientLabelSet,\n EnsRainbowServerLabelSet,\n LabelSetId,\n LabelSetVersion,\n} from \"@ensnode/ensnode-sdk\";\n// Re-export utility functions and classes from ensnode-sdk that are needed by consumers\nexport { buildEnsRainbowClientLabelSet } from \"@ensnode/ensnode-sdk\";\n\nexport * from \"./client\";\nexport * from \"./consts\";\n","import type { EncodedLabelHash, Label, LabelHash } from \"enssdk\";\nimport { parseLabelHashOrEncodedLabelHash } from \"enssdk\";\n\nimport {\n buildEnsRainbowClientLabelSet,\n type Cache,\n type EnsRainbowClientLabelSet,\n type EnsRainbowPublicConfig,\n LruCache,\n} from \"@ensnode/ensnode-sdk\";\n\nimport { DEFAULT_ENSRAINBOW_URL, ErrorCode, StatusCode } from \"./consts\";\n\n/**\n * Error thrown by {@link EnsRainbowApiClient} methods when the ENSRainbow service responds\n * with a non-2xx HTTP status code.\n *\n * Carries the HTTP status code as a structured property (rather than only embedding it in the\n * error message) so callers can branch their retry/abort logic on the status — e.g. retry on\n * `503 Service Unavailable` while ENSRainbow bootstraps, but abort immediately on `404`/`500`,\n * which usually indicate a misconfigured base URL or a hard server failure.\n *\n * Network-level failures (DNS, ECONNREFUSED, fetch parse errors) are *not* wrapped in this\n * class — they propagate as their original `Error` (typically a `TypeError` from `fetch`),\n * because such failures are commonly transient during cold start and should remain retryable\n * by callers.\n */\nexport class EnsRainbowHttpError extends Error {\n readonly name = \"EnsRainbowHttpError\";\n\n /**\n * The HTTP status code returned by the ENSRainbow service.\n */\n readonly status: number;\n\n /**\n * The HTTP status text returned by the ENSRainbow service, if any.\n */\n readonly statusText: string;\n\n constructor(message: string, status: number, statusText = \"\") {\n super(message);\n this.status = status;\n this.statusText = statusText;\n }\n}\n\nexport namespace EnsRainbow {\n export type ApiClientOptions = EnsRainbowApiClientOptions;\n\n export interface ApiClient {\n count(): Promise<CountResponse>;\n\n /**\n * Get the public configuration of the ENSRainbow service\n */\n config(): Promise<ENSRainbowPublicConfig>;\n\n /**\n * Heal a labelHash to its original label.\n * Accepts a strict `LabelHash`, an `EncodedLabelHash` (bracket-enclosed), or any string\n * that can be normalized (missing `0x` prefix, uppercase hex chars, or 63-char hex).\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n */\n heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<HealResponse>;\n\n health(): Promise<HealthResponse>;\n\n /**\n * Check whether the ENSRainbow service has finished bootstrapping and is ready to serve requests.\n *\n * Throws when the service is not ready (e.g. 503 while the database is still being downloaded\n * or validated) so callers can retry.\n */\n ready(): Promise<ReadyResponse>;\n\n getOptions(): Readonly<EnsRainbowApiClientOptions>;\n }\n\n type StatusCode = (typeof StatusCode)[keyof typeof StatusCode];\n\n type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\n export interface HealthResponse {\n status: \"ok\";\n }\n\n /**\n * Response returned by `GET /ready` when the ENSRainbow service is ready to serve requests.\n */\n export interface ReadyResponse {\n status: \"ok\";\n }\n\n /**\n * Generic error shape used by endpoints that return 503 Service Unavailable while the\n * database is still bootstrapping (downloading, extracting, or validating).\n */\n export interface ServiceUnavailableError {\n status: typeof StatusCode.Error;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export interface BaseHealResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n label?: Label | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface HealSuccess extends BaseHealResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n label: Label;\n error?: never;\n errorCode?: never;\n }\n\n export interface HealNotFoundError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.NotFound> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.NotFound;\n }\n\n export interface HealServerError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface HealBadRequestError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.BadRequest> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.BadRequest;\n }\n\n export interface HealServiceUnavailableError\n extends BaseHealResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {\n status: typeof StatusCode.Error;\n label?: never;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export type HealResponse =\n | HealSuccess\n | HealNotFoundError\n | HealServerError\n | HealBadRequestError\n | HealServiceUnavailableError;\n export type HealError = Exclude<HealResponse, HealSuccess>;\n\n /**\n * Server errors and transient bootstrap errors should not be cached.\n */\n export type CacheableHealResponse = Exclude<\n HealResponse,\n HealServerError | HealServiceUnavailableError\n >;\n\n export interface BaseCountResponse<Status extends StatusCode, Error extends ErrorCode> {\n status: Status;\n count?: number | never;\n timestamp?: string | never;\n error?: string | never;\n errorCode?: Error | never;\n }\n\n export interface CountSuccess extends BaseCountResponse<typeof StatusCode.Success, never> {\n status: typeof StatusCode.Success;\n /** The total count of labels that can be healed by the ENSRainbow instance. Always a\n * non-negative integer. */\n count: number;\n timestamp: string;\n error?: never;\n errorCode?: never;\n }\n\n export interface CountServerError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServerError> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServerError;\n }\n\n export interface CountServiceUnavailableError\n extends BaseCountResponse<typeof StatusCode.Error, typeof ErrorCode.ServiceUnavailable> {\n status: typeof StatusCode.Error;\n count?: never;\n timestamp?: never;\n error: string;\n errorCode: typeof ErrorCode.ServiceUnavailable;\n }\n\n export type CountResponse = CountSuccess | CountServerError | CountServiceUnavailableError;\n\n /**\n * Complete public configuration object for ENSRainbow.\n *\n * Contains all public configuration information about the ENSRainbow service instance,\n * including version, label set information, and record counts.\n */\n export type ENSRainbowPublicConfig = EnsRainbowPublicConfig;\n}\n\nexport interface EnsRainbowApiClientOptions {\n /**\n * The maximum number of `HealResponse` values to cache.\n * Must be a non-negative integer.\n * Setting to 0 will disable caching.\n */\n cacheCapacity: number;\n\n /**\n * The URL of an ENSRainbow API endpoint.\n */\n endpointUrl: URL;\n\n /**\n * Optional client label set preferences that the ENSRainbow server at endpointUrl is expected to\n * support. If provided, enables deterministic heal results across time, such that only\n * labels from label sets with versions less than or equal to this value will be returned.\n * Therefore, even if the ENSRainbow server later ingests label sets with greater versions\n * than this value, the results returned across time can be deterministic. If\n * provided, heal operations with this EnsRainbowApiClient will validate the ENSRainbow\n * server manages a compatible label set. If not provided no specific labelSetId validation\n * will be performed during heal operations.\n * If `labelSetId` is provided without `labelSetVersion`, the server will use the latest\n * available version.\n * If `labelSetVersion` is defined, only labels from sets less than or equal to this value\n * will be returned.\n * When `labelSetVersion` is defined, `labelSetId` must also be defined.\n */\n clientLabelSet?: EnsRainbowClientLabelSet;\n}\n\n/**\n * ENSRainbow API client\n *\n * @example\n * ```typescript\n * // default options\n * const client = new EnsRainbowApiClient();\n * // custom options\n * const client = new EnsRainbowApiClient({\n * endpointUrl: new URL(\"https://api.ensrainbow.io\"),\n * });\n * ```\n */\nexport class EnsRainbowApiClient implements EnsRainbow.ApiClient {\n private readonly options: EnsRainbowApiClientOptions;\n private readonly cache: Cache<LabelHash, EnsRainbow.CacheableHealResponse>;\n private readonly clientLabelSetSearchParams: URLSearchParams;\n\n public static readonly DEFAULT_CACHE_CAPACITY = 1000;\n\n /**\n * Create default client options.\n *\n * @returns default options\n */\n static defaultOptions(): EnsRainbow.ApiClientOptions {\n return {\n endpointUrl: new URL(DEFAULT_ENSRAINBOW_URL),\n cacheCapacity: EnsRainbowApiClient.DEFAULT_CACHE_CAPACITY,\n clientLabelSet: buildEnsRainbowClientLabelSet(),\n };\n }\n\n constructor(options: Partial<EnsRainbow.ApiClientOptions> = {}) {\n const { clientLabelSet: optionsClientLabelSet, ...rest } = options;\n const defaultOptions = EnsRainbowApiClient.defaultOptions();\n\n const copiedLabelSet = buildEnsRainbowClientLabelSet(\n optionsClientLabelSet?.labelSetId,\n optionsClientLabelSet?.labelSetVersion,\n );\n\n this.options = {\n ...defaultOptions,\n ...rest,\n clientLabelSet: copiedLabelSet,\n };\n\n this.cache = new LruCache<LabelHash, EnsRainbow.CacheableHealResponse>(\n this.options.cacheCapacity,\n );\n\n // Pre-compute query parameters for label set options\n this.clientLabelSetSearchParams = new URLSearchParams();\n if (this.options.clientLabelSet?.labelSetId !== undefined) {\n this.clientLabelSetSearchParams.append(\n \"label_set_id\",\n this.options.clientLabelSet.labelSetId,\n );\n }\n if (this.options.clientLabelSet?.labelSetVersion !== undefined) {\n this.clientLabelSetSearchParams.append(\n \"label_set_version\",\n this.options.clientLabelSet.labelSetVersion.toString(),\n );\n }\n }\n\n /**\n * Attempt to [heal](https://ensnode.io/ensrainbow/concepts/glossary#heal) a labelHash to its original label.\n *\n * Note on returned labels: ENSRainbow returns labels exactly as they are\n * represented in source rainbow table data. This means:\n *\n * - Labels may or may not be ENS-normalized\n * - Labels can contain any valid string, including dots, null bytes, or be empty\n * - Clients should handle all possible string values appropriately\n *\n * @param labelHash - A labelHash to heal, either as a strict `LabelHash`, an `EncodedLabelHash`\n * (bracket-enclosed), or any string that can be normalized (missing `0x` prefix, uppercase hex\n * chars, or 63-char hex are all accepted and normalized automatically).\n * @returns a `HealResponse` indicating the result of the request and the healed label if successful.\n * Returns a `HealBadRequestError` if the input cannot be normalized to a valid labelHash.\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n * ```typescript\n * const response = await client.heal(\n * \"0xaf2caa1c2ca1d027f1ac823b529d0a67cd144264b2789fa2ea4d63a67c7103cc\"\n * );\n *\n * console.log(response);\n *\n * // Output:\n * // {\n * // status: \"success\",\n * // label: \"vitalik\"\n * // }\n *\n * const notFoundResponse = await client.heal(\n * \"0xf64dc17ae2e2b9b16dbcb8cb05f35a2e6080a5ff1dc53ac0bc48f0e79111f264\"\n * );\n *\n * console.log(notFoundResponse);\n *\n * // Output:\n * // {\n * // status: \"error\",\n * // error: \"Label not found\",\n * // errorCode: 404\n * // }\n * ```\n */\n async heal(labelHash: LabelHash | EncodedLabelHash | string): Promise<EnsRainbow.HealResponse> {\n let normalizedLabelHash: LabelHash;\n\n try {\n normalizedLabelHash = parseLabelHashOrEncodedLabelHash(labelHash);\n } catch (error) {\n return {\n status: StatusCode.Error,\n error: error instanceof Error ? error.message : String(error),\n errorCode: ErrorCode.BadRequest,\n } as EnsRainbow.HealBadRequestError;\n }\n\n const cachedResult = this.cache.get(normalizedLabelHash);\n if (cachedResult) return cachedResult;\n\n const url = new URL(`/v1/heal/${normalizedLabelHash}`, this.options.endpointUrl);\n\n // Apply pre-computed label set query parameters\n this.clientLabelSetSearchParams.forEach((value, key) => {\n url.searchParams.append(key, value);\n });\n\n const response = await fetch(url);\n const healResponse = (await response.json()) as EnsRainbow.HealResponse;\n\n if (isCacheableHealResponse(healResponse)) {\n this.cache.set(normalizedLabelHash, healResponse);\n }\n\n return healResponse;\n }\n\n /**\n * Get Count of Healable Labels\n *\n * @returns a `CountResponse` indicating the result and the timestamp of the request and the\n * number of healable labels if successful\n * @throws if the request fails due to network failures, DNS lookup failures, request timeouts,\n * CORS violations, or Invalid URLs\n * @example\n *\n * const response = await client.count();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"success\",\n * // \"count\": 133856894,\n * // \"timestamp\": \"2024-01-30T11:18:56Z\"\n * // }\n *\n */\n async count(): Promise<EnsRainbow.CountResponse> {\n const response = await fetch(new URL(\"/v1/labels/count\", this.options.endpointUrl));\n\n return response.json() as Promise<EnsRainbow.CountResponse>;\n }\n\n /**\n *\n * Simple verification that the service is running, either in your local setup or for the\n * provided hosted instance.\n * @returns a status of ENS Rainbow service\n * @example\n *\n * const response = await client.health();\n *\n * console.log(response);\n *\n * // {\n * // \"status\": \"ok\",\n * // }\n */\n async health(): Promise<EnsRainbow.HealthResponse> {\n const response = await fetch(new URL(\"/health\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new EnsRainbowHttpError(\n `ENSRainbow health check failed (HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n })`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.HealthResponse>;\n }\n\n /**\n * Check whether the ENSRainbow service is ready (database is downloaded, validated, and open).\n *\n * Unlike {@link EnsRainbowApiClient.health}, which is a pure liveness probe that succeeds as soon\n * as the HTTP server is accepting requests, `ready()` only resolves once the service has finished\n * bootstrapping its database. Clients that require a usable database (e.g. ENSIndexer) should\n * poll this method instead of `health()` during startup.\n *\n * @throws {EnsRainbowHttpError} if the service responds with a non-2xx status. The thrown\n * error carries the HTTP `status` so callers can distinguish the retryable bootstrap case\n * (`503 Service Unavailable`) from likely-non-retryable misconfiguration / server failures\n * (e.g. `404`, `500`) and abort retries early in the latter cases.\n * @throws Network/fetch errors (DNS, ECONNREFUSED, etc.) propagate as their original error\n * type and should generally remain retryable, since they are common during cold start before\n * the ENSRainbow HTTP server has bound its port.\n */\n async ready(): Promise<EnsRainbow.ReadyResponse> {\n const response = await fetch(new URL(\"/ready\", this.options.endpointUrl));\n\n if (!response.ok) {\n const statusSuffix = `HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n }`;\n\n if (response.status === 503) {\n throw new EnsRainbowHttpError(\n `ENSRainbow readiness check: service not ready yet (${statusSuffix})`,\n response.status,\n response.statusText,\n );\n }\n\n throw new EnsRainbowHttpError(\n `ENSRainbow readiness check failed (${statusSuffix}). This usually indicates a non-readiness issue (e.g. wrong base URL, misrouting, or a server error).`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.ReadyResponse>;\n }\n\n /**\n * Get the public configuration of the ENSRainbow service.\n *\n * @throws {EnsRainbowHttpError} if the service responds with a non-2xx status.\n */\n async config(): Promise<EnsRainbow.ENSRainbowPublicConfig> {\n const response = await fetch(new URL(\"/v1/config\", this.options.endpointUrl));\n\n if (!response.ok) {\n throw new EnsRainbowHttpError(\n `Failed to fetch ENSRainbow config: HTTP ${response.status}${\n response.statusText ? ` ${response.statusText}` : \"\"\n }`,\n response.status,\n response.statusText,\n );\n }\n\n return response.json() as Promise<EnsRainbow.ENSRainbowPublicConfig>;\n }\n\n /**\n * Get a copy of the current client options.\n *\n * @returns a copy of the current client options.\n */\n getOptions(): Readonly<EnsRainbowApiClientOptions> {\n // build a deep copy to prevent modification\n const deepCopy = {\n cacheCapacity: this.options.cacheCapacity,\n endpointUrl: new URL(this.options.endpointUrl.href),\n clientLabelSet: this.options.clientLabelSet ? { ...this.options.clientLabelSet } : undefined,\n } satisfies EnsRainbowApiClientOptions;\n\n return Object.freeze(deepCopy);\n }\n}\n\n/**\n * Determine if a heal response is an error.\n *\n * @param response the heal response to check\n * @returns true if the response is an error, false otherwise\n */\nexport const isHealError = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.HealError => {\n return response.status === StatusCode.Error;\n};\n\n/**\n * Determine if a heal response is cacheable.\n *\n * Server errors at not cachable and should be retried.\n *\n * @param response the heal response to check\n * @returns true if the response is cacheable, false otherwise\n */\nexport const isCacheableHealResponse = (\n response: EnsRainbow.HealResponse,\n): response is EnsRainbow.CacheableHealResponse => {\n if (response.status === StatusCode.Success) return true;\n return (\n response.errorCode !== ErrorCode.ServerError &&\n response.errorCode !== ErrorCode.ServiceUnavailable\n );\n};\n","export const DEFAULT_ENSRAINBOW_URL = \"https://api.ensrainbow.io\" as const;\n\nexport const StatusCode = {\n Success: \"success\",\n Error: \"error\",\n} as const;\n\nexport const ErrorCode = {\n BadRequest: 400,\n NotFound: 404,\n ServerError: 500,\n ServiceUnavailable: 503,\n} as const;\n"],"mappings":"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","names":["buildEnsRainbowClientLabelSet"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ensnode/ensrainbow-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ENSRainbow SDK for interacting with the ENSRainbow API.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
"viem": "^2.22.13"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"enssdk": "1.
|
|
42
|
+
"enssdk": "1.11.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"tsup": "^8.3.6",
|
|
46
46
|
"typescript": "^5.7.3",
|
|
47
47
|
"vitest": "^4.0.2",
|
|
48
|
-
"@ensnode/shared-configs": "1.
|
|
49
|
-
"@ensnode/ensnode-sdk": "1.
|
|
48
|
+
"@ensnode/shared-configs": "1.11.1",
|
|
49
|
+
"@ensnode/ensnode-sdk": "1.11.1"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"prepublish": "tsup",
|