@secondlayer/sdk 6.22.0 → 6.23.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.js CHANGED
@@ -442,6 +442,280 @@ class Contracts extends BaseClient {
442
442
  }
443
443
  }
444
444
 
445
+ // src/streams/errors.ts
446
+ class AuthError extends Error {
447
+ status = 401;
448
+ constructor(message = "API key invalid or expired.") {
449
+ super(message);
450
+ this.name = "AuthError";
451
+ }
452
+ }
453
+
454
+ class RateLimitError extends Error {
455
+ retryAfter;
456
+ status = 429;
457
+ constructor(message = "Rate limited. Try again later.", retryAfter) {
458
+ super(message);
459
+ this.retryAfter = retryAfter;
460
+ this.name = "RateLimitError";
461
+ }
462
+ }
463
+
464
+ class ValidationError extends Error {
465
+ status;
466
+ body;
467
+ constructor(message, status, body) {
468
+ super(message);
469
+ this.status = status;
470
+ this.body = body;
471
+ this.name = "ValidationError";
472
+ }
473
+ }
474
+
475
+ class StreamsServerError extends Error {
476
+ status;
477
+ body;
478
+ constructor(message, status, body) {
479
+ super(message);
480
+ this.status = status;
481
+ this.body = body;
482
+ this.name = "StreamsServerError";
483
+ }
484
+ }
485
+
486
+ class StreamsSignatureError extends Error {
487
+ constructor(message = "Streams response signature verification failed.") {
488
+ super(message);
489
+ this.name = "StreamsSignatureError";
490
+ }
491
+ }
492
+
493
+ // src/streams/cursor.ts
494
+ var Cursor = {
495
+ atHeight(height) {
496
+ return `${height}:0`;
497
+ },
498
+ parse(cursor) {
499
+ const parts = cursor.split(":");
500
+ const blockHeight = Number(parts[0]);
501
+ const eventIndex = Number(parts[1]);
502
+ if (parts.length !== 2 || !Number.isInteger(blockHeight) || !Number.isInteger(eventIndex)) {
503
+ throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
504
+ }
505
+ return { blockHeight, eventIndex };
506
+ }
507
+ };
508
+
509
+ // src/streams/consumer.ts
510
+ function reorgKey(reorg) {
511
+ return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
512
+ }
513
+ async function defaultSleep(ms, signal) {
514
+ if (signal?.aborted)
515
+ return;
516
+ await new Promise((resolve) => {
517
+ const timeout = setTimeout(resolve, ms);
518
+ if (!signal)
519
+ return;
520
+ signal.addEventListener("abort", () => {
521
+ clearTimeout(timeout);
522
+ resolve();
523
+ }, { once: true });
524
+ });
525
+ }
526
+ async function consumeStreamsEvents(opts) {
527
+ const sleep = opts.sleep ?? defaultSleep;
528
+ const mode = opts.mode ?? "tail";
529
+ const finalizedOnly = opts.finalizedOnly ?? false;
530
+ const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
531
+ const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
532
+ const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
533
+ let cursor = opts.fromCursor ?? null;
534
+ const handledReorgs = new Set;
535
+ let pages = 0;
536
+ let emptyPolls = 0;
537
+ while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
538
+ const envelope = await opts.fetchEvents({
539
+ cursor,
540
+ limit: opts.batchSize,
541
+ types: opts.types,
542
+ notTypes: opts.notTypes,
543
+ contractId: opts.contractId,
544
+ sender: opts.sender,
545
+ recipient: opts.recipient,
546
+ assetIdentifier: opts.assetIdentifier
547
+ });
548
+ pages++;
549
+ if (!finalizedOnly && opts.onReorg) {
550
+ const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorgKey(reorg))).sort((a, b) => a.fork_point_height - b.fork_point_height);
551
+ if (fresh.length > 0) {
552
+ const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
553
+ const rewind = Cursor.atHeight(forkPoint);
554
+ for (const reorg of fresh) {
555
+ await opts.onReorg(reorg, { cursor: rewind });
556
+ handledReorgs.add(reorgKey(reorg));
557
+ }
558
+ cursor = rewind;
559
+ emptyPolls = 0;
560
+ continue;
561
+ }
562
+ }
563
+ const emitted = finalizedOnly ? envelope.events.filter((event) => event.finalized) : envelope.events;
564
+ const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
565
+ const returnedCursor = await opts.onBatch(emitted, envelope, {
566
+ cursor: checkpoint
567
+ });
568
+ const nextCursor = returnedCursor ?? checkpoint;
569
+ if (nextCursor && nextCursor !== cursor) {
570
+ cursor = nextCursor;
571
+ emptyPolls = 0;
572
+ continue;
573
+ }
574
+ if (emitted.length === 0) {
575
+ emptyPolls++;
576
+ if (mode === "bounded") {
577
+ return { cursor, pages, emptyPolls };
578
+ }
579
+ await sleep(emptyBackoffMs, opts.signal);
580
+ continue;
581
+ }
582
+ return { cursor, pages, emptyPolls };
583
+ }
584
+ return { cursor, pages, emptyPolls };
585
+ }
586
+ async function* iterateStreamsBatches(opts) {
587
+ const sleep = opts.sleep ?? defaultSleep;
588
+ let cursor = opts.fromCursor ?? null;
589
+ while (!opts.signal?.aborted) {
590
+ const envelope = await opts.fetchEvents({
591
+ cursor,
592
+ limit: opts.batchSize,
593
+ types: opts.types,
594
+ notTypes: opts.notTypes,
595
+ contractId: opts.contractId,
596
+ sender: opts.sender,
597
+ recipient: opts.recipient,
598
+ assetIdentifier: opts.assetIdentifier
599
+ });
600
+ const checkpoint = envelope.next_cursor ?? cursor;
601
+ if (envelope.events.length > 0 || envelope.reorgs.length > 0) {
602
+ yield {
603
+ events: envelope.events,
604
+ cursor: checkpoint,
605
+ tip: envelope.tip,
606
+ reorgs: envelope.reorgs
607
+ };
608
+ }
609
+ const advanced = checkpoint !== null && checkpoint !== cursor;
610
+ cursor = checkpoint;
611
+ if (!advanced && envelope.events.length === 0) {
612
+ if (opts.signal?.aborted)
613
+ return;
614
+ await sleep(opts.intervalMs, opts.signal);
615
+ }
616
+ }
617
+ }
618
+ async function* streamStreamsEvents(opts) {
619
+ const sleep = opts.sleep ?? defaultSleep;
620
+ const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
621
+ const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
622
+ const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
623
+ let cursor = opts.fromCursor ?? null;
624
+ let pages = 0;
625
+ let emptyPolls = 0;
626
+ while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
627
+ const envelope = await opts.fetchEvents({
628
+ cursor,
629
+ limit: opts.batchSize,
630
+ types: opts.types,
631
+ notTypes: opts.notTypes,
632
+ contractId: opts.contractId,
633
+ sender: opts.sender,
634
+ recipient: opts.recipient,
635
+ assetIdentifier: opts.assetIdentifier
636
+ });
637
+ pages++;
638
+ for (const event of envelope.events) {
639
+ if (opts.signal?.aborted)
640
+ return;
641
+ yield event;
642
+ }
643
+ const nextCursor = envelope.next_cursor;
644
+ if (nextCursor && nextCursor !== cursor) {
645
+ cursor = nextCursor;
646
+ emptyPolls = 0;
647
+ continue;
648
+ }
649
+ if (envelope.events.length === 0) {
650
+ emptyPolls++;
651
+ if (emptyPolls >= maxEmptyPolls || pages >= maxPages)
652
+ return;
653
+ await sleep(emptyBackoffMs, opts.signal);
654
+ continue;
655
+ }
656
+ return;
657
+ }
658
+ }
659
+
660
+ // src/index-api/consumer.ts
661
+ async function consumeIndexFeed(opts) {
662
+ const sleep = opts.sleep ?? defaultSleep;
663
+ const mode = opts.mode ?? "tail";
664
+ const finalizedOnly = opts.finalizedOnly ?? false;
665
+ const batchSize = opts.batchSize ?? 200;
666
+ const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
667
+ const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
668
+ const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
669
+ let cursor = opts.fromCursor ?? null;
670
+ const handledReorgs = new Set;
671
+ let pages = 0;
672
+ let emptyPolls = 0;
673
+ while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
674
+ const envelope = await opts.fetchPage({
675
+ cursor,
676
+ fromHeight: cursor === null ? opts.fromHeight : undefined,
677
+ limit: batchSize
678
+ });
679
+ pages++;
680
+ if (!finalizedOnly && opts.onReorg) {
681
+ const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorg.id)).sort((a, b) => a.fork_point_height - b.fork_point_height);
682
+ if (fresh.length > 0) {
683
+ const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
684
+ const rewind = Cursor.atHeight(forkPoint);
685
+ for (const reorg of fresh) {
686
+ await opts.onReorg(reorg, { cursor: rewind });
687
+ handledReorgs.add(reorg.id);
688
+ }
689
+ cursor = rewind;
690
+ emptyPolls = 0;
691
+ continue;
692
+ }
693
+ }
694
+ const items = opts.itemsOf(envelope);
695
+ const emitted = finalizedOnly ? items.filter((item) => item.block_height <= envelope.tip.finalized_height) : items;
696
+ const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
697
+ const returnedCursor = await opts.onBatch(emitted, envelope, {
698
+ cursor: checkpoint
699
+ });
700
+ const nextCursor = returnedCursor ?? checkpoint;
701
+ if (nextCursor && nextCursor !== cursor) {
702
+ cursor = nextCursor;
703
+ emptyPolls = 0;
704
+ continue;
705
+ }
706
+ if (emitted.length === 0) {
707
+ emptyPolls++;
708
+ if (mode === "bounded") {
709
+ return { cursor, pages, emptyPolls };
710
+ }
711
+ await sleep(emptyBackoffMs, opts.signal);
712
+ continue;
713
+ }
714
+ return { cursor, pages, emptyPolls };
715
+ }
716
+ return { cursor, pages, emptyPolls };
717
+ }
718
+
445
719
  // src/index-api/client.ts
446
720
  function firstWalkFromHeight(params) {
447
721
  if (params.fromHeight !== undefined)
@@ -480,11 +754,41 @@ class Index extends BaseClient {
480
754
  });
481
755
  events = Object.assign((params) => this.listEvents(params), {
482
756
  list: (params) => this.listEvents(params),
483
- walk: (params) => this.walkEvents(params)
757
+ walk: (params) => this.walkEvents(params),
758
+ consume: (params) => consumeIndexFeed({
759
+ ...params,
760
+ fetchPage: ({ cursor, fromHeight, limit }) => this.listEvents({
761
+ eventType: params.eventType,
762
+ contractId: params.contractId,
763
+ assetIdentifier: params.assetIdentifier,
764
+ sender: params.sender,
765
+ recipient: params.recipient,
766
+ trait: params.trait,
767
+ toHeight: params.toHeight,
768
+ cursor,
769
+ fromHeight,
770
+ limit
771
+ }),
772
+ itemsOf: (envelope) => envelope.events
773
+ })
484
774
  });
485
775
  contractCalls = {
486
776
  list: (params = {}) => this.listContractCalls(params),
487
- walk: (params = {}) => this.walkContractCalls(params)
777
+ walk: (params = {}) => this.walkContractCalls(params),
778
+ consume: (params) => consumeIndexFeed({
779
+ ...params,
780
+ fetchPage: ({ cursor, fromHeight, limit }) => this.listContractCalls({
781
+ contractId: params.contractId,
782
+ functionName: params.functionName,
783
+ sender: params.sender,
784
+ trait: params.trait,
785
+ toHeight: params.toHeight,
786
+ cursor,
787
+ fromHeight,
788
+ limit
789
+ }),
790
+ itemsOf: (envelope) => envelope.contract_calls
791
+ })
488
792
  };
489
793
  canonical = {
490
794
  list: (params = {}) => this.listCanonical(params),
@@ -905,221 +1209,6 @@ class Projects extends BaseClient {
905
1209
  // src/streams/client.ts
906
1210
  import { ed25519 as ed255192 } from "@secondlayer/shared";
907
1211
 
908
- // src/streams/errors.ts
909
- class AuthError extends Error {
910
- status = 401;
911
- constructor(message = "API key invalid or expired.") {
912
- super(message);
913
- this.name = "AuthError";
914
- }
915
- }
916
-
917
- class RateLimitError extends Error {
918
- retryAfter;
919
- status = 429;
920
- constructor(message = "Rate limited. Try again later.", retryAfter) {
921
- super(message);
922
- this.retryAfter = retryAfter;
923
- this.name = "RateLimitError";
924
- }
925
- }
926
-
927
- class ValidationError extends Error {
928
- status;
929
- body;
930
- constructor(message, status, body) {
931
- super(message);
932
- this.status = status;
933
- this.body = body;
934
- this.name = "ValidationError";
935
- }
936
- }
937
-
938
- class StreamsServerError extends Error {
939
- status;
940
- body;
941
- constructor(message, status, body) {
942
- super(message);
943
- this.status = status;
944
- this.body = body;
945
- this.name = "StreamsServerError";
946
- }
947
- }
948
-
949
- class StreamsSignatureError extends Error {
950
- constructor(message = "Streams response signature verification failed.") {
951
- super(message);
952
- this.name = "StreamsSignatureError";
953
- }
954
- }
955
-
956
- // src/streams/cursor.ts
957
- var Cursor = {
958
- atHeight(height) {
959
- return `${height}:0`;
960
- },
961
- parse(cursor) {
962
- const parts = cursor.split(":");
963
- const blockHeight = Number(parts[0]);
964
- const eventIndex = Number(parts[1]);
965
- if (parts.length !== 2 || !Number.isInteger(blockHeight) || !Number.isInteger(eventIndex)) {
966
- throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
967
- }
968
- return { blockHeight, eventIndex };
969
- }
970
- };
971
-
972
- // src/streams/consumer.ts
973
- function reorgKey(reorg) {
974
- return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
975
- }
976
- async function defaultSleep(ms, signal) {
977
- if (signal?.aborted)
978
- return;
979
- await new Promise((resolve) => {
980
- const timeout = setTimeout(resolve, ms);
981
- if (!signal)
982
- return;
983
- signal.addEventListener("abort", () => {
984
- clearTimeout(timeout);
985
- resolve();
986
- }, { once: true });
987
- });
988
- }
989
- async function consumeStreamsEvents(opts) {
990
- const sleep = opts.sleep ?? defaultSleep;
991
- const mode = opts.mode ?? "tail";
992
- const finalizedOnly = opts.finalizedOnly ?? false;
993
- const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
994
- const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
995
- const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
996
- let cursor = opts.fromCursor ?? null;
997
- const handledReorgs = new Set;
998
- let pages = 0;
999
- let emptyPolls = 0;
1000
- while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
1001
- const envelope = await opts.fetchEvents({
1002
- cursor,
1003
- limit: opts.batchSize,
1004
- types: opts.types,
1005
- notTypes: opts.notTypes,
1006
- contractId: opts.contractId,
1007
- sender: opts.sender,
1008
- recipient: opts.recipient,
1009
- assetIdentifier: opts.assetIdentifier
1010
- });
1011
- pages++;
1012
- if (!finalizedOnly && opts.onReorg) {
1013
- const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorgKey(reorg))).sort((a, b) => a.fork_point_height - b.fork_point_height);
1014
- if (fresh.length > 0) {
1015
- const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
1016
- const rewind = Cursor.atHeight(forkPoint);
1017
- for (const reorg of fresh) {
1018
- await opts.onReorg(reorg, { cursor: rewind });
1019
- handledReorgs.add(reorgKey(reorg));
1020
- }
1021
- cursor = rewind;
1022
- emptyPolls = 0;
1023
- continue;
1024
- }
1025
- }
1026
- const emitted = finalizedOnly ? envelope.events.filter((event) => event.finalized) : envelope.events;
1027
- const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
1028
- const returnedCursor = await opts.onBatch(emitted, envelope, {
1029
- cursor: checkpoint
1030
- });
1031
- const nextCursor = returnedCursor ?? checkpoint;
1032
- if (nextCursor && nextCursor !== cursor) {
1033
- cursor = nextCursor;
1034
- emptyPolls = 0;
1035
- continue;
1036
- }
1037
- if (emitted.length === 0) {
1038
- emptyPolls++;
1039
- if (mode === "bounded") {
1040
- return { cursor, pages, emptyPolls };
1041
- }
1042
- await sleep(emptyBackoffMs, opts.signal);
1043
- continue;
1044
- }
1045
- return { cursor, pages, emptyPolls };
1046
- }
1047
- return { cursor, pages, emptyPolls };
1048
- }
1049
- async function* iterateStreamsBatches(opts) {
1050
- const sleep = opts.sleep ?? defaultSleep;
1051
- let cursor = opts.fromCursor ?? null;
1052
- while (!opts.signal?.aborted) {
1053
- const envelope = await opts.fetchEvents({
1054
- cursor,
1055
- limit: opts.batchSize,
1056
- types: opts.types,
1057
- notTypes: opts.notTypes,
1058
- contractId: opts.contractId,
1059
- sender: opts.sender,
1060
- recipient: opts.recipient,
1061
- assetIdentifier: opts.assetIdentifier
1062
- });
1063
- const checkpoint = envelope.next_cursor ?? cursor;
1064
- if (envelope.events.length > 0 || envelope.reorgs.length > 0) {
1065
- yield {
1066
- events: envelope.events,
1067
- cursor: checkpoint,
1068
- tip: envelope.tip,
1069
- reorgs: envelope.reorgs
1070
- };
1071
- }
1072
- const advanced = checkpoint !== null && checkpoint !== cursor;
1073
- cursor = checkpoint;
1074
- if (!advanced && envelope.events.length === 0) {
1075
- if (opts.signal?.aborted)
1076
- return;
1077
- await sleep(opts.intervalMs, opts.signal);
1078
- }
1079
- }
1080
- }
1081
- async function* streamStreamsEvents(opts) {
1082
- const sleep = opts.sleep ?? defaultSleep;
1083
- const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
1084
- const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
1085
- const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
1086
- let cursor = opts.fromCursor ?? null;
1087
- let pages = 0;
1088
- let emptyPolls = 0;
1089
- while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
1090
- const envelope = await opts.fetchEvents({
1091
- cursor,
1092
- limit: opts.batchSize,
1093
- types: opts.types,
1094
- notTypes: opts.notTypes,
1095
- contractId: opts.contractId,
1096
- sender: opts.sender,
1097
- recipient: opts.recipient,
1098
- assetIdentifier: opts.assetIdentifier
1099
- });
1100
- pages++;
1101
- for (const event of envelope.events) {
1102
- if (opts.signal?.aborted)
1103
- return;
1104
- yield event;
1105
- }
1106
- const nextCursor = envelope.next_cursor;
1107
- if (nextCursor && nextCursor !== cursor) {
1108
- cursor = nextCursor;
1109
- emptyPolls = 0;
1110
- continue;
1111
- }
1112
- if (envelope.events.length === 0) {
1113
- emptyPolls++;
1114
- if (emptyPolls >= maxEmptyPolls || pages >= maxPages)
1115
- return;
1116
- await sleep(emptyBackoffMs, opts.signal);
1117
- continue;
1118
- }
1119
- return;
1120
- }
1121
- }
1122
-
1123
1212
  // src/streams/dumps.ts
1124
1213
  import { createHash } from "node:crypto";
1125
1214
  import { verifyStreamsBulkManifestSignature } from "@secondlayer/shared/streams-bulk-manifest";
@@ -1132,7 +1221,15 @@ function createStreamsDumps(opts) {
1132
1221
  return baseUrl;
1133
1222
  }
1134
1223
  function fileUrl(file) {
1135
- return `${requireBaseUrl()}/${file.path.replace(/^\/+/, "")}`;
1224
+ const base = requireBaseUrl();
1225
+ const path = file.path.replace(/^\/+/, "");
1226
+ try {
1227
+ const basePath = new URL(base).pathname.replace(/^\/+|\/+$/g, "");
1228
+ if (basePath && path.startsWith(`${basePath}/`)) {
1229
+ return `${base}/${path.slice(basePath.length + 1)}`;
1230
+ }
1231
+ } catch {}
1232
+ return `${base}/${path}`;
1136
1233
  }
1137
1234
  async function list() {
1138
1235
  const url = `${requireBaseUrl()}/manifest/latest.json`;
@@ -2382,6 +2479,7 @@ export {
2382
2479
  decodeClarityValue,
2383
2480
  createX402Client,
2384
2481
  createStreamsClient,
2482
+ consumeIndexFeed,
2385
2483
  buildSignedX402Payment,
2386
2484
  X402SpendGuardError,
2387
2485
  VersionConflictError,
@@ -2403,5 +2501,5 @@ export {
2403
2501
  ApiError
2404
2502
  };
2405
2503
 
2406
- //# debugId=E3A93F3CA875072D64756E2164756E21
2504
+ //# debugId=949AD5368987433264756E2164756E21
2407
2505
  //# sourceMappingURL=index.js.map