@secondlayer/sdk 5.7.0 → 5.9.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/README.md CHANGED
@@ -50,7 +50,7 @@ console.log({ tip, firstCursor: page.events[0]?.cursor });
50
50
  import { createStreamsClient } from "@secondlayer/sdk";
51
51
 
52
52
  const streams = createStreamsClient({
53
- apiKey: process.env.SL_SERVICE_KEY!, // sk-sl_...
53
+ apiKey: process.env.SL_API_KEY!, // sk-sl_...
54
54
  });
55
55
  ```
56
56
 
package/dist/index.d.ts CHANGED
@@ -359,11 +359,21 @@ type StreamsEvent = {
359
359
  contract_id: string | null
360
360
  payload: StreamsEventPayload
361
361
  ts: string
362
+ /**
363
+ * True when this event's block is past the finality boundary (immutable).
364
+ * Optional for back-compat; the API always sets it on Streams responses.
365
+ */
366
+ finalized?: boolean
362
367
  };
363
368
  type StreamsTip = {
364
369
  block_height: number
365
370
  block_hash: string
366
371
  burn_block_height: number
372
+ /**
373
+ * Highest Stacks block past the burn-confirmation finality boundary.
374
+ * Optional for back-compat; the API always sets it.
375
+ */
376
+ finalized_height?: number
367
377
  lag_seconds: number
368
378
  };
369
379
  type StreamsCanonicalBlock = {
@@ -403,12 +413,18 @@ type StreamsEventsListParams = {
403
413
  toHeight?: number
404
414
  types?: readonly StreamsEventType[]
405
415
  contractId?: string
416
+ sender?: string
417
+ recipient?: string
418
+ assetIdentifier?: string
406
419
  limit?: number
407
420
  };
408
421
  type StreamsEventsStreamParams = {
409
422
  fromCursor?: string | null
410
423
  types?: readonly StreamsEventType[]
411
424
  contractId?: string
425
+ sender?: string
426
+ recipient?: string
427
+ assetIdentifier?: string
412
428
  batchSize?: number
413
429
  emptyBackoffMs?: number
414
430
  maxPages?: number
@@ -420,6 +436,9 @@ type StreamsEventsConsumeParams = {
420
436
  mode?: "tail" | "bounded"
421
437
  types?: readonly StreamsEventType[]
422
438
  contractId?: string
439
+ sender?: string
440
+ recipient?: string
441
+ assetIdentifier?: string
423
442
  batchSize?: number
424
443
  onBatch: (events: StreamsEvent[], envelope: StreamsEventsEnvelope) => Promise<string | null | undefined> | string | null | undefined
425
444
  emptyBackoffMs?: number
@@ -432,7 +451,63 @@ type StreamsEventsConsumeResult = {
432
451
  pages: number
433
452
  emptyPolls: number
434
453
  };
454
+ type StreamsEventsReplayParams = {
455
+ /** Start point: `"genesis"` (default) or a `<block>:<index>` cursor. */
456
+ from?: "genesis" | string
457
+ /**
458
+ * Called once per finalized dump file, in block order, before live tailing.
459
+ * Process the parquet with your own tooling (e.g. DuckDB) — the SDK does not
460
+ * decode parquet. Use `client.dumps.download(file)` to fetch + verify bytes.
461
+ */
462
+ onDumpFile: (file: StreamsDumpFile) => Promise<void> | void
463
+ /** Called per live page after the dump phase, like `consume`. */
464
+ onBatch: (events: StreamsEvent[], envelope: StreamsEventsEnvelope) => Promise<string | null | undefined> | string | null | undefined
465
+ mode?: "tail" | "bounded"
466
+ batchSize?: number
467
+ emptyBackoffMs?: number
468
+ maxPages?: number
469
+ maxEmptyPolls?: number
470
+ signal?: AbortSignal
471
+ };
435
472
  type FetchLike2 = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
473
+ /** One bulk parquet file in the dumps manifest. `path` is the object key under
474
+ * the dumps base URL. */
475
+ type StreamsDumpFile = {
476
+ path: string
477
+ from_block: number
478
+ to_block: number
479
+ min_cursor: string | null
480
+ max_cursor: string | null
481
+ row_count: number
482
+ byte_size: number
483
+ sha256: string
484
+ schema_version: number
485
+ created_at: string
486
+ };
487
+ type StreamsDumpsManifest = {
488
+ dataset: string
489
+ network: string
490
+ version: string
491
+ schema_version: number
492
+ generated_at: string
493
+ producer_version: string
494
+ finality_lag_blocks: number
495
+ /** Cursor at the end of the finalized bulk coverage — hand to live tailing. */
496
+ latest_finalized_cursor: string | null
497
+ coverage: {
498
+ from_block: number
499
+ to_block: number
500
+ }
501
+ files: StreamsDumpFile[]
502
+ };
503
+ type StreamsDumps = {
504
+ /** Fetch and parse the latest dumps manifest. */
505
+ list(): Promise<StreamsDumpsManifest>
506
+ /** Absolute URL for a manifest file. */
507
+ fileUrl(file: StreamsDumpFile): string
508
+ /** Download a parquet file and verify its sha256 against the manifest. */
509
+ download(file: StreamsDumpFile): Promise<Uint8Array>
510
+ };
436
511
  type StreamsClient = {
437
512
  events: {
438
513
  list(params?: StreamsEventsListParams): Promise<StreamsEventsEnvelope>
@@ -448,6 +523,14 @@ type StreamsClient = {
448
523
  */
449
524
  consume(params: StreamsEventsConsumeParams): Promise<StreamsEventsConsumeResult>
450
525
  /**
526
+ * Backfill from bulk dumps, then continue live from the dump→live seam in
527
+ * one call. Iterates finalized dump files (via `onDumpFile`) in block
528
+ * order, then tails live from the manifest's `latest_finalized_cursor`
529
+ * (exclusive input → no gap or duplicate at the seam). Requires
530
+ * `dumpsBaseUrl`.
531
+ */
532
+ replay(params: StreamsEventsReplayParams): Promise<StreamsEventsConsumeResult>
533
+ /**
451
534
  * Follow Streams as an async iterator.
452
535
  *
453
536
  * Use `stream` for live processors and watch-style apps. It tails
@@ -462,6 +545,8 @@ type StreamsClient = {
462
545
  reorgs: {
463
546
  list(params: StreamsReorgsListParams): Promise<StreamsReorgsListEnvelope>
464
547
  }
548
+ /** Bulk parquet dumps. Requires `dumpsBaseUrl` on the client. */
549
+ dumps: StreamsDumps
465
550
  canonical(height: number): Promise<StreamsCanonicalBlock>
466
551
  tip(): Promise<StreamsTip>
467
552
  };
@@ -523,6 +608,20 @@ type CreateStreamsClientOptions = {
523
608
  apiKey: string
524
609
  baseUrl?: string
525
610
  fetchImpl?: FetchLike2
611
+ /**
612
+ * Public base URL for bulk parquet dumps (the R2/CDN bucket root). Required
613
+ * to use `client.dumps`. See `GET /public/streams/dumps/manifest`.
614
+ */
615
+ dumpsBaseUrl?: string
616
+ /**
617
+ * Verify the ed25519 `X-Signature` on every response (default off). Pass
618
+ * `true` to fetch the server's public key from
619
+ * `/public/streams/signing-key`, or `{ publicKey }` to pin a known PEM. A
620
+ * failed or missing signature throws `StreamsSignatureError`.
621
+ */
622
+ verify?: boolean | {
623
+ publicKey: string
624
+ }
526
625
  };
527
626
  declare function createStreamsClient(options: CreateStreamsClientOptions): StreamsClient;
528
627
  declare class AuthError extends Error {
@@ -544,6 +643,10 @@ declare class StreamsServerError extends Error {
544
643
  readonly body?: unknown;
545
644
  constructor(message: string, status: number, body?: unknown);
546
645
  }
646
+ /** Thrown when response signature verification is enabled and fails. */
647
+ declare class StreamsSignatureError extends Error {
648
+ constructor(message?: string);
649
+ }
547
650
  type FtTransferPayload = {
548
651
  asset_identifier: string
549
652
  sender: string
@@ -1054,4 +1157,4 @@ declare function toJsonSafe(value: unknown): unknown;
1054
1157
  /** Decode a hex-encoded Clarity value to JSON-safe JS (uints as strings,
1055
1158
  * buffers as `0x…` hex, tuples as objects). Returns the input hex on failure. */
1056
1159
  declare function decodeClarityValue(hex: string): unknown;
1057
- export { verifyWebhookSignature, toJsonSafe, isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, getSubgraph, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decodeClarityValue, createStreamsClient, VersionConflictError, ValidationError, UpdateSubscriptionRequest2 as UpdateSubscriptionRequest, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsTip, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorg, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsClient, StreamsCanonicalBlock, SecondLayerOptions, SecondLayer, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, IndexTip, IndexEventType, IndexEvent, IndexContractCall, Index, FtTransfersWalkParams, FtTransfersListParams, FtTransfersEnvelope, FtTransferPayload, FtTransferEvent, FtTransfer, FetchLike2 as FetchLike, EventsWalkParams, EventsListParams, EventsEnvelope, DeliveryRow2 as DeliveryRow, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, DeadRow2 as DeadRow, Datasets, DatasetRow, CursorListParams, CursorEnvelope, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, CURSOR_SLUGS, AuthError, ApiError };
1160
+ export { verifyWebhookSignature, toJsonSafe, isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, getSubgraph, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decodeClarityValue, createStreamsClient, VersionConflictError, ValidationError, UpdateSubscriptionRequest2 as UpdateSubscriptionRequest, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorg, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, SecondLayerOptions, SecondLayer, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, IndexTip, IndexEventType, IndexEvent, IndexContractCall, Index, FtTransfersWalkParams, FtTransfersListParams, FtTransfersEnvelope, FtTransferPayload, FtTransferEvent, FtTransfer, FetchLike2 as FetchLike, EventsWalkParams, EventsListParams, EventsEnvelope, DeliveryRow2 as DeliveryRow, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, DeadRow2 as DeadRow, Datasets, DatasetRow, CursorListParams, CursorEnvelope, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, CURSOR_SLUGS, AuthError, ApiError };
package/dist/index.js CHANGED
@@ -282,6 +282,31 @@ class Subgraphs extends BaseClient {
282
282
  filters
283
283
  });
284
284
  return result.count;
285
+ },
286
+ subscribe(onRow, options = {}) {
287
+ const filters = options.where ? serializeWhere(options.where) : {};
288
+ const qs = new URLSearchParams;
289
+ for (const [k, v] of Object.entries(filters))
290
+ qs.set(k, String(v));
291
+ if (options.since != null)
292
+ qs.set("since", String(options.since));
293
+ const query = qs.toString();
294
+ const url = `${self.baseUrl}/api/subgraphs/${subgraphName}/${tableName}/stream${query ? `?${query}` : ""}`;
295
+ const ES = globalThis.EventSource;
296
+ if (!ES) {
297
+ throw new Error("subscribe() needs a global EventSource (available in browsers and Node >= 22).");
298
+ }
299
+ const es = new ES(url);
300
+ es.onmessage = (ev) => {
301
+ try {
302
+ onRow(JSON.parse(ev.data));
303
+ } catch {}
304
+ };
305
+ if (options.onError) {
306
+ const handler = options.onError;
307
+ es.onerror = (ev) => handler(ev);
308
+ }
309
+ return () => es.close();
285
310
  }
286
311
  };
287
312
  }
@@ -476,6 +501,9 @@ class Index extends BaseClient {
476
501
  }
477
502
  }
478
503
 
504
+ // src/streams/client.ts
505
+ import { ed25519 } from "@secondlayer/shared";
506
+
479
507
  // src/streams/consumer.ts
480
508
  async function defaultSleep(ms, signal) {
481
509
  if (signal?.aborted)
@@ -504,7 +532,10 @@ async function consumeStreamsEvents(opts) {
504
532
  cursor,
505
533
  limit: opts.batchSize,
506
534
  types: opts.types,
507
- contractId: opts.contractId
535
+ contractId: opts.contractId,
536
+ sender: opts.sender,
537
+ recipient: opts.recipient,
538
+ assetIdentifier: opts.assetIdentifier
508
539
  });
509
540
  pages++;
510
541
  const returnedCursor = await opts.onBatch(envelope.events, envelope);
@@ -539,7 +570,10 @@ async function* streamStreamsEvents(opts) {
539
570
  cursor,
540
571
  limit: opts.batchSize,
541
572
  types: opts.types,
542
- contractId: opts.contractId
573
+ contractId: opts.contractId,
574
+ sender: opts.sender,
575
+ recipient: opts.recipient,
576
+ assetIdentifier: opts.assetIdentifier
543
577
  });
544
578
  pages++;
545
579
  for (const event of envelope.events) {
@@ -564,6 +598,9 @@ async function* streamStreamsEvents(opts) {
564
598
  }
565
599
  }
566
600
 
601
+ // src/streams/dumps.ts
602
+ import { createHash } from "node:crypto";
603
+
567
604
  // src/streams/errors.ts
568
605
  class AuthError extends Error {
569
606
  status = 401;
@@ -605,7 +642,60 @@ class StreamsServerError extends Error {
605
642
  }
606
643
  }
607
644
 
645
+ class StreamsSignatureError extends Error {
646
+ constructor(message = "Streams response signature verification failed.") {
647
+ super(message);
648
+ this.name = "StreamsSignatureError";
649
+ }
650
+ }
651
+
652
+ // src/streams/dumps.ts
653
+ function createStreamsDumps(opts) {
654
+ const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
655
+ function requireBaseUrl() {
656
+ if (!baseUrl) {
657
+ throw new StreamsServerError("Streams dumps require `dumpsBaseUrl` on createStreamsClient.", 0);
658
+ }
659
+ return baseUrl;
660
+ }
661
+ function fileUrl(file) {
662
+ return `${requireBaseUrl()}/${file.path.replace(/^\/+/, "")}`;
663
+ }
664
+ async function list() {
665
+ const url = `${requireBaseUrl()}/manifest/latest.json`;
666
+ const res = await opts.fetchImpl(url);
667
+ if (!res.ok) {
668
+ throw new StreamsServerError(`Could not fetch dumps manifest (${res.status}).`, res.status);
669
+ }
670
+ return await res.json();
671
+ }
672
+ async function download(file) {
673
+ const res = await opts.fetchImpl(fileUrl(file));
674
+ if (!res.ok) {
675
+ throw new StreamsServerError(`Could not download dump ${file.path} (${res.status}).`, res.status);
676
+ }
677
+ const bytes = new Uint8Array(await res.arrayBuffer());
678
+ const digest = createHash("sha256").update(bytes).digest("hex");
679
+ if (digest !== file.sha256) {
680
+ throw new StreamsSignatureError(`Dump ${file.path} sha256 mismatch (expected ${file.sha256}, got ${digest}).`);
681
+ }
682
+ return bytes;
683
+ }
684
+ return { list, fileUrl, download };
685
+ }
686
+
608
687
  // src/streams/client.ts
688
+ function cursorTuple(cursor) {
689
+ if (!cursor)
690
+ return [-1, -1];
691
+ const [block, index] = cursor.split(":");
692
+ return [Number(block), Number(index)];
693
+ }
694
+ function maxCursor(a, b) {
695
+ const [ah, ai] = cursorTuple(a);
696
+ const [bh, bi] = cursorTuple(b);
697
+ return ah > bh || ah === bh && ai >= bi ? a : b;
698
+ }
609
699
  var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
610
700
  function normalizeBaseUrl(baseUrl) {
611
701
  return baseUrl.replace(/\/+$/, "");
@@ -653,21 +743,68 @@ async function mapStreamsError(response) {
653
743
  function createStreamsClient(options) {
654
744
  const baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_STREAMS_BASE_URL);
655
745
  const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
746
+ const verify = options.verify ?? false;
747
+ const dumps = createStreamsDumps({
748
+ baseUrl: options.dumpsBaseUrl,
749
+ fetchImpl
750
+ });
751
+ let publicKeyPromise = null;
752
+ function getPublicKey() {
753
+ if (publicKeyPromise)
754
+ return publicKeyPromise;
755
+ publicKeyPromise = (async () => {
756
+ if (typeof verify === "object") {
757
+ return ed25519.loadEd25519PublicKey(verify.publicKey);
758
+ }
759
+ const res = await fetchImpl(`${baseUrl}/public/streams/signing-key`);
760
+ if (!res.ok) {
761
+ throw new StreamsSignatureError(`Could not fetch signing key (${res.status}).`);
762
+ }
763
+ const body = await res.json();
764
+ if (!body.public_key_pem) {
765
+ throw new StreamsSignatureError("Signing key response missing key.");
766
+ }
767
+ return ed25519.loadEd25519PublicKey(body.public_key_pem);
768
+ })();
769
+ return publicKeyPromise;
770
+ }
656
771
  async function request(path) {
657
772
  const response = await fetchImpl(`${baseUrl}${path}`, {
658
773
  headers: { Authorization: `Bearer ${options.apiKey}` }
659
774
  });
660
775
  if (!response.ok)
661
776
  await mapStreamsError(response);
662
- return await response.json();
777
+ const text = await response.text();
778
+ if (verify) {
779
+ const signature = response.headers.get("X-Signature");
780
+ if (!signature) {
781
+ throw new StreamsSignatureError("Response is missing X-Signature.");
782
+ }
783
+ const publicKey = await getPublicKey();
784
+ if (!ed25519.verifyEd25519(text, signature, publicKey)) {
785
+ throw new StreamsSignatureError;
786
+ }
787
+ }
788
+ return JSON.parse(text);
663
789
  }
664
790
  const fetchEvents = async ({
665
791
  cursor,
666
792
  limit,
667
793
  types,
668
- contractId
794
+ contractId,
795
+ sender,
796
+ recipient,
797
+ assetIdentifier
669
798
  }) => {
670
- return listEvents({ cursor, limit, types, contractId });
799
+ return listEvents({
800
+ cursor,
801
+ limit,
802
+ types,
803
+ contractId,
804
+ sender,
805
+ recipient,
806
+ assetIdentifier
807
+ });
671
808
  };
672
809
  async function listEvents(params = {}) {
673
810
  const searchParams = new URLSearchParams;
@@ -676,6 +813,9 @@ function createStreamsClient(options) {
676
813
  appendSearchParam2(searchParams, "to_height", params.toHeight);
677
814
  appendSearchParam2(searchParams, "limit", params.limit);
678
815
  appendSearchParam2(searchParams, "contract_id", params.contractId);
816
+ appendSearchParam2(searchParams, "sender", params.sender);
817
+ appendSearchParam2(searchParams, "recipient", params.recipient);
818
+ appendSearchParam2(searchParams, "asset_identifier", params.assetIdentifier);
679
819
  if (params.types?.length) {
680
820
  searchParams.set("types", params.types.join(","));
681
821
  }
@@ -694,6 +834,9 @@ function createStreamsClient(options) {
694
834
  mode: params.mode,
695
835
  types: params.types,
696
836
  contractId: params.contractId,
837
+ sender: params.sender,
838
+ recipient: params.recipient,
839
+ assetIdentifier: params.assetIdentifier,
697
840
  batchSize: params.batchSize ?? 100,
698
841
  fetchEvents,
699
842
  onBatch: params.onBatch,
@@ -708,6 +851,9 @@ function createStreamsClient(options) {
708
851
  fromCursor: params.fromCursor,
709
852
  types: params.types,
710
853
  contractId: params.contractId,
854
+ sender: params.sender,
855
+ recipient: params.recipient,
856
+ assetIdentifier: params.assetIdentifier,
711
857
  batchSize: params.batchSize ?? 100,
712
858
  emptyBackoffMs: params.emptyBackoffMs,
713
859
  maxPages: params.maxPages,
@@ -715,6 +861,29 @@ function createStreamsClient(options) {
715
861
  signal: params.signal,
716
862
  fetchEvents
717
863
  });
864
+ },
865
+ async replay(params) {
866
+ const fromCursor = params.from === "genesis" ? null : params.from ?? null;
867
+ const fromBlock = fromCursor ? cursorTuple(fromCursor)[0] : 0;
868
+ const manifest = await dumps.list();
869
+ const files = manifest.files.filter((file) => file.to_block >= fromBlock).sort((a, b) => a.from_block - b.from_block || a.to_block - b.to_block);
870
+ for (const file of files) {
871
+ if (params.signal?.aborted)
872
+ break;
873
+ await params.onDumpFile(file);
874
+ }
875
+ const seam = maxCursor(fromCursor, manifest.latest_finalized_cursor);
876
+ return consumeStreamsEvents({
877
+ fromCursor: seam,
878
+ mode: params.mode ?? "tail",
879
+ batchSize: params.batchSize ?? 100,
880
+ fetchEvents,
881
+ onBatch: params.onBatch,
882
+ emptyBackoffMs: params.emptyBackoffMs,
883
+ maxPages: params.maxPages,
884
+ maxEmptyPolls: params.maxEmptyPolls,
885
+ signal: params.signal
886
+ });
718
887
  }
719
888
  },
720
889
  blocks: {
@@ -731,6 +900,7 @@ function createStreamsClient(options) {
731
900
  return request(`/v1/streams/reorgs${query ? `?${query}` : ""}`);
732
901
  }
733
902
  },
903
+ dumps,
734
904
  canonical(height) {
735
905
  return request(`/v1/streams/canonical/${height}`);
736
906
  },
@@ -1377,6 +1547,7 @@ export {
1377
1547
  ValidationError,
1378
1548
  Subscriptions,
1379
1549
  Subgraphs,
1550
+ StreamsSignatureError,
1380
1551
  StreamsServerError,
1381
1552
  SecondLayer,
1382
1553
  RateLimitError,
@@ -1387,5 +1558,5 @@ export {
1387
1558
  ApiError
1388
1559
  };
1389
1560
 
1390
- //# debugId=6FAE9FA63DDF564064756E2164756E21
1561
+ //# debugId=54289A1292072EB864756E2164756E21
1391
1562
  //# sourceMappingURL=index.js.map