@secondlayer/sdk 6.36.0 → 6.37.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/index.d.ts +171 -74
- package/dist/index.js +186 -51
- package/dist/index.js.map +10 -10
- 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 +44 -6
- package/dist/subgraphs/index.js +184 -51
- package/dist/subgraphs/index.js.map +9 -9
- package/package.json +4 -6
|
@@ -277,6 +277,8 @@ declare class Contracts extends BaseClient {
|
|
|
277
277
|
includeAbi?: boolean
|
|
278
278
|
}): Promise<ContractSummary | null>;
|
|
279
279
|
}
|
|
280
|
+
import { Pox5EventTopic } from "@secondlayer/stacks/pox5";
|
|
281
|
+
import { SbtcEventTopic } from "@secondlayer/stacks/sbtc";
|
|
280
282
|
import { InferredTopicSchema } from "@secondlayer/subgraphs";
|
|
281
283
|
import { RewardSet } from "@secondlayer/shared/node/consensus";
|
|
282
284
|
import { MerkleProofStep } from "@secondlayer/shared/node/nakamoto";
|
|
@@ -587,6 +589,18 @@ type StreamsEventsConsumeParams = {
|
|
|
587
589
|
* stale rows from an orphaned fork are left in place).
|
|
588
590
|
*/
|
|
589
591
|
onReorg?: (reorg: StreamsReorg, ctx: StreamsReorgContext) => Promise<void> | void
|
|
592
|
+
/** Page-fetch retries after the first failure (429/5xx/network only —
|
|
593
|
+
* 4xx and handler throws always propagate). Default 3; `0` disables. */
|
|
594
|
+
retryCount?: number
|
|
595
|
+
/** Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server
|
|
596
|
+
* `Retry-After` overrides it. Default 1000. */
|
|
597
|
+
retryDelay?: number
|
|
598
|
+
/** Void observer, called before each retry sleep (metrics/logging). */
|
|
599
|
+
onError?: (err: unknown, ctx: {
|
|
600
|
+
attempt: number
|
|
601
|
+
retriesLeft: number
|
|
602
|
+
delayMs: number
|
|
603
|
+
}) => void
|
|
590
604
|
emptyBackoffMs?: number
|
|
591
605
|
maxPages?: number
|
|
592
606
|
maxEmptyPolls?: number
|
|
@@ -774,6 +788,23 @@ type StreamsUsage = {
|
|
|
774
788
|
}
|
|
775
789
|
};
|
|
776
790
|
type Sleep = (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
791
|
+
/** Options shared by both consume loops' page-fetch retry. Vocabulary matches
|
|
792
|
+
* `@secondlayer/stacks` transports (`retryCount`/`retryDelay`) — one retry
|
|
793
|
+
* language across the family, not a third. */
|
|
794
|
+
type PageRetryOptions = {
|
|
795
|
+
/** Retries after the first failure. Default 3; `0` disables. */
|
|
796
|
+
retryCount?: number
|
|
797
|
+
/** Base delay in ms; the n-th retry waits `retryDelay * n` (matches the
|
|
798
|
+
* stacks transport). A server `Retry-After` overrides it. Default 1000. */
|
|
799
|
+
retryDelay?: number
|
|
800
|
+
/** Void observer, called before each retry sleep (metrics/logging). The
|
|
801
|
+
* retry policy owns the decision; this cannot change it. */
|
|
802
|
+
onError?: (err: unknown, ctx: {
|
|
803
|
+
attempt: number
|
|
804
|
+
retriesLeft: number
|
|
805
|
+
delayMs: number
|
|
806
|
+
}) => void
|
|
807
|
+
};
|
|
777
808
|
/** Minimum shape a consumed Index row must expose. */
|
|
778
809
|
type IndexFeedItem = {
|
|
779
810
|
cursor: string
|
|
@@ -815,6 +846,15 @@ type IndexConsumeOptions<
|
|
|
815
846
|
maxPages?: number
|
|
816
847
|
maxEmptyPolls?: number
|
|
817
848
|
signal?: AbortSignal
|
|
849
|
+
/** Page-fetch retries after the first failure (429/5xx/network only —
|
|
850
|
+
* 4xx and handler throws always propagate). Default 3; `0` disables. */
|
|
851
|
+
retryCount?: number
|
|
852
|
+
/** Base retry delay in ms; the n-th retry waits `retryDelay * n`. A server
|
|
853
|
+
* `Retry-After` overrides it. Default 1000. */
|
|
854
|
+
retryDelay?: number
|
|
855
|
+
/** Void observer, called before each retry sleep. Cannot change the retry
|
|
856
|
+
* decision — the policy owns it. */
|
|
857
|
+
onError?: PageRetryOptions["onError"]
|
|
818
858
|
};
|
|
819
859
|
type IndexTip = {
|
|
820
860
|
block_height: number
|
|
@@ -1374,8 +1414,6 @@ type PrintSchemaResponse = {
|
|
|
1374
1414
|
}
|
|
1375
1415
|
tip: IndexTip
|
|
1376
1416
|
};
|
|
1377
|
-
/** The decoded `sbtc_events` topics. */
|
|
1378
|
-
type SbtcEventTopic = "completed-deposit" | "withdrawal-create" | "withdrawal-accept" | "withdrawal-reject" | "key-rotation" | "update-protocol-contract";
|
|
1379
1417
|
type SbtcWithdrawalStatus = "REQUESTED" | "ACCEPTED" | "REJECTED";
|
|
1380
1418
|
/** A completed sBTC peg-in, keyed by `bitcoin_txid` (deposits carry no
|
|
1381
1419
|
* `request_id`). One terminal `completed-deposit` event per deposit. */
|
|
@@ -1665,10 +1703,10 @@ interface PoxResource {
|
|
|
1665
1703
|
get(rewardCycle: number): Promise<PoxCycleEnvelope | null>
|
|
1666
1704
|
};
|
|
1667
1705
|
}
|
|
1668
|
-
/** The decoded `pox-5` boot-contract print topics (SIP-045 Bitcoin Staking)
|
|
1669
|
-
*
|
|
1670
|
-
*
|
|
1671
|
-
type IndexPox5EventTopic =
|
|
1706
|
+
/** The decoded `pox-5` boot-contract print topics (SIP-045 Bitcoin Staking) —
|
|
1707
|
+
* one source of truth in `@secondlayer/stacks/pox5` (already a dependency;
|
|
1708
|
+
* the old "SDK owns its own dependency surface" rationale predated that). */
|
|
1709
|
+
type IndexPox5EventTopic = Pox5EventTopic;
|
|
1672
1710
|
/** A raw decoded PoX-5 print event. Promoted fields cover the hot query paths;
|
|
1673
1711
|
* `data` always carries the full decoded tuple, including nested shapes
|
|
1674
1712
|
* (`btc-lockup`, `bond-rewards`, `bond-periods`) that have no flat column. */
|
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
|