@secondlayer/sdk 6.36.0 → 6.37.0
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/index.d.ts +164 -68
- package/dist/index.js +186 -51
- package/dist/index.js.map +9 -9
- package/dist/streams/index.d.ts +101 -11
- package/dist/streams/index.js +177 -50
- package/dist/streams/index.js.map +8 -8
- package/dist/subgraphs/index.d.ts +38 -0
- package/dist/subgraphs/index.js +184 -51
- package/dist/subgraphs/index.js.map +8 -8
- package/package.json +2 -2
package/dist/subgraphs/index.js
CHANGED
|
@@ -1,17 +1,127 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
|
-
|
|
2
|
+
var DOCS_BASE = "https://www.secondlayer.tools";
|
|
3
|
+
|
|
4
|
+
class SecondLayerError extends Error {
|
|
5
|
+
name = "SecondLayerError";
|
|
6
|
+
shortMessage;
|
|
7
|
+
code;
|
|
8
|
+
docsUrl;
|
|
9
|
+
metaMessages;
|
|
10
|
+
retryable;
|
|
11
|
+
retryAfterSeconds;
|
|
12
|
+
constructor(shortMessage, options = {}) {
|
|
13
|
+
const detailBlocks = [
|
|
14
|
+
...options.metaMessages ?? [],
|
|
15
|
+
...options.details ? [options.details] : [],
|
|
16
|
+
...options.docsUrl ? [`Docs: ${options.docsUrl}`] : []
|
|
17
|
+
];
|
|
18
|
+
const message = [
|
|
19
|
+
shortMessage,
|
|
20
|
+
detailBlocks.length > 0 ? `
|
|
21
|
+
${detailBlocks.join(`
|
|
22
|
+
`)}` : ""
|
|
23
|
+
].join("");
|
|
24
|
+
super(message, options.cause ? { cause: options.cause } : undefined);
|
|
25
|
+
this.shortMessage = shortMessage;
|
|
26
|
+
this.code = options.code;
|
|
27
|
+
this.docsUrl = options.docsUrl;
|
|
28
|
+
this.metaMessages = options.metaMessages;
|
|
29
|
+
this.retryable = options.retryable ?? false;
|
|
30
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
31
|
+
}
|
|
32
|
+
walk(fn) {
|
|
33
|
+
let err = this;
|
|
34
|
+
let last = this;
|
|
35
|
+
while (err != null) {
|
|
36
|
+
if (fn?.(err))
|
|
37
|
+
return err;
|
|
38
|
+
last = err;
|
|
39
|
+
err = err.cause;
|
|
40
|
+
}
|
|
41
|
+
return fn ? null : last;
|
|
42
|
+
}
|
|
43
|
+
toJSON() {
|
|
44
|
+
return {
|
|
45
|
+
name: this.name,
|
|
46
|
+
message: this.message,
|
|
47
|
+
shortMessage: this.shortMessage,
|
|
48
|
+
cause: this.cause instanceof Error ? this.cause.message : undefined,
|
|
49
|
+
code: this.code,
|
|
50
|
+
docsUrl: this.docsUrl,
|
|
51
|
+
retryable: this.retryable,
|
|
52
|
+
retryAfterSeconds: this.retryAfterSeconds
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function statusRetryable(status) {
|
|
57
|
+
return status === 429 || status >= 500;
|
|
58
|
+
}
|
|
59
|
+
function docsFor(status, code) {
|
|
60
|
+
if (code === "UPGRADE_REQUIRED" || code === "PLAN_REQUIRED" || status === 402)
|
|
61
|
+
return `${DOCS_BASE}/docs/authentication#pay-as-you-go-credits`;
|
|
62
|
+
if (status === 429 || status === 401)
|
|
63
|
+
return `${DOCS_BASE}/docs/authentication`;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class ApiError extends SecondLayerError {
|
|
3
68
|
status;
|
|
4
69
|
body;
|
|
5
|
-
code
|
|
6
|
-
|
|
7
|
-
|
|
70
|
+
constructor(status, message, body, code, options = {}) {
|
|
71
|
+
super(message, {
|
|
72
|
+
code,
|
|
73
|
+
retryable: options.retryable ?? statusRetryable(status),
|
|
74
|
+
docsUrl: options.docsUrl ?? docsFor(status, code),
|
|
75
|
+
...options.cause ? { cause: options.cause } : {},
|
|
76
|
+
...options.details ? { details: options.details } : {},
|
|
77
|
+
...options.metaMessages ? { metaMessages: options.metaMessages } : {},
|
|
78
|
+
...options.retryAfterSeconds !== undefined ? { retryAfterSeconds: options.retryAfterSeconds } : {}
|
|
79
|
+
});
|
|
8
80
|
this.status = status;
|
|
9
81
|
this.body = body;
|
|
10
|
-
this.code = code;
|
|
11
82
|
this.name = "ApiError";
|
|
12
83
|
}
|
|
13
84
|
}
|
|
14
85
|
|
|
86
|
+
class AuthError extends ApiError {
|
|
87
|
+
status = 401;
|
|
88
|
+
constructor(message = "API key invalid or expired.") {
|
|
89
|
+
super(401, message);
|
|
90
|
+
this.name = "AuthError";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class RateLimitError extends ApiError {
|
|
95
|
+
retryAfter;
|
|
96
|
+
status = 429;
|
|
97
|
+
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
98
|
+
super(429, message, undefined, undefined, {
|
|
99
|
+
retryable: true,
|
|
100
|
+
retryAfterSeconds: parseRetryAfter(retryAfter)
|
|
101
|
+
});
|
|
102
|
+
this.retryAfter = retryAfter;
|
|
103
|
+
this.name = "RateLimitError";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
class ValidationError extends ApiError {
|
|
108
|
+
constructor(message, status, body) {
|
|
109
|
+
super(status, message, body, undefined, { retryable: false });
|
|
110
|
+
this.name = "ValidationError";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function parseRetryAfter(value) {
|
|
114
|
+
if (!value)
|
|
115
|
+
return;
|
|
116
|
+
const seconds = Number(value);
|
|
117
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
118
|
+
return seconds;
|
|
119
|
+
const date = Date.parse(value);
|
|
120
|
+
if (!Number.isNaN(date))
|
|
121
|
+
return Math.max(0, (date - Date.now()) / 1000);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
15
125
|
class VersionConflictError extends ApiError {
|
|
16
126
|
currentVersion;
|
|
17
127
|
expectedVersion;
|
|
@@ -26,7 +136,13 @@ class VersionConflictError extends ApiError {
|
|
|
26
136
|
class ByoBreakingChangeError extends ApiError {
|
|
27
137
|
details;
|
|
28
138
|
constructor(message, details) {
|
|
29
|
-
super(422, message, details, "BYO_BREAKING_CHANGE"
|
|
139
|
+
super(422, message, details, "BYO_BREAKING_CHANGE", {
|
|
140
|
+
metaMessages: [
|
|
141
|
+
"Review and run manually on your database:",
|
|
142
|
+
details.plan.dropStatement,
|
|
143
|
+
...details.plan.statements
|
|
144
|
+
]
|
|
145
|
+
});
|
|
30
146
|
this.name = "ByoBreakingChangeError";
|
|
31
147
|
this.details = details;
|
|
32
148
|
}
|
|
@@ -112,7 +228,7 @@ class BaseClient {
|
|
|
112
228
|
serializedBody = JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
113
229
|
} catch (err) {
|
|
114
230
|
const detail = err instanceof Error ? err.message : String(err);
|
|
115
|
-
throw new ApiError(0, `Failed to serialize request body: ${detail}
|
|
231
|
+
throw new ApiError(0, `Failed to serialize request body: ${detail}`, undefined, undefined, { retryable: false });
|
|
116
232
|
}
|
|
117
233
|
}
|
|
118
234
|
let response;
|
|
@@ -122,17 +238,17 @@ class BaseClient {
|
|
|
122
238
|
headers,
|
|
123
239
|
body: serializedBody
|
|
124
240
|
});
|
|
125
|
-
} catch {
|
|
126
|
-
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again
|
|
241
|
+
} catch (err) {
|
|
242
|
+
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`, undefined, undefined, { retryable: true, cause: err instanceof Error ? err : undefined });
|
|
127
243
|
}
|
|
128
244
|
if (!response.ok) {
|
|
129
245
|
if (response.status === 401) {
|
|
130
|
-
throw new
|
|
246
|
+
throw new AuthError;
|
|
131
247
|
}
|
|
132
248
|
if (response.status === 429) {
|
|
133
249
|
const retryAfter = response.headers.get("Retry-After");
|
|
134
250
|
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
135
|
-
throw new
|
|
251
|
+
throw new RateLimitError(msg, retryAfter ?? undefined);
|
|
136
252
|
}
|
|
137
253
|
if (response.status >= 500) {
|
|
138
254
|
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
@@ -426,47 +542,14 @@ class Contracts extends BaseClient {
|
|
|
426
542
|
}
|
|
427
543
|
|
|
428
544
|
// src/streams/errors.ts
|
|
429
|
-
class
|
|
430
|
-
status = 401;
|
|
431
|
-
constructor(message = "API key invalid or expired.") {
|
|
432
|
-
super(message);
|
|
433
|
-
this.name = "AuthError";
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
class RateLimitError extends Error {
|
|
438
|
-
retryAfter;
|
|
439
|
-
status = 429;
|
|
440
|
-
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
441
|
-
super(message);
|
|
442
|
-
this.retryAfter = retryAfter;
|
|
443
|
-
this.name = "RateLimitError";
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
class ValidationError extends Error {
|
|
448
|
-
status;
|
|
449
|
-
body;
|
|
450
|
-
constructor(message, status, body) {
|
|
451
|
-
super(message);
|
|
452
|
-
this.status = status;
|
|
453
|
-
this.body = body;
|
|
454
|
-
this.name = "ValidationError";
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
class StreamsServerError extends Error {
|
|
459
|
-
status;
|
|
460
|
-
body;
|
|
545
|
+
class StreamsServerError extends ApiError {
|
|
461
546
|
constructor(message, status, body) {
|
|
462
|
-
super(message);
|
|
463
|
-
this.status = status;
|
|
464
|
-
this.body = body;
|
|
547
|
+
super(status, message, body, undefined, { retryable: true });
|
|
465
548
|
this.name = "StreamsServerError";
|
|
466
549
|
}
|
|
467
550
|
}
|
|
468
551
|
|
|
469
|
-
class StreamsSignatureError extends
|
|
552
|
+
class StreamsSignatureError extends SecondLayerError {
|
|
470
553
|
constructor(message = "Streams response signature verification failed.") {
|
|
471
554
|
super(message);
|
|
472
555
|
this.name = "StreamsSignatureError";
|
|
@@ -517,6 +600,41 @@ function batchContext(cursor, height, tipHeight) {
|
|
|
517
600
|
blocksBehind: height === null ? null : Math.max(0, tipHeight - height)
|
|
518
601
|
};
|
|
519
602
|
}
|
|
603
|
+
var MAX_RETRY_AFTER_MS = 300000;
|
|
604
|
+
function errRetryable(err) {
|
|
605
|
+
if (err && typeof err === "object" && "retryable" in err) {
|
|
606
|
+
return err.retryable === true;
|
|
607
|
+
}
|
|
608
|
+
return err instanceof TypeError;
|
|
609
|
+
}
|
|
610
|
+
function errRetryAfterMs(err) {
|
|
611
|
+
if (err && typeof err === "object" && "retryAfterSeconds" in err) {
|
|
612
|
+
const seconds = err.retryAfterSeconds;
|
|
613
|
+
if (typeof seconds === "number" && Number.isFinite(seconds)) {
|
|
614
|
+
return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
async function fetchPageWithRetry(fetchPage, opts) {
|
|
620
|
+
const retryCount = opts.retryCount ?? 3;
|
|
621
|
+
const retryDelay = opts.retryDelay ?? 1000;
|
|
622
|
+
for (let attempt = 0;; attempt++) {
|
|
623
|
+
try {
|
|
624
|
+
return await fetchPage();
|
|
625
|
+
} catch (err) {
|
|
626
|
+
const retriesLeft = retryCount - attempt;
|
|
627
|
+
if (retriesLeft <= 0 || !errRetryable(err) || opts.signal?.aborted) {
|
|
628
|
+
throw err;
|
|
629
|
+
}
|
|
630
|
+
const delayMs = errRetryAfterMs(err) ?? retryDelay * (attempt + 1);
|
|
631
|
+
opts.onError?.(err, { attempt, retriesLeft, delayMs });
|
|
632
|
+
await opts.sleep(delayMs, opts.signal);
|
|
633
|
+
if (opts.signal?.aborted)
|
|
634
|
+
throw err;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
520
638
|
async function defaultSleep(ms, signal) {
|
|
521
639
|
if (signal?.aborted)
|
|
522
640
|
return;
|
|
@@ -543,7 +661,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
543
661
|
let emptyPolls = 0;
|
|
544
662
|
let height = null;
|
|
545
663
|
while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
|
|
546
|
-
const envelope = await opts.fetchEvents({
|
|
664
|
+
const envelope = await fetchPageWithRetry(() => opts.fetchEvents({
|
|
547
665
|
cursor,
|
|
548
666
|
limit: opts.batchSize,
|
|
549
667
|
types: opts.types,
|
|
@@ -552,6 +670,12 @@ async function consumeStreamsEvents(opts) {
|
|
|
552
670
|
sender: opts.sender,
|
|
553
671
|
recipient: opts.recipient,
|
|
554
672
|
assetIdentifier: opts.assetIdentifier
|
|
673
|
+
}), {
|
|
674
|
+
retryCount: opts.retryCount,
|
|
675
|
+
retryDelay: opts.retryDelay,
|
|
676
|
+
onError: opts.onError,
|
|
677
|
+
sleep,
|
|
678
|
+
signal: opts.signal
|
|
555
679
|
});
|
|
556
680
|
pages++;
|
|
557
681
|
if (!finalizedOnly && opts.onReorg) {
|
|
@@ -682,10 +806,16 @@ async function consumeIndexFeed(opts) {
|
|
|
682
806
|
let emptyPolls = 0;
|
|
683
807
|
let height = null;
|
|
684
808
|
while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
|
|
685
|
-
const envelope = await opts.fetchPage({
|
|
809
|
+
const envelope = await fetchPageWithRetry(() => opts.fetchPage({
|
|
686
810
|
cursor,
|
|
687
811
|
fromHeight: cursor === null ? opts.fromHeight : undefined,
|
|
688
812
|
limit: batchSize
|
|
813
|
+
}), {
|
|
814
|
+
retryCount: opts.retryCount,
|
|
815
|
+
retryDelay: opts.retryDelay,
|
|
816
|
+
onError: opts.onError,
|
|
817
|
+
sleep,
|
|
818
|
+
signal: opts.signal
|
|
689
819
|
});
|
|
690
820
|
pages++;
|
|
691
821
|
if (!finalizedOnly && opts.onReorg) {
|
|
@@ -1574,7 +1704,10 @@ function createStreamsClient(options) {
|
|
|
1574
1704
|
emptyBackoffMs: params.emptyBackoffMs,
|
|
1575
1705
|
maxPages: params.maxPages,
|
|
1576
1706
|
maxEmptyPolls: params.maxEmptyPolls,
|
|
1577
|
-
signal: params.signal
|
|
1707
|
+
signal: params.signal,
|
|
1708
|
+
retryCount: params.retryCount,
|
|
1709
|
+
retryDelay: params.retryDelay,
|
|
1710
|
+
onError: params.onError
|
|
1578
1711
|
});
|
|
1579
1712
|
},
|
|
1580
1713
|
stream(params = {}) {
|
|
@@ -1809,5 +1942,5 @@ export {
|
|
|
1809
1942
|
Subgraphs
|
|
1810
1943
|
};
|
|
1811
1944
|
|
|
1812
|
-
//# debugId=
|
|
1945
|
+
//# debugId=A36A74FD0514129964756E2164756E21
|
|
1813
1946
|
//# sourceMappingURL=index.js.map
|