@secondlayer/sdk 6.35.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 +165 -68
- package/dist/index.js +216 -55
- package/dist/index.js.map +12 -12
- package/dist/streams/index.d.ts +101 -11
- package/dist/streams/index.js +195 -51
- package/dist/streams/index.js.map +8 -8
- package/dist/subgraphs/index.d.ts +39 -0
- package/dist/subgraphs/index.js +204 -52
- package/dist/subgraphs/index.js.map +9 -9
- package/dist/x402.js +9 -3
- package/dist/x402.js.map +3 -3
- package/package.json +6 -4
package/dist/streams/index.d.ts
CHANGED
|
@@ -274,6 +274,18 @@ type StreamsEventsConsumeParams = {
|
|
|
274
274
|
* stale rows from an orphaned fork are left in place).
|
|
275
275
|
*/
|
|
276
276
|
onReorg?: (reorg: StreamsReorg, ctx: StreamsReorgContext) => Promise<void> | void
|
|
277
|
+
/** Page-fetch retries after the first failure (429/5xx/network only —
|
|
278
|
+
* 4xx and handler throws always propagate). Default 3; `0` disables. */
|
|
279
|
+
retryCount?: number
|
|
280
|
+
/** Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server
|
|
281
|
+
* `Retry-After` overrides it. Default 1000. */
|
|
282
|
+
retryDelay?: number
|
|
283
|
+
/** Void observer, called before each retry sleep (metrics/logging). */
|
|
284
|
+
onError?: (err: unknown, ctx: {
|
|
285
|
+
attempt: number
|
|
286
|
+
retriesLeft: number
|
|
287
|
+
delayMs: number
|
|
288
|
+
}) => void
|
|
277
289
|
emptyBackoffMs?: number
|
|
278
290
|
maxPages?: number
|
|
279
291
|
maxEmptyPolls?: number
|
|
@@ -500,27 +512,105 @@ type CreateStreamsClientOptions = {
|
|
|
500
512
|
verifyDumpsManifest?: boolean
|
|
501
513
|
};
|
|
502
514
|
declare function createStreamsClient(options: CreateStreamsClientOptions): StreamsClient;
|
|
503
|
-
|
|
504
|
-
|
|
515
|
+
/** Options accepted by every error in the family. */
|
|
516
|
+
interface SecondLayerErrorOptions {
|
|
517
|
+
cause?: Error;
|
|
518
|
+
details?: string;
|
|
519
|
+
/** Stable machine-readable code (e.g. `UPGRADE_REQUIRED`, `BYO_BREAKING_CHANGE`). */
|
|
520
|
+
code?: string;
|
|
521
|
+
/** Docs page that explains this failure and its fix. */
|
|
522
|
+
docsUrl?: string;
|
|
523
|
+
/** Extra context lines appended to the message (DDL to run, next steps). */
|
|
524
|
+
metaMessages?: string[];
|
|
525
|
+
/** Whether retrying the SAME request can succeed (429/5xx/network). */
|
|
526
|
+
retryable?: boolean;
|
|
527
|
+
/** Parsed `Retry-After`, in seconds — set on rate limits that carry it. */
|
|
528
|
+
retryAfterSeconds?: number;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Root of the SDK error family. Implements the same protocol as `BaseError`
|
|
532
|
+
* in `@secondlayer/stacks` (`shortMessage`, `cause`, `toJSON`) so a failed
|
|
533
|
+
* chain read and a failed platform read serialize and introspect identically.
|
|
534
|
+
* It intentionally does NOT extend that class: `BaseError.details` is a
|
|
535
|
+
* `string`, while {@link ByoBreakingChangeError.details} is the documented
|
|
536
|
+
* structured migration plan — and cross-package `instanceof` is unreliable
|
|
537
|
+
* under per-package bundling anyway (match on `code`/`name` across packages).
|
|
538
|
+
*
|
|
539
|
+
* `retryable` is the signal the consume loops act on: 429/5xx/network are
|
|
540
|
+
* retryable, 4xx and body-serialization failures are not.
|
|
541
|
+
*/
|
|
542
|
+
declare class SecondLayerError extends Error {
|
|
543
|
+
name: string;
|
|
544
|
+
/** The one-line failure, without the appended context blocks. */
|
|
545
|
+
shortMessage: string;
|
|
546
|
+
code?: string;
|
|
547
|
+
docsUrl?: string;
|
|
548
|
+
metaMessages?: string[];
|
|
549
|
+
retryable: boolean;
|
|
550
|
+
retryAfterSeconds?: number;
|
|
551
|
+
constructor(shortMessage: string, options?: SecondLayerErrorOptions);
|
|
552
|
+
/**
|
|
553
|
+
* Walk the cause chain. With a predicate, returns the first error matching
|
|
554
|
+
* it (or `null`); without one, returns the deepest cause. Mirrors viem's
|
|
555
|
+
* `error.walk()` so cross-library error handling reads the same.
|
|
556
|
+
*/
|
|
557
|
+
walk(fn?: (err: unknown) => boolean): unknown;
|
|
558
|
+
toJSON(): {
|
|
559
|
+
name: string
|
|
560
|
+
message: string
|
|
561
|
+
shortMessage: string
|
|
562
|
+
cause: string | undefined
|
|
563
|
+
code: string | undefined
|
|
564
|
+
docsUrl: string | undefined
|
|
565
|
+
retryable: boolean
|
|
566
|
+
retryAfterSeconds: number | undefined
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Error thrown by {@link SecondLayer} when an API request fails.
|
|
571
|
+
* Includes the HTTP status code for programmatic error handling.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* try {
|
|
576
|
+
* await client.subgraphs.get("my-subgraph");
|
|
577
|
+
* } catch (err) {
|
|
578
|
+
* if (err instanceof ApiError && err.status === 404) {
|
|
579
|
+
* console.log("Subgraph not found");
|
|
580
|
+
* }
|
|
581
|
+
* }
|
|
582
|
+
* ```
|
|
583
|
+
*/
|
|
584
|
+
declare class ApiError extends SecondLayerError {
|
|
585
|
+
/** HTTP status code (0 for network errors). */
|
|
586
|
+
status: number;
|
|
587
|
+
/** Raw response body (parsed JSON if possible) — preserved for callers that need error details. */
|
|
588
|
+
body?: unknown;
|
|
589
|
+
constructor(status: number, message: string, body?: unknown, code?: string, options?: SecondLayerErrorOptions);
|
|
590
|
+
}
|
|
591
|
+
/** Thrown on a 401 — by both the platform clients and Streams. */
|
|
592
|
+
declare class AuthError extends ApiError {
|
|
593
|
+
readonly status: 401;
|
|
505
594
|
constructor(message?: string);
|
|
506
595
|
}
|
|
507
|
-
|
|
596
|
+
/** Thrown on a 429 — by both the platform clients and Streams. `retryable`,
|
|
597
|
+
* with `retryAfterSeconds` parsed from the `Retry-After` header when sent. */
|
|
598
|
+
declare class RateLimitError extends ApiError {
|
|
599
|
+
/** Raw `Retry-After` header value (seconds or HTTP-date). */
|
|
508
600
|
readonly retryAfter?: string;
|
|
509
|
-
readonly status
|
|
601
|
+
readonly status: 429;
|
|
510
602
|
constructor(message?: string, retryAfter?: string);
|
|
511
603
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
readonly body?: unknown;
|
|
604
|
+
/** Thrown on a 4xx the caller can fix (bad cursor, bad params). Never retried. */
|
|
605
|
+
declare class ValidationError extends ApiError {
|
|
515
606
|
constructor(message: string, status: number, body?: unknown);
|
|
516
607
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
readonly body?: unknown;
|
|
608
|
+
/** Thrown on a 5xx from the Streams API. `retryable`. */
|
|
609
|
+
declare class StreamsServerError extends ApiError {
|
|
520
610
|
constructor(message: string, status: number, body?: unknown);
|
|
521
611
|
}
|
|
522
612
|
/** Thrown when response signature verification is enabled and fails. */
|
|
523
|
-
declare class StreamsSignatureError extends
|
|
613
|
+
declare class StreamsSignatureError extends SecondLayerError {
|
|
524
614
|
constructor(message?: string);
|
|
525
615
|
}
|
|
526
616
|
type FtTransferEvent = Extract<StreamsEvent, {
|
package/dist/streams/index.js
CHANGED
|
@@ -2,19 +2,129 @@
|
|
|
2
2
|
import { ed25519 as ed255192 } from "@secondlayer/shared";
|
|
3
3
|
|
|
4
4
|
// src/errors.ts
|
|
5
|
-
|
|
5
|
+
var DOCS_BASE = "https://www.secondlayer.tools";
|
|
6
|
+
|
|
7
|
+
class SecondLayerError extends Error {
|
|
8
|
+
name = "SecondLayerError";
|
|
9
|
+
shortMessage;
|
|
10
|
+
code;
|
|
11
|
+
docsUrl;
|
|
12
|
+
metaMessages;
|
|
13
|
+
retryable;
|
|
14
|
+
retryAfterSeconds;
|
|
15
|
+
constructor(shortMessage, options = {}) {
|
|
16
|
+
const detailBlocks = [
|
|
17
|
+
...options.metaMessages ?? [],
|
|
18
|
+
...options.details ? [options.details] : [],
|
|
19
|
+
...options.docsUrl ? [`Docs: ${options.docsUrl}`] : []
|
|
20
|
+
];
|
|
21
|
+
const message = [
|
|
22
|
+
shortMessage,
|
|
23
|
+
detailBlocks.length > 0 ? `
|
|
24
|
+
${detailBlocks.join(`
|
|
25
|
+
`)}` : ""
|
|
26
|
+
].join("");
|
|
27
|
+
super(message, options.cause ? { cause: options.cause } : undefined);
|
|
28
|
+
this.shortMessage = shortMessage;
|
|
29
|
+
this.code = options.code;
|
|
30
|
+
this.docsUrl = options.docsUrl;
|
|
31
|
+
this.metaMessages = options.metaMessages;
|
|
32
|
+
this.retryable = options.retryable ?? false;
|
|
33
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
34
|
+
}
|
|
35
|
+
walk(fn) {
|
|
36
|
+
let err = this;
|
|
37
|
+
let last = this;
|
|
38
|
+
while (err != null) {
|
|
39
|
+
if (fn?.(err))
|
|
40
|
+
return err;
|
|
41
|
+
last = err;
|
|
42
|
+
err = err.cause;
|
|
43
|
+
}
|
|
44
|
+
return fn ? null : last;
|
|
45
|
+
}
|
|
46
|
+
toJSON() {
|
|
47
|
+
return {
|
|
48
|
+
name: this.name,
|
|
49
|
+
message: this.message,
|
|
50
|
+
shortMessage: this.shortMessage,
|
|
51
|
+
cause: this.cause instanceof Error ? this.cause.message : undefined,
|
|
52
|
+
code: this.code,
|
|
53
|
+
docsUrl: this.docsUrl,
|
|
54
|
+
retryable: this.retryable,
|
|
55
|
+
retryAfterSeconds: this.retryAfterSeconds
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function statusRetryable(status) {
|
|
60
|
+
return status === 429 || status >= 500;
|
|
61
|
+
}
|
|
62
|
+
function docsFor(status, code) {
|
|
63
|
+
if (code === "UPGRADE_REQUIRED" || code === "PLAN_REQUIRED" || status === 402)
|
|
64
|
+
return `${DOCS_BASE}/docs/authentication#pay-as-you-go-credits`;
|
|
65
|
+
if (status === 429 || status === 401)
|
|
66
|
+
return `${DOCS_BASE}/docs/authentication`;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class ApiError extends SecondLayerError {
|
|
6
71
|
status;
|
|
7
72
|
body;
|
|
8
|
-
code
|
|
9
|
-
|
|
10
|
-
|
|
73
|
+
constructor(status, message, body, code, options = {}) {
|
|
74
|
+
super(message, {
|
|
75
|
+
code,
|
|
76
|
+
retryable: options.retryable ?? statusRetryable(status),
|
|
77
|
+
docsUrl: options.docsUrl ?? docsFor(status, code),
|
|
78
|
+
...options.cause ? { cause: options.cause } : {},
|
|
79
|
+
...options.details ? { details: options.details } : {},
|
|
80
|
+
...options.metaMessages ? { metaMessages: options.metaMessages } : {},
|
|
81
|
+
...options.retryAfterSeconds !== undefined ? { retryAfterSeconds: options.retryAfterSeconds } : {}
|
|
82
|
+
});
|
|
11
83
|
this.status = status;
|
|
12
84
|
this.body = body;
|
|
13
|
-
this.code = code;
|
|
14
85
|
this.name = "ApiError";
|
|
15
86
|
}
|
|
16
87
|
}
|
|
17
88
|
|
|
89
|
+
class AuthError extends ApiError {
|
|
90
|
+
status = 401;
|
|
91
|
+
constructor(message = "API key invalid or expired.") {
|
|
92
|
+
super(401, message);
|
|
93
|
+
this.name = "AuthError";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class RateLimitError extends ApiError {
|
|
98
|
+
retryAfter;
|
|
99
|
+
status = 429;
|
|
100
|
+
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
101
|
+
super(429, message, undefined, undefined, {
|
|
102
|
+
retryable: true,
|
|
103
|
+
retryAfterSeconds: parseRetryAfter(retryAfter)
|
|
104
|
+
});
|
|
105
|
+
this.retryAfter = retryAfter;
|
|
106
|
+
this.name = "RateLimitError";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class ValidationError extends ApiError {
|
|
111
|
+
constructor(message, status, body) {
|
|
112
|
+
super(status, message, body, undefined, { retryable: false });
|
|
113
|
+
this.name = "ValidationError";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function parseRetryAfter(value) {
|
|
117
|
+
if (!value)
|
|
118
|
+
return;
|
|
119
|
+
const seconds = Number(value);
|
|
120
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
121
|
+
return seconds;
|
|
122
|
+
const date = Date.parse(value);
|
|
123
|
+
if (!Number.isNaN(date))
|
|
124
|
+
return Math.max(0, (date - Date.now()) / 1000);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
18
128
|
class VersionConflictError extends ApiError {
|
|
19
129
|
currentVersion;
|
|
20
130
|
expectedVersion;
|
|
@@ -29,7 +139,13 @@ class VersionConflictError extends ApiError {
|
|
|
29
139
|
class ByoBreakingChangeError extends ApiError {
|
|
30
140
|
details;
|
|
31
141
|
constructor(message, details) {
|
|
32
|
-
super(422, message, details, "BYO_BREAKING_CHANGE"
|
|
142
|
+
super(422, message, details, "BYO_BREAKING_CHANGE", {
|
|
143
|
+
metaMessages: [
|
|
144
|
+
"Review and run manually on your database:",
|
|
145
|
+
details.plan.dropStatement,
|
|
146
|
+
...details.plan.statements
|
|
147
|
+
]
|
|
148
|
+
});
|
|
33
149
|
this.name = "ByoBreakingChangeError";
|
|
34
150
|
this.details = details;
|
|
35
151
|
}
|
|
@@ -69,10 +185,12 @@ class BaseClient {
|
|
|
69
185
|
baseUrl;
|
|
70
186
|
apiKey;
|
|
71
187
|
origin;
|
|
188
|
+
fetchImpl;
|
|
72
189
|
constructor(options = {}) {
|
|
73
190
|
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
74
191
|
this.apiKey = resolveApiKey(options.apiKey);
|
|
75
192
|
this.origin = options.origin ?? "cli";
|
|
193
|
+
this.fetchImpl = options.fetchImpl ?? ((...args) => fetch(...args));
|
|
76
194
|
}
|
|
77
195
|
static authHeaders(apiKey) {
|
|
78
196
|
const headers = {
|
|
@@ -113,27 +231,27 @@ class BaseClient {
|
|
|
113
231
|
serializedBody = JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
114
232
|
} catch (err) {
|
|
115
233
|
const detail = err instanceof Error ? err.message : String(err);
|
|
116
|
-
throw new ApiError(0, `Failed to serialize request body: ${detail}
|
|
234
|
+
throw new ApiError(0, `Failed to serialize request body: ${detail}`, undefined, undefined, { retryable: false });
|
|
117
235
|
}
|
|
118
236
|
}
|
|
119
237
|
let response;
|
|
120
238
|
try {
|
|
121
|
-
response = await
|
|
239
|
+
response = await this.fetchImpl(url, {
|
|
122
240
|
method,
|
|
123
241
|
headers,
|
|
124
242
|
body: serializedBody
|
|
125
243
|
});
|
|
126
|
-
} catch {
|
|
127
|
-
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again
|
|
244
|
+
} catch (err) {
|
|
245
|
+
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 });
|
|
128
246
|
}
|
|
129
247
|
if (!response.ok) {
|
|
130
248
|
if (response.status === 401) {
|
|
131
|
-
throw new
|
|
249
|
+
throw new AuthError;
|
|
132
250
|
}
|
|
133
251
|
if (response.status === 429) {
|
|
134
252
|
const retryAfter = response.headers.get("Retry-After");
|
|
135
253
|
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
136
|
-
throw new
|
|
254
|
+
throw new RateLimitError(msg, retryAfter ?? undefined);
|
|
137
255
|
}
|
|
138
256
|
if (response.status >= 500) {
|
|
139
257
|
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
@@ -168,47 +286,14 @@ class BaseClient {
|
|
|
168
286
|
}
|
|
169
287
|
|
|
170
288
|
// src/streams/errors.ts
|
|
171
|
-
class
|
|
172
|
-
status = 401;
|
|
173
|
-
constructor(message = "API key invalid or expired.") {
|
|
174
|
-
super(message);
|
|
175
|
-
this.name = "AuthError";
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
class RateLimitError extends Error {
|
|
180
|
-
retryAfter;
|
|
181
|
-
status = 429;
|
|
182
|
-
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
183
|
-
super(message);
|
|
184
|
-
this.retryAfter = retryAfter;
|
|
185
|
-
this.name = "RateLimitError";
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
class ValidationError extends Error {
|
|
190
|
-
status;
|
|
191
|
-
body;
|
|
192
|
-
constructor(message, status, body) {
|
|
193
|
-
super(message);
|
|
194
|
-
this.status = status;
|
|
195
|
-
this.body = body;
|
|
196
|
-
this.name = "ValidationError";
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
class StreamsServerError extends Error {
|
|
201
|
-
status;
|
|
202
|
-
body;
|
|
289
|
+
class StreamsServerError extends ApiError {
|
|
203
290
|
constructor(message, status, body) {
|
|
204
|
-
super(message);
|
|
205
|
-
this.status = status;
|
|
206
|
-
this.body = body;
|
|
291
|
+
super(status, message, body, undefined, { retryable: true });
|
|
207
292
|
this.name = "StreamsServerError";
|
|
208
293
|
}
|
|
209
294
|
}
|
|
210
295
|
|
|
211
|
-
class StreamsSignatureError extends
|
|
296
|
+
class StreamsSignatureError extends SecondLayerError {
|
|
212
297
|
constructor(message = "Streams response signature verification failed.") {
|
|
213
298
|
super(message);
|
|
214
299
|
this.name = "StreamsSignatureError";
|
|
@@ -238,6 +323,19 @@ var Cursor = {
|
|
|
238
323
|
function reorgKey(reorg) {
|
|
239
324
|
return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
|
|
240
325
|
}
|
|
326
|
+
function assertFinalizedCheckpoint(returned, checkpoint) {
|
|
327
|
+
if (returned === null || returned === undefined)
|
|
328
|
+
return;
|
|
329
|
+
if (returned === checkpoint)
|
|
330
|
+
return;
|
|
331
|
+
const r = Cursor.parse(returned);
|
|
332
|
+
const c = checkpoint === null ? null : Cursor.parse(checkpoint);
|
|
333
|
+
const above = c === null || r.blockHeight > c.blockHeight || r.blockHeight === c.blockHeight && r.eventIndex > c.eventIndex;
|
|
334
|
+
if (above) {
|
|
335
|
+
const boundary = c ? ` ("${checkpoint}")` : " (none delivered yet)";
|
|
336
|
+
throw new ValidationError(`onBatch returned cursor "${returned}", above the last delivered finalized event${boundary}. With finalizedOnly, events past that point were filtered out and will be re-read once finalized — committing beyond it skips them permanently. Return ctx.cursor (or nothing); never envelope.next_cursor.`, 400);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
241
339
|
function batchContext(cursor, height, tipHeight) {
|
|
242
340
|
return {
|
|
243
341
|
cursor,
|
|
@@ -246,6 +344,41 @@ function batchContext(cursor, height, tipHeight) {
|
|
|
246
344
|
blocksBehind: height === null ? null : Math.max(0, tipHeight - height)
|
|
247
345
|
};
|
|
248
346
|
}
|
|
347
|
+
var MAX_RETRY_AFTER_MS = 300000;
|
|
348
|
+
function errRetryable(err) {
|
|
349
|
+
if (err && typeof err === "object" && "retryable" in err) {
|
|
350
|
+
return err.retryable === true;
|
|
351
|
+
}
|
|
352
|
+
return err instanceof TypeError;
|
|
353
|
+
}
|
|
354
|
+
function errRetryAfterMs(err) {
|
|
355
|
+
if (err && typeof err === "object" && "retryAfterSeconds" in err) {
|
|
356
|
+
const seconds = err.retryAfterSeconds;
|
|
357
|
+
if (typeof seconds === "number" && Number.isFinite(seconds)) {
|
|
358
|
+
return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
async function fetchPageWithRetry(fetchPage, opts) {
|
|
364
|
+
const retryCount = opts.retryCount ?? 3;
|
|
365
|
+
const retryDelay = opts.retryDelay ?? 1000;
|
|
366
|
+
for (let attempt = 0;; attempt++) {
|
|
367
|
+
try {
|
|
368
|
+
return await fetchPage();
|
|
369
|
+
} catch (err) {
|
|
370
|
+
const retriesLeft = retryCount - attempt;
|
|
371
|
+
if (retriesLeft <= 0 || !errRetryable(err) || opts.signal?.aborted) {
|
|
372
|
+
throw err;
|
|
373
|
+
}
|
|
374
|
+
const delayMs = errRetryAfterMs(err) ?? retryDelay * (attempt + 1);
|
|
375
|
+
opts.onError?.(err, { attempt, retriesLeft, delayMs });
|
|
376
|
+
await opts.sleep(delayMs, opts.signal);
|
|
377
|
+
if (opts.signal?.aborted)
|
|
378
|
+
throw err;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
249
382
|
async function defaultSleep(ms, signal) {
|
|
250
383
|
if (signal?.aborted)
|
|
251
384
|
return;
|
|
@@ -272,7 +405,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
272
405
|
let emptyPolls = 0;
|
|
273
406
|
let height = null;
|
|
274
407
|
while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
|
|
275
|
-
const envelope = await opts.fetchEvents({
|
|
408
|
+
const envelope = await fetchPageWithRetry(() => opts.fetchEvents({
|
|
276
409
|
cursor,
|
|
277
410
|
limit: opts.batchSize,
|
|
278
411
|
types: opts.types,
|
|
@@ -281,6 +414,12 @@ async function consumeStreamsEvents(opts) {
|
|
|
281
414
|
sender: opts.sender,
|
|
282
415
|
recipient: opts.recipient,
|
|
283
416
|
assetIdentifier: opts.assetIdentifier
|
|
417
|
+
}), {
|
|
418
|
+
retryCount: opts.retryCount,
|
|
419
|
+
retryDelay: opts.retryDelay,
|
|
420
|
+
onError: opts.onError,
|
|
421
|
+
sleep,
|
|
422
|
+
signal: opts.signal
|
|
284
423
|
});
|
|
285
424
|
pages++;
|
|
286
425
|
if (!finalizedOnly && opts.onReorg) {
|
|
@@ -302,6 +441,8 @@ async function consumeStreamsEvents(opts) {
|
|
|
302
441
|
const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
|
|
303
442
|
height = emitted.at(-1)?.block_height ?? height;
|
|
304
443
|
const returnedCursor = await opts.onBatch(emitted, envelope, batchContext(checkpoint, height, envelope.tip.block_height));
|
|
444
|
+
if (finalizedOnly)
|
|
445
|
+
assertFinalizedCheckpoint(returnedCursor, checkpoint);
|
|
305
446
|
const nextCursor = returnedCursor ?? checkpoint;
|
|
306
447
|
if (nextCursor && nextCursor !== cursor) {
|
|
307
448
|
cursor = nextCursor;
|
|
@@ -785,7 +926,10 @@ function createStreamsClient(options) {
|
|
|
785
926
|
emptyBackoffMs: params.emptyBackoffMs,
|
|
786
927
|
maxPages: params.maxPages,
|
|
787
928
|
maxEmptyPolls: params.maxEmptyPolls,
|
|
788
|
-
signal: params.signal
|
|
929
|
+
signal: params.signal,
|
|
930
|
+
retryCount: params.retryCount,
|
|
931
|
+
retryDelay: params.retryDelay,
|
|
932
|
+
onError: params.onError
|
|
789
933
|
});
|
|
790
934
|
},
|
|
791
935
|
stream(params = {}) {
|
|
@@ -1181,5 +1325,5 @@ export {
|
|
|
1181
1325
|
AuthError
|
|
1182
1326
|
};
|
|
1183
1327
|
|
|
1184
|
-
//# debugId=
|
|
1328
|
+
//# debugId=9793637197EFD6F364756E2164756E21
|
|
1185
1329
|
//# sourceMappingURL=index.js.map
|