@secondlayer/sdk 6.0.0 → 6.1.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.
@@ -25,6 +25,19 @@ class VersionConflictError extends ApiError {
25
25
 
26
26
  // src/base.ts
27
27
  var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
28
+ function buildQuery(params) {
29
+ const search = new URLSearchParams;
30
+ for (const [name, value] of Object.entries(params)) {
31
+ if (value === undefined || value === null)
32
+ continue;
33
+ const serialized = Array.isArray(value) ? value.join(",") : String(value);
34
+ if (serialized.length === 0)
35
+ continue;
36
+ search.set(name, serialized);
37
+ }
38
+ const query = search.toString();
39
+ return query ? `?${query}` : "";
40
+ }
28
41
 
29
42
  class BaseClient {
30
43
  baseUrl;
@@ -312,11 +325,6 @@ class Subgraphs extends BaseClient {
312
325
  }
313
326
  }
314
327
  // src/index-api/client.ts
315
- function appendSearchParam(params, name, value) {
316
- if (value === undefined || value === null)
317
- return;
318
- params.set(name, String(value));
319
- }
320
328
  function firstWalkFromHeight(params) {
321
329
  if (params.fromHeight !== undefined)
322
330
  return params.fromHeight;
@@ -346,31 +354,29 @@ class Index extends BaseClient {
346
354
  walk: (params = {}) => this.walkContractCalls(params)
347
355
  };
348
356
  async listFtTransfers(params = {}) {
349
- const searchParams = new URLSearchParams;
350
- appendSearchParam(searchParams, "cursor", params.cursor);
351
- appendSearchParam(searchParams, "from_cursor", params.fromCursor);
352
- appendSearchParam(searchParams, "limit", params.limit);
353
- appendSearchParam(searchParams, "contract_id", params.contractId);
354
- appendSearchParam(searchParams, "sender", params.sender);
355
- appendSearchParam(searchParams, "recipient", params.recipient);
356
- appendSearchParam(searchParams, "from_height", params.fromHeight);
357
- appendSearchParam(searchParams, "to_height", params.toHeight);
358
- const query = searchParams.toString();
359
- return this.request("GET", `/v1/index/ft-transfers${query ? `?${query}` : ""}`);
357
+ return this.request("GET", `/v1/index/ft-transfers${buildQuery({
358
+ cursor: params.cursor,
359
+ from_cursor: params.fromCursor,
360
+ limit: params.limit,
361
+ contract_id: params.contractId,
362
+ sender: params.sender,
363
+ recipient: params.recipient,
364
+ from_height: params.fromHeight,
365
+ to_height: params.toHeight
366
+ })}`);
360
367
  }
361
368
  async listNftTransfers(params = {}) {
362
- const searchParams = new URLSearchParams;
363
- appendSearchParam(searchParams, "cursor", params.cursor);
364
- appendSearchParam(searchParams, "from_cursor", params.fromCursor);
365
- appendSearchParam(searchParams, "limit", params.limit);
366
- appendSearchParam(searchParams, "contract_id", params.contractId);
367
- appendSearchParam(searchParams, "asset_identifier", params.assetIdentifier);
368
- appendSearchParam(searchParams, "sender", params.sender);
369
- appendSearchParam(searchParams, "recipient", params.recipient);
370
- appendSearchParam(searchParams, "from_height", params.fromHeight);
371
- appendSearchParam(searchParams, "to_height", params.toHeight);
372
- const query = searchParams.toString();
373
- return this.request("GET", `/v1/index/nft-transfers${query ? `?${query}` : ""}`);
369
+ return this.request("GET", `/v1/index/nft-transfers${buildQuery({
370
+ cursor: params.cursor,
371
+ from_cursor: params.fromCursor,
372
+ limit: params.limit,
373
+ contract_id: params.contractId,
374
+ asset_identifier: params.assetIdentifier,
375
+ sender: params.sender,
376
+ recipient: params.recipient,
377
+ from_height: params.fromHeight,
378
+ to_height: params.toHeight
379
+ })}`);
374
380
  }
375
381
  async* walkFtTransfers(params = {}) {
376
382
  const batchSize = params.batchSize ?? 200;
@@ -423,18 +429,18 @@ class Index extends BaseClient {
423
429
  }
424
430
  }
425
431
  async listEvents(params) {
426
- const searchParams = new URLSearchParams;
427
- appendSearchParam(searchParams, "event_type", params.eventType);
428
- appendSearchParam(searchParams, "cursor", params.cursor);
429
- appendSearchParam(searchParams, "from_cursor", params.fromCursor);
430
- appendSearchParam(searchParams, "limit", params.limit);
431
- appendSearchParam(searchParams, "contract_id", params.contractId);
432
- appendSearchParam(searchParams, "asset_identifier", params.assetIdentifier);
433
- appendSearchParam(searchParams, "sender", params.sender);
434
- appendSearchParam(searchParams, "recipient", params.recipient);
435
- appendSearchParam(searchParams, "from_height", params.fromHeight);
436
- appendSearchParam(searchParams, "to_height", params.toHeight);
437
- return this.request("GET", `/v1/index/events?${searchParams.toString()}`);
432
+ return this.request("GET", `/v1/index/events${buildQuery({
433
+ event_type: params.eventType,
434
+ cursor: params.cursor,
435
+ from_cursor: params.fromCursor,
436
+ limit: params.limit,
437
+ contract_id: params.contractId,
438
+ asset_identifier: params.assetIdentifier,
439
+ sender: params.sender,
440
+ recipient: params.recipient,
441
+ from_height: params.fromHeight,
442
+ to_height: params.toHeight
443
+ })}`);
438
444
  }
439
445
  async* walkEvents(params) {
440
446
  const batchSize = params.batchSize ?? 200;
@@ -462,17 +468,16 @@ class Index extends BaseClient {
462
468
  }
463
469
  }
464
470
  async listContractCalls(params = {}) {
465
- const searchParams = new URLSearchParams;
466
- appendSearchParam(searchParams, "cursor", params.cursor);
467
- appendSearchParam(searchParams, "from_cursor", params.fromCursor);
468
- appendSearchParam(searchParams, "limit", params.limit);
469
- appendSearchParam(searchParams, "contract_id", params.contractId);
470
- appendSearchParam(searchParams, "function_name", params.functionName);
471
- appendSearchParam(searchParams, "sender", params.sender);
472
- appendSearchParam(searchParams, "from_height", params.fromHeight);
473
- appendSearchParam(searchParams, "to_height", params.toHeight);
474
- const query = searchParams.toString();
475
- return this.request("GET", `/v1/index/contract-calls${query ? `?${query}` : ""}`);
471
+ return this.request("GET", `/v1/index/contract-calls${buildQuery({
472
+ cursor: params.cursor,
473
+ from_cursor: params.fromCursor,
474
+ limit: params.limit,
475
+ contract_id: params.contractId,
476
+ function_name: params.functionName,
477
+ sender: params.sender,
478
+ from_height: params.fromHeight,
479
+ to_height: params.toHeight
480
+ })}`);
476
481
  }
477
482
  async* walkContractCalls(params = {}) {
478
483
  const batchSize = params.batchSize ?? 200;
@@ -504,7 +509,74 @@ class Index extends BaseClient {
504
509
  // src/streams/client.ts
505
510
  import { ed25519 } from "@secondlayer/shared";
506
511
 
512
+ // src/streams/errors.ts
513
+ class AuthError extends Error {
514
+ status = 401;
515
+ constructor(message = "API key invalid or expired.") {
516
+ super(message);
517
+ this.name = "AuthError";
518
+ }
519
+ }
520
+
521
+ class RateLimitError extends Error {
522
+ retryAfter;
523
+ status = 429;
524
+ constructor(message = "Rate limited. Try again later.", retryAfter) {
525
+ super(message);
526
+ this.retryAfter = retryAfter;
527
+ this.name = "RateLimitError";
528
+ }
529
+ }
530
+
531
+ class ValidationError extends Error {
532
+ status;
533
+ body;
534
+ constructor(message, status, body) {
535
+ super(message);
536
+ this.status = status;
537
+ this.body = body;
538
+ this.name = "ValidationError";
539
+ }
540
+ }
541
+
542
+ class StreamsServerError extends Error {
543
+ status;
544
+ body;
545
+ constructor(message, status, body) {
546
+ super(message);
547
+ this.status = status;
548
+ this.body = body;
549
+ this.name = "StreamsServerError";
550
+ }
551
+ }
552
+
553
+ class StreamsSignatureError extends Error {
554
+ constructor(message = "Streams response signature verification failed.") {
555
+ super(message);
556
+ this.name = "StreamsSignatureError";
557
+ }
558
+ }
559
+
560
+ // src/streams/cursor.ts
561
+ var Cursor = {
562
+ atHeight(height) {
563
+ return `${height}:0`;
564
+ },
565
+ parse(cursor) {
566
+ const parts = cursor.split(":");
567
+ const blockHeight = Number(parts[0]);
568
+ const eventIndex = Number(parts[1]);
569
+ if (parts.length !== 2 || !Number.isInteger(blockHeight) || !Number.isInteger(eventIndex)) {
570
+ throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
571
+ }
572
+ return { blockHeight, eventIndex };
573
+ }
574
+ };
575
+
507
576
  // src/streams/consumer.ts
577
+ function reorgKey(reorg) {
578
+ return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
579
+ }
508
580
  async function defaultSleep(ms, signal) {
509
581
  if (signal?.aborted)
510
582
  return;
@@ -521,10 +593,12 @@ async function defaultSleep(ms, signal) {
521
593
  async function consumeStreamsEvents(opts) {
522
594
  const sleep = opts.sleep ?? defaultSleep;
523
595
  const mode = opts.mode ?? "tail";
596
+ const finalizedOnly = opts.finalizedOnly ?? false;
524
597
  const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
525
598
  const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
526
599
  const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
527
600
  let cursor = opts.fromCursor ?? null;
601
+ const handledReorgs = new Set;
528
602
  let pages = 0;
529
603
  let emptyPolls = 0;
530
604
  while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
@@ -539,14 +613,32 @@ async function consumeStreamsEvents(opts) {
539
613
  assetIdentifier: opts.assetIdentifier
540
614
  });
541
615
  pages++;
542
- const returnedCursor = await opts.onBatch(envelope.events, envelope);
543
- const nextCursor = returnedCursor ?? envelope.next_cursor;
616
+ if (!finalizedOnly && opts.onReorg) {
617
+ const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorgKey(reorg))).sort((a, b) => a.fork_point_height - b.fork_point_height);
618
+ if (fresh.length > 0) {
619
+ const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
620
+ const rewind = Cursor.atHeight(forkPoint);
621
+ for (const reorg of fresh) {
622
+ await opts.onReorg(reorg, { cursor: rewind });
623
+ handledReorgs.add(reorgKey(reorg));
624
+ }
625
+ cursor = rewind;
626
+ emptyPolls = 0;
627
+ continue;
628
+ }
629
+ }
630
+ const emitted = finalizedOnly ? envelope.events.filter((event) => event.finalized) : envelope.events;
631
+ const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
632
+ const returnedCursor = await opts.onBatch(emitted, envelope, {
633
+ cursor: checkpoint
634
+ });
635
+ const nextCursor = returnedCursor ?? checkpoint;
544
636
  if (nextCursor && nextCursor !== cursor) {
545
637
  cursor = nextCursor;
546
638
  emptyPolls = 0;
547
639
  continue;
548
640
  }
549
- if (envelope.events.length === 0) {
641
+ if (emitted.length === 0) {
550
642
  emptyPolls++;
551
643
  if (mode === "bounded") {
552
644
  return { cursor, pages, emptyPolls };
@@ -602,56 +694,6 @@ async function* streamStreamsEvents(opts) {
602
694
 
603
695
  // src/streams/dumps.ts
604
696
  import { createHash } from "node:crypto";
605
-
606
- // src/streams/errors.ts
607
- class AuthError extends Error {
608
- status = 401;
609
- constructor(message = "API key invalid or expired.") {
610
- super(message);
611
- this.name = "AuthError";
612
- }
613
- }
614
-
615
- class RateLimitError extends Error {
616
- retryAfter;
617
- status = 429;
618
- constructor(message = "Rate limited. Try again later.", retryAfter) {
619
- super(message);
620
- this.retryAfter = retryAfter;
621
- this.name = "RateLimitError";
622
- }
623
- }
624
-
625
- class ValidationError extends Error {
626
- status;
627
- body;
628
- constructor(message, status, body) {
629
- super(message);
630
- this.status = status;
631
- this.body = body;
632
- this.name = "ValidationError";
633
- }
634
- }
635
-
636
- class StreamsServerError extends Error {
637
- status;
638
- body;
639
- constructor(message, status, body) {
640
- super(message);
641
- this.status = status;
642
- this.body = body;
643
- this.name = "StreamsServerError";
644
- }
645
- }
646
-
647
- class StreamsSignatureError extends Error {
648
- constructor(message = "Streams response signature verification failed.") {
649
- super(message);
650
- this.name = "StreamsSignatureError";
651
- }
652
- }
653
-
654
- // src/streams/dumps.ts
655
697
  function createStreamsDumps(opts) {
656
698
  const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
657
699
  function requireBaseUrl() {
@@ -690,8 +732,12 @@ function createStreamsDumps(opts) {
690
732
  function cursorTuple(cursor) {
691
733
  if (!cursor)
692
734
  return [-1, -1];
693
- const [block, index] = cursor.split(":");
694
- return [Number(block), Number(index)];
735
+ const parts = cursor.split(":");
736
+ const [block, index] = parts.map(Number);
737
+ if (parts.length !== 2 || !Number.isInteger(block) || !Number.isInteger(index)) {
738
+ throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
739
+ }
740
+ return [block, index];
695
741
  }
696
742
  function maxCursor(a, b) {
697
743
  const [ah, ai] = cursorTuple(a);
@@ -702,18 +748,6 @@ var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
702
748
  function normalizeBaseUrl(baseUrl) {
703
749
  return baseUrl.replace(/\/+$/, "");
704
750
  }
705
- function appendSearchParam2(params, name, value) {
706
- if (value === undefined || value === null)
707
- return;
708
- params.set(name, String(value));
709
- }
710
- function appendListParam(params, name, value) {
711
- if (value === undefined || value === null)
712
- return;
713
- const joined = Array.isArray(value) ? value.join(",") : value;
714
- if (joined.length > 0)
715
- params.set(name, joined);
716
- }
717
751
  async function responseBody(response) {
718
752
  const text = await response.text();
719
753
  if (text.length === 0)
@@ -835,23 +869,18 @@ function createStreamsClient(options) {
835
869
  });
836
870
  };
837
871
  async function listEvents(params = {}) {
838
- const searchParams = new URLSearchParams;
839
- appendSearchParam2(searchParams, "cursor", params.cursor);
840
- appendSearchParam2(searchParams, "from_height", params.fromHeight);
841
- appendSearchParam2(searchParams, "to_height", params.toHeight);
842
- appendSearchParam2(searchParams, "limit", params.limit);
843
- appendListParam(searchParams, "contract_id", params.contractId);
844
- appendListParam(searchParams, "sender", params.sender);
845
- appendListParam(searchParams, "recipient", params.recipient);
846
- appendSearchParam2(searchParams, "asset_identifier", params.assetIdentifier);
847
- if (params.types?.length) {
848
- searchParams.set("types", params.types.join(","));
849
- }
850
- if (params.notTypes?.length) {
851
- searchParams.set("not_types", params.notTypes.join(","));
852
- }
853
- const query = searchParams.toString();
854
- return request(`/v1/streams/events${query ? `?${query}` : ""}`);
872
+ return request(`/v1/streams/events${buildQuery({
873
+ cursor: params.cursor,
874
+ from_height: params.fromHeight,
875
+ to_height: params.toHeight,
876
+ limit: params.limit,
877
+ contract_id: params.contractId,
878
+ sender: params.sender,
879
+ recipient: params.recipient,
880
+ asset_identifier: params.assetIdentifier,
881
+ types: params.types,
882
+ not_types: params.notTypes
883
+ })}`);
855
884
  }
856
885
  return {
857
886
  events: {
@@ -863,6 +892,7 @@ function createStreamsClient(options) {
863
892
  return consumeStreamsEvents({
864
893
  fromCursor: params.fromCursor,
865
894
  mode: params.mode,
895
+ finalizedOnly: params.finalizedOnly,
866
896
  types: params.types,
867
897
  notTypes: params.notTypes,
868
898
  contractId: params.contractId,
@@ -872,6 +902,7 @@ function createStreamsClient(options) {
872
902
  batchSize: params.batchSize ?? 100,
873
903
  fetchEvents,
874
904
  onBatch: params.onBatch,
905
+ onReorg: params.onReorg,
875
906
  emptyBackoffMs: params.emptyBackoffMs,
876
907
  maxPages: params.maxPages,
877
908
  maxEmptyPolls: params.maxEmptyPolls,
@@ -926,11 +957,10 @@ function createStreamsClient(options) {
926
957
  },
927
958
  reorgs: {
928
959
  list(params) {
929
- const searchParams = new URLSearchParams;
930
- appendSearchParam2(searchParams, "since", params.since);
931
- appendSearchParam2(searchParams, "limit", params.limit);
932
- const query = searchParams.toString();
933
- return request(`/v1/streams/reorgs${query ? `?${query}` : ""}`);
960
+ return request(`/v1/streams/reorgs${buildQuery({
961
+ since: params.since,
962
+ limit: params.limit
963
+ })}`);
934
964
  }
935
965
  },
936
966
  dumps,
@@ -1017,5 +1047,5 @@ export {
1017
1047
  Subgraphs
1018
1048
  };
1019
1049
 
1020
- //# debugId=9589AE42EB31EABA64756E2164756E21
1050
+ //# debugId=CDC19E41AE11FF9164756E2164756E21
1021
1051
  //# sourceMappingURL=index.js.map