@secondlayer/sdk 13.1.0 → 13.2.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.
@@ -603,7 +603,7 @@ type WithSinkTx<TTx> = [TTx] extends [never] ? unknown : {
603
603
  tx: TTx;
604
604
  };
605
605
  import { VmEventType } from "@secondlayer/shared";
606
- import { StreamsEvent, StreamsEventType, StreamsWireEvent, VmStreamsEvent } from "@secondlayer/shared/streams-rows";
606
+ import { RuneEventType, RuneStreamsEvent, StreamsEvent, StreamsEventType, StreamsWireEvent, VmStreamsEvent } from "@secondlayer/shared/streams-rows";
607
607
  type StreamsTip = {
608
608
  block_height: number;
609
609
  block_hash: string;
@@ -623,6 +623,15 @@ type StreamsTip = {
623
623
  /** Oldest seekable cursor (`<oldest_seekable_height>:0`); `null` = unlimited. */
624
624
  oldest_cursor?: string | null;
625
625
  };
626
+ /** `chain=bitcoin` tip (plan 059) — no `burn_block_height`/retention fields,
627
+ * since Bitcoin has no burn chain of its own and Runes reads have no
628
+ * retention ladder. Returned by `tip({ chain: "bitcoin" })`. */
629
+ type StreamsBitcoinTip = {
630
+ block_height: number;
631
+ block_hash: string;
632
+ finalized_height: number;
633
+ lag_seconds: number;
634
+ };
626
635
  type StreamsCanonicalBlock = {
627
636
  block_height: number;
628
637
  block_hash: string;
@@ -630,6 +639,12 @@ type StreamsCanonicalBlock = {
630
639
  burn_block_hash: string | null;
631
640
  is_canonical: true;
632
641
  };
642
+ /** `chain=bitcoin` canonical block (plan 059) — no burn-chain fields. */
643
+ type StreamsBitcoinCanonicalBlock = {
644
+ block_height: number;
645
+ block_hash: string;
646
+ is_canonical: true;
647
+ };
633
648
  type StreamsReorg = {
634
649
  detected_at: string;
635
650
  fork_point_height: number;
@@ -645,13 +660,19 @@ type StreamsReorg = {
645
660
  */
646
661
  new_canonical_tip: string;
647
662
  };
648
- type StreamsEventsEnvelope<TEvent = StreamsEvent> = {
663
+ type StreamsEventsEnvelope<
664
+ TEvent = StreamsEvent,
665
+ TTip = StreamsTip
666
+ > = {
649
667
  events: TEvent[];
650
668
  next_cursor: string | null;
651
- tip: StreamsTip;
669
+ tip: TTip;
652
670
  reorgs: StreamsReorg[];
653
671
  };
654
- type StreamsEventsListEnvelope = Omit<StreamsEventsEnvelope, "next_cursor">;
672
+ type StreamsEventsListEnvelope<
673
+ TEvent = StreamsEvent,
674
+ TTip = StreamsTip
675
+ > = Omit<StreamsEventsEnvelope<TEvent, TTip>, "next_cursor">;
655
676
  /** The StreamsEvent union narrowed to a `types` selection — what a
656
677
  * const-generic `types: ["ft_transfer"]` buys at the type level. */
657
678
  type StreamsEventOfTypes<T extends readonly StreamsEventType[]> = Extract<StreamsEvent, {
@@ -695,10 +716,16 @@ type StreamsEventForFilter<
695
716
  }> : Extract<StreamsEvent, {
696
717
  event_type: T;
697
718
  }> : D extends true ? IndexEvent : StreamsEvent;
698
- type StreamsEventsListParams = {
719
+ type StreamsEventsListParamsCommon = {
699
720
  cursor?: string | null;
700
721
  fromHeight?: number;
701
722
  toHeight?: number;
723
+ limit?: number;
724
+ };
725
+ /** `chain: "stacks"` (default, omit `chain` entirely) — unchanged since
726
+ * before plan 059. */
727
+ type StreamsEventsStacksListParams = StreamsEventsListParamsCommon & {
728
+ chain?: "stacks";
702
729
  /** `vm` reads ordinal. Omit for Streams 1.0. */
703
730
  clock?: "classic" | "vm";
704
731
  types?: readonly (StreamsEventType | VmEventType)[];
@@ -714,27 +741,37 @@ type StreamsEventsListParams = {
714
741
  * unrelated concerns share one page, one cursor, one checkpoint.
715
742
  */
716
743
  filters?: StreamsFilterMap;
717
- limit?: number;
718
744
  };
719
- type StreamsEventsStreamParams = {
745
+ /**
746
+ * `chain: "bitcoin"` (plan 059) — Runes events, own cursor space. `types` is
747
+ * restricted to {@link RuneEventType} at the type level, so e.g.
748
+ * `{ chain: "bitcoin", types: ["ft_transfer"] }` is a compile error rather
749
+ * than a silent 400 at request time. `contractId`/`sender`/`recipient`/
750
+ * `assetIdentifier`/`filters`/`clock` don't exist on this branch — they're
751
+ * Stacks-only (the server 400s on them too).
752
+ */
753
+ type StreamsEventsBitcoinListParams = StreamsEventsListParamsCommon & {
754
+ chain: "bitcoin";
755
+ types?: readonly RuneEventType[];
756
+ notTypes?: readonly RuneEventType[];
757
+ /** A RuneRef: an id (`840000:3`) or a name (`DOG•GO•TO•THE•MOON`). */
758
+ rune?: string;
759
+ /** A mainnet address. */
760
+ address?: string;
761
+ };
762
+ type StreamsEventsListParams = StreamsEventsStacksListParams | StreamsEventsBitcoinListParams;
763
+ type StreamsEventsStreamCommon = {
720
764
  fromCursor?: string | null;
721
- types?: readonly StreamsEventType[];
722
- notTypes?: readonly StreamsEventType[];
723
- contractId?: StreamsFilterValue;
724
- sender?: StreamsFilterValue;
725
- recipient?: StreamsFilterValue;
726
- assetIdentifier?: string;
727
- /** Labelled filter groups; see {@link StreamsEventsListParams.filters}. */
728
- filters?: StreamsFilterMap;
729
765
  batchSize?: number;
730
766
  emptyBackoffMs?: number;
731
767
  maxPages?: number;
732
768
  maxEmptyPolls?: number;
733
769
  signal?: AbortSignal;
734
770
  };
735
- type StreamsEventsSubscribeParams = {
736
- /** Resume strictly after this cursor; omit to live-tail from the tip. */
737
- fromCursor?: string | null;
771
+ /** `chain: "stacks"` (default, omit `chain` entirely) — matches
772
+ * {@link StreamsEventsStacksListParams}. */
773
+ type StreamsEventsStreamStacksParams = StreamsEventsStreamCommon & {
774
+ chain?: "stacks";
738
775
  types?: readonly StreamsEventType[];
739
776
  notTypes?: readonly StreamsEventType[];
740
777
  contractId?: StreamsFilterValue;
@@ -743,15 +780,28 @@ type StreamsEventsSubscribeParams = {
743
780
  assetIdentifier?: string;
744
781
  /** Labelled filter groups; see {@link StreamsEventsListParams.filters}. */
745
782
  filters?: StreamsFilterMap;
783
+ };
784
+ /** `chain: "bitcoin"` (plan 059/061) — Runes events, tailed the same way as
785
+ * the Stacks default. `types` is restricted to {@link RuneEventType} at the
786
+ * type level, matching {@link StreamsEventsBitcoinListParams}.
787
+ * `contractId`/`sender`/`recipient`/`assetIdentifier`/`filters` don't exist
788
+ * on this branch — they're Stacks-only. */
789
+ type StreamsEventsStreamBitcoinParams = StreamsEventsStreamCommon & {
790
+ chain: "bitcoin";
791
+ types?: readonly RuneEventType[];
792
+ notTypes?: readonly RuneEventType[];
793
+ /** A RuneRef: an id (`840000:3`) or a name (`DOG•GO•TO•THE•MOON`). */
794
+ rune?: string;
795
+ /** A mainnet address. */
796
+ address?: string;
797
+ };
798
+ type StreamsEventsStreamParams = StreamsEventsStreamStacksParams | StreamsEventsStreamBitcoinParams;
799
+ type StreamsEventsSubscribeCommon = {
800
+ /** Resume strictly after this cursor; omit to live-tail from the tip. */
801
+ fromCursor?: string | null;
746
802
  /** Abort to unsubscribe (the returned function does the same). */
747
803
  signal?: AbortSignal;
748
804
  /**
749
- * Called for each pushed event, in order. The resume cursor advances only
750
- * after this resolves, so a handler that throws sees the same event again
751
- * on reconnect (at-least-once). Key durable writes by `cursor`.
752
- */
753
- onEvent: (event: StreamsEvent) => void | Promise<void>;
754
- /**
755
805
  * Called on every failure. Transport errors (dropped socket, 5xx, 429, a
756
806
  * stale connection, a throwing handler) reconnect from the last handled
757
807
  * cursor with exponential backoff. Errors that a retry cannot fix (401,
@@ -771,6 +821,37 @@ type StreamsEventsSubscribeParams = {
771
821
  */
772
822
  staleAfterMs?: number;
773
823
  };
824
+ /** `chain: "stacks"` (default, omit `chain` entirely). */
825
+ type StreamsEventsSubscribeStacksParams = StreamsEventsSubscribeCommon & {
826
+ chain?: "stacks";
827
+ types?: readonly StreamsEventType[];
828
+ notTypes?: readonly StreamsEventType[];
829
+ contractId?: StreamsFilterValue;
830
+ sender?: StreamsFilterValue;
831
+ recipient?: StreamsFilterValue;
832
+ assetIdentifier?: string;
833
+ /** Labelled filter groups; see {@link StreamsEventsListParams.filters}. */
834
+ filters?: StreamsFilterMap;
835
+ /**
836
+ * Called for each pushed event, in order. The resume cursor advances only
837
+ * after this resolves, so a handler that throws sees the same event again
838
+ * on reconnect (at-least-once). Key durable writes by `cursor`.
839
+ */
840
+ onEvent: (event: StreamsEvent) => void | Promise<void>;
841
+ };
842
+ /** `chain: "bitcoin"` (plan 059/061) — Runes events pushed live, on their own
843
+ * cursor space. `contractId`/`sender`/`recipient`/`assetIdentifier`/`filters`
844
+ * don't exist on this branch — they're Stacks-only. */
845
+ type StreamsEventsSubscribeBitcoinParams = StreamsEventsSubscribeCommon & {
846
+ chain: "bitcoin";
847
+ types?: readonly RuneEventType[];
848
+ notTypes?: readonly RuneEventType[];
849
+ /** A RuneRef: an id (`840000:3`) or a name (`DOG•GO•TO•THE•MOON`). */
850
+ rune?: string;
851
+ /** A mainnet address. */
852
+ address?: string;
853
+ onEvent: (event: RuneStreamsEvent) => void | Promise<void>;
854
+ };
774
855
  /**
775
856
  * Handle for a live subscription: call it to unsubscribe. `done` resolves
776
857
  * after unsubscribe (or the signal aborting) and rejects with the error that
@@ -855,6 +936,10 @@ type StreamsEventsConsumeParams<
855
936
  D extends boolean = false,
856
937
  F extends StreamsFilterMap = Record<never, never>
857
938
  > = {
939
+ /** `chain: "stacks"` (default, omit `chain` entirely) — the bitcoin branch
940
+ * is {@link StreamsEventsConsumeBitcoinParams}, a separate overload with
941
+ * no `filters`/`on`/`decoded` (Stacks-only concepts). */
942
+ chain?: "stacks";
858
943
  fromCursor?: string | null;
859
944
  /**
860
945
  * Deliver events pre-decoded as the flat, `event_type`-discriminated rows
@@ -963,22 +1048,74 @@ type StreamsEventsConsumeParams<
963
1048
  wake?: () => Promise<void>;
964
1049
  };
965
1050
  /**
1051
+ * `chain: "bitcoin"` (plan 059/061) — the checkpointed sweep over Runes
1052
+ * events. A separate, simpler type from {@link StreamsEventsConsumeParams}
1053
+ * rather than a widened union member: `filters`/`on`/`decoded` are Stacks-only
1054
+ * concepts (labelled OR-groups and Index-shaped projection have no Runes
1055
+ * equivalent yet), so `onBatch` is the only way in and is required. Reorg
1056
+ * rewind still works the same way (bitcoin reorgs carry the same
1057
+ * `fork_point_height` + `new_canonical_tip` shape as Stacks ones).
1058
+ */
1059
+ type StreamsEventsConsumeBitcoinParams<TTx = never> = {
1060
+ chain: "bitcoin";
1061
+ fromCursor?: string | null;
1062
+ sink?: ConsumerSink<TTx>;
1063
+ onProgress?: (ctx: ConsumerBatchContext) => void;
1064
+ mode?: "tail" | "bounded";
1065
+ finalizedOnly?: boolean;
1066
+ types?: readonly RuneEventType[];
1067
+ notTypes?: readonly RuneEventType[];
1068
+ /** A RuneRef: an id (`840000:3`) or a name (`DOG•GO•TO•THE•MOON`). */
1069
+ rune?: string;
1070
+ /** A mainnet address. */
1071
+ address?: string;
1072
+ batchSize?: number;
1073
+ onBatch: (events: RuneStreamsEvent[], envelope: StreamsEventsEnvelope<RuneStreamsEvent, StreamsBitcoinTip>, ctx: StreamsBatchContext & WithSinkTx<TTx>) => void | string | null | undefined | Promise<void> | Promise<string | null | undefined>;
1074
+ onReorg?: (reorg: StreamsReorg, ctx: StreamsReorgContext) => Promise<void> | void;
1075
+ maxRollbackDepth?: number;
1076
+ retryCount?: number;
1077
+ retryDelay?: number;
1078
+ onError?: (err: unknown, ctx: {
1079
+ attempt: number;
1080
+ retriesLeft: number;
1081
+ delayMs: number;
1082
+ }) => void;
1083
+ emptyBackoffMs?: number;
1084
+ maxPages?: number;
1085
+ maxEmptyPolls?: number;
1086
+ signal?: AbortSignal;
1087
+ wake?: () => Promise<void>;
1088
+ };
1089
+ /**
966
1090
  * One yielded page from {@link StreamsClient.consume} — the
967
1091
  * `GET /v1/streams/events` envelope verbatim, with `next_cursor` renamed to
968
1092
  * `cursor` (the checkpoint to persist and resume from).
969
1093
  */
970
- type StreamsBatch<TEvent = StreamsEvent> = {
1094
+ type StreamsBatch<
1095
+ TEvent = StreamsEvent,
1096
+ TTip = StreamsTip
1097
+ > = {
971
1098
  /** Canonical events of this page, in cursor order. */
972
1099
  events: TEvent[];
973
1100
  /** Checkpoint after this page — pass back as `consume({ cursor })` to resume. */
974
1101
  cursor: string | null;
975
- tip: StreamsTip;
1102
+ tip: TTip;
976
1103
  /** Chain reorgs reported alongside this page; empty when none. */
977
1104
  reorgs: StreamsReorg[];
978
1105
  };
979
- type StreamsConsumeParams = {
1106
+ type StreamsConsumeCommon = {
980
1107
  /** Resume strictly after this cursor; omit to start from the oldest seekable page. */
981
1108
  cursor?: string | null;
1109
+ /** Events per page (the `limit` query param). Default 100. */
1110
+ batchSize?: number;
1111
+ /** Poll interval while caught up at the tip, in ms. Default 2000. */
1112
+ intervalMs?: number;
1113
+ /** Abort to end the iteration. */
1114
+ signal?: AbortSignal;
1115
+ };
1116
+ /** `chain: "stacks"` (default, omit `chain` entirely). */
1117
+ type StreamsConsumeStacksParams = StreamsConsumeCommon & {
1118
+ chain?: "stacks";
982
1119
  types?: readonly StreamsEventType[];
983
1120
  notTypes?: readonly StreamsEventType[];
984
1121
  contractId?: StreamsFilterValue;
@@ -988,13 +1125,20 @@ type StreamsConsumeParams = {
988
1125
  /** Labelled OR-groups — same semantics as `events.consume`; each returned
989
1126
  * event echoes the labels it matched. */
990
1127
  filters?: StreamsFilterMap;
991
- /** Events per page (the `limit` query param). Default 100. */
992
- batchSize?: number;
993
- /** Poll interval while caught up at the tip, in ms. Default 2000. */
994
- intervalMs?: number;
995
- /** Abort to end the iteration. */
996
- signal?: AbortSignal;
997
1128
  };
1129
+ /** `chain: "bitcoin"` (plan 059/061) — Runes batches, own cursor space.
1130
+ * `contractId`/`sender`/`recipient`/`assetIdentifier`/`filters` don't exist
1131
+ * on this branch — they're Stacks-only. */
1132
+ type StreamsConsumeBitcoinParams = StreamsConsumeCommon & {
1133
+ chain: "bitcoin";
1134
+ types?: readonly RuneEventType[];
1135
+ notTypes?: readonly RuneEventType[];
1136
+ /** A RuneRef: an id (`840000:3`) or a name (`DOG•GO•TO•THE•MOON`). */
1137
+ rune?: string;
1138
+ /** A mainnet address. */
1139
+ address?: string;
1140
+ };
1141
+ type StreamsConsumeParams = StreamsConsumeStacksParams | StreamsConsumeBitcoinParams;
998
1142
  type StreamsEventsConsumeResult = {
999
1143
  cursor: string | null;
1000
1144
  pages: number;
@@ -1098,33 +1242,44 @@ type StreamsClient = {
1098
1242
  * rewound automatically — use `events.consume` with `onReorg` for managed
1099
1243
  * rollback semantics.
1100
1244
  */
1245
+ /** `chain: "bitcoin"` — batches carry Runes rows only, on the bitcoin tip
1246
+ * shape (plan 059/061). */
1247
+ consume(params: StreamsConsumeBitcoinParams): AsyncIterableIterator<StreamsBatch<RuneStreamsEvent, StreamsBitcoinTip>>;
1101
1248
  /** Narrowing overload: a literal `types` array narrows every batch's
1102
1249
  * event union to exactly those members. */
1103
- consume<const T extends readonly StreamsEventType[]>(params: StreamsConsumeParams & {
1250
+ consume<const T extends readonly StreamsEventType[]>(params: StreamsConsumeStacksParams & {
1104
1251
  types: T;
1105
1252
  }): AsyncIterableIterator<StreamsBatch<StreamsEventOfTypes<T>>>;
1106
1253
  consume(params?: StreamsConsumeParams): AsyncIterableIterator<StreamsBatch>;
1107
1254
  events: {
1108
1255
  /** `clock: "vm"` pages carry vm rows only (a parallel vocabulary,
1109
- * `event_index` = `ordinal`). Never `StreamsEvent`. */
1110
- list(params: StreamsEventsListParams & {
1256
+ * `event_index` = `ordinal`). Never `StreamsEvent`. Stacks-only —
1257
+ * `chain: "bitcoin"` has no `clock`. */
1258
+ list(params: StreamsEventsStacksListParams & {
1111
1259
  clock: "vm";
1112
1260
  }): Promise<StreamsEventsEnvelope<VmStreamsEvent>>;
1113
1261
  /** Narrowing overload, matching `consume`. Classic clock only —
1114
1262
  * `clock: "vm"` is overload 1, and a general `StreamsEventsListParams`
1115
1263
  * variable (clock unresolved) is the wire-union fallback. */
1116
- list<const T extends readonly StreamsEventType[]>(params: Omit<StreamsEventsListParams, "clock" | "types" | "notTypes"> & {
1264
+ list<const T extends readonly StreamsEventType[]>(params: Omit<StreamsEventsStacksListParams, "clock" | "types" | "notTypes"> & {
1117
1265
  types: T;
1118
1266
  notTypes?: readonly StreamsEventType[];
1119
1267
  clock?: "classic";
1120
1268
  }): Promise<StreamsEventsEnvelope<StreamsEventOfTypes<T>>>;
1121
- list(params?: Omit<StreamsEventsListParams, "clock" | "types" | "notTypes"> & {
1269
+ list(params?: Omit<StreamsEventsStacksListParams, "clock" | "types" | "notTypes"> & {
1122
1270
  types?: readonly StreamsEventType[];
1123
1271
  notTypes?: readonly StreamsEventType[];
1124
1272
  clock?: "classic";
1125
1273
  }): Promise<StreamsEventsEnvelope>;
1126
- /** Unresolved `clock` on a general params variable: the wire union. */
1274
+ /** `chain: "bitcoin"` pages carry Runes rows only, on the bitcoin tip
1275
+ * shape (plan 059). */
1276
+ list(params: StreamsEventsBitcoinListParams): Promise<StreamsEventsEnvelope<RuneStreamsEvent, StreamsBitcoinTip>>;
1277
+ /** Unresolved `chain`/`clock` on a general params variable: the wire union. */
1127
1278
  list(params: StreamsEventsListParams): Promise<StreamsEventsEnvelope<StreamsWireEvent>>;
1279
+ /** `chain: "bitcoin"` — Runes events for one transaction. */
1280
+ byTxId(txId: string, opts: {
1281
+ chain: "bitcoin";
1282
+ }): Promise<StreamsEventsListEnvelope<RuneStreamsEvent, StreamsBitcoinTip>>;
1128
1283
  byTxId(txId: string): Promise<StreamsEventsListEnvelope>;
1129
1284
  /**
1130
1285
  * Pull pages from Streams and call `onBatch` after each page.
@@ -1135,6 +1290,11 @@ type StreamsClient = {
1135
1290
  * The consumer also exits when `maxPages`, `maxEmptyPolls`, or `signal`
1136
1291
  * stops it.
1137
1292
  */
1293
+ /** `chain: "bitcoin"` — the checkpointed sweep over Runes events; see
1294
+ * {@link StreamsEventsConsumeBitcoinParams}. */
1295
+ consume<TTx = never>(params: StreamsEventsConsumeBitcoinParams<TTx> & {
1296
+ sink?: ConsumerSink<TTx>;
1297
+ }): Promise<StreamsEventsConsumeResult>;
1138
1298
  consume<
1139
1299
  const F extends StreamsFilterMap = Record<never, never>,
1140
1300
  TTx = never,
@@ -1157,8 +1317,10 @@ type StreamsClient = {
1157
1317
  * indefinitely by default and stops when its `AbortSignal`, `maxPages`, or
1158
1318
  * `maxEmptyPolls` stops it.
1159
1319
  */
1320
+ /** `chain: "bitcoin"` — Runes events only (plan 059/061). */
1321
+ stream(params: StreamsEventsStreamBitcoinParams): AsyncIterable<RuneStreamsEvent>;
1160
1322
  /** Narrowing overload, matching `consume`. */
1161
- stream<const T extends readonly StreamsEventType[]>(params: StreamsEventsStreamParams & {
1323
+ stream<const T extends readonly StreamsEventType[]>(params: StreamsEventsStreamStacksParams & {
1162
1324
  types: T;
1163
1325
  }): AsyncIterable<StreamsEventOfTypes<T>>;
1164
1326
  stream(params?: StreamsEventsStreamParams): AsyncIterable<StreamsEvent>;
@@ -1170,17 +1332,31 @@ type StreamsClient = {
1170
1332
  * settles when the loop ends. Not reorg-aware: an event delivered then
1171
1333
  * orphaned is never retracted. Durable writers use `consume()`.
1172
1334
  */
1173
- subscribe(params: StreamsEventsSubscribeParams): StreamsSubscription;
1335
+ /** `chain: "bitcoin"` — `onEvent` receives Runes rows (plan 059/061). */
1336
+ subscribe(params: StreamsEventsSubscribeBitcoinParams): StreamsSubscription;
1337
+ subscribe(params: StreamsEventsSubscribeStacksParams): StreamsSubscription;
1174
1338
  };
1175
1339
  blocks: {
1340
+ events(heightOrHash: number | string, opts: {
1341
+ chain: "bitcoin";
1342
+ }): Promise<StreamsEventsListEnvelope<RuneStreamsEvent, StreamsBitcoinTip>>;
1176
1343
  events(heightOrHash: number | string): Promise<StreamsEventsListEnvelope>;
1177
1344
  };
1178
1345
  reorgs: {
1346
+ list(params: StreamsReorgsListParams & {
1347
+ chain: "bitcoin";
1348
+ }): Promise<StreamsReorgsListEnvelope>;
1179
1349
  list(params: StreamsReorgsListParams): Promise<StreamsReorgsListEnvelope>;
1180
1350
  };
1181
1351
  /** Bulk parquet dumps. Requires `dumpsBaseUrl` on the client. */
1182
1352
  dumps: StreamsDumps;
1353
+ canonical(height: number, opts: {
1354
+ chain: "bitcoin";
1355
+ }): Promise<StreamsBitcoinCanonicalBlock>;
1183
1356
  canonical(height: number): Promise<StreamsCanonicalBlock>;
1357
+ tip(opts: {
1358
+ chain: "bitcoin";
1359
+ }): Promise<StreamsBitcoinTip>;
1184
1360
  tip(): Promise<StreamsTip>;
1185
1361
  };
1186
1362
  type Sleep = (ms: number, signal?: AbortSignal) => Promise<void>;
@@ -2318,6 +2494,185 @@ interface Pox5Resource {
2318
2494
  }>;
2319
2495
  };
2320
2496
  }
2497
+ /** A rune's `<block>:<tx>` id — the etch transaction's position, e.g. `840000:3`. */
2498
+ type RuneId = `${number}:${number}`;
2499
+ /**
2500
+ * A rune reference accepted by every Runes call: an id ({@link RuneId}) or a
2501
+ * name, with or without spacers, in any case — `DOG•GO•TO•THE•MOON`,
2502
+ * `dog.go.to.the.moon` and `doggotothemoon` all resolve to the same rune.
2503
+ * Typing every rune name is a no (178k runes and growing — a generated union
2504
+ * would be huge and stale on day one); `list({ search })` covers discovery.
2505
+ */
2506
+ type RuneRef = RuneId | (string & {});
2507
+ /** The Bitcoin chain tip a Runes read was served against — a separate clock
2508
+ * from the Stacks {@link IndexTip}: Runes ingest tracks Bitcoin blocks, not
2509
+ * Stacks ones. */
2510
+ type IndexBitcoinTip = {
2511
+ block_height: number;
2512
+ finalized_height: number;
2513
+ lag_seconds: number;
2514
+ };
2515
+ /** A Bitcoin fork the Runes indexer rolled back. Rows in
2516
+ * `[orphaned_from, orphaned_to]` were replaced; undo anything you committed
2517
+ * from them. Block-level only — a Bitcoin reorg has no Stacks-style
2518
+ * event_index component. */
2519
+ type IndexBtcReorg = {
2520
+ id: string;
2521
+ detected_at: string;
2522
+ fork_point_height: number;
2523
+ old_hash: string;
2524
+ new_hash: string;
2525
+ orphaned_from: number;
2526
+ orphaned_to: number;
2527
+ new_tip_height: number;
2528
+ };
2529
+ /** The rune identity bundle every Runes response embeds, so a client never
2530
+ * needs a second lookup just to show a name next to an event or a balance. */
2531
+ type IndexRuneRefSummary = {
2532
+ id: string;
2533
+ /** Spacers stripped, uppercase. */
2534
+ name: string;
2535
+ /** As etched, spacers (`•`) included. */
2536
+ spaced_name: string;
2537
+ symbol: string | null;
2538
+ divisibility: number;
2539
+ };
2540
+ /** Mint terms, or `null` for a premine-only rune that was never mintable. */
2541
+ type IndexRuneTerms = {
2542
+ amount: string | null;
2543
+ cap: string | null;
2544
+ height_start: string | null;
2545
+ height_end: string | null;
2546
+ offset_start: string | null;
2547
+ offset_end: string | null;
2548
+ };
2549
+ /** One etched rune's full entry: identity, premine/mint/burn counters,
2550
+ * computed supply, and mint terms. */
2551
+ type IndexRuneEntry = IndexRuneRefSummary & {
2552
+ /** Etch sequence number (u128 decimal string) — 0 is genesis. */
2553
+ number: string;
2554
+ /** Premined amount, u128 decimal string. Never `Number()`. */
2555
+ premine: string;
2556
+ /** Computed: `premine + mints × terms.amount` (0 with no terms). u128
2557
+ * decimal string. */
2558
+ supply: string;
2559
+ burned: string;
2560
+ mints: string;
2561
+ turbo: boolean;
2562
+ etching_txid: string;
2563
+ etched_height: number;
2564
+ etched_tx_index: number;
2565
+ terms: IndexRuneTerms | null;
2566
+ };
2567
+ type RunesEnvelope<TRow = IndexRuneEntry> = {
2568
+ runes: TRow[];
2569
+ next_cursor: string | null;
2570
+ tip: IndexBitcoinTip;
2571
+ /** Always empty: `list` reads a reorg-corrected snapshot table, not an
2572
+ * append-only log — there is no stale page to reconcile. */
2573
+ reorgs: IndexBtcReorg[];
2574
+ /** Present only when Runes data is not configured on this instance. */
2575
+ notes?: string;
2576
+ };
2577
+ type RunesListParams = {
2578
+ /** Name prefix, spacers and case ignored (`dog` matches `DOG•GO•TO•THE•MOON`). */
2579
+ search?: string;
2580
+ /** `number` (etch order, oldest first — default) or `mints` (busiest first). */
2581
+ sort?: "number" | "mints";
2582
+ cursor?: string | null;
2583
+ limit?: number;
2584
+ };
2585
+ type RuneEnvelope = {
2586
+ rune: IndexRuneEntry;
2587
+ tip: IndexBitcoinTip;
2588
+ };
2589
+ type IndexRuneEventKind = "rune_etch" | "rune_mint" | "rune_transfer" | "rune_burn";
2590
+ /** One etch/mint/transfer/burn event, with the rune's identity embedded so an
2591
+ * activity feed never needs a second lookup to show a name. */
2592
+ type IndexRuneEvent = {
2593
+ cursor: string;
2594
+ block_height: number;
2595
+ tx_index: number;
2596
+ txid: string;
2597
+ event_index: number;
2598
+ kind: IndexRuneEventKind;
2599
+ /** u128 decimal string. Never `Number()`. */
2600
+ amount: string;
2601
+ vout: number | null;
2602
+ /** The output's mainnet address. Only ever set on a `rune_transfer` event. */
2603
+ address: string | null;
2604
+ rune: IndexRuneRefSummary;
2605
+ };
2606
+ type RuneActivityEnvelope<TRow = IndexRuneEvent> = {
2607
+ events: TRow[];
2608
+ next_cursor: string | null;
2609
+ tip: IndexBitcoinTip;
2610
+ reorgs: IndexBtcReorg[];
2611
+ notes?: string;
2612
+ };
2613
+ type RuneActivityListParams = {
2614
+ cursor?: string | null;
2615
+ fromCursor?: string | null;
2616
+ fromHeight?: number;
2617
+ toHeight?: number;
2618
+ limit?: number;
2619
+ rune?: RuneRef;
2620
+ address?: string;
2621
+ kind?: readonly IndexRuneEventKind[];
2622
+ txid?: string;
2623
+ };
2624
+ type RuneActivityWalkParams = Omit<RuneActivityListParams, "limit"> & WalkOptions;
2625
+ /** A rune's balance on one unspent output, with the rune's identity embedded. */
2626
+ type IndexRuneBalance = {
2627
+ rune: IndexRuneRefSummary;
2628
+ /** The output's mainnet address, or `null` for a non-standard script. */
2629
+ address: string | null;
2630
+ txid: string;
2631
+ vout: number;
2632
+ /** u128 decimal string. Never `Number()`. */
2633
+ amount: string;
2634
+ };
2635
+ type RuneBalancesEnvelope<TRow = IndexRuneBalance> = {
2636
+ balances: TRow[];
2637
+ next_cursor: string | null;
2638
+ tip: IndexBitcoinTip;
2639
+ /** Always empty — see {@link RunesEnvelope.reorgs}. */
2640
+ reorgs: IndexBtcReorg[];
2641
+ notes?: string;
2642
+ };
2643
+ /** Exactly one of `address`/`outpoint` is required — enforced at the type
2644
+ * level, not just at runtime. */
2645
+ type RuneBalancesListParams = ({
2646
+ address: string;
2647
+ outpoint?: never;
2648
+ } | {
2649
+ outpoint: string;
2650
+ address?: never;
2651
+ }) & {
2652
+ /** Narrows the result to one rune. */
2653
+ rune?: RuneRef;
2654
+ cursor?: string | null;
2655
+ limit?: number;
2656
+ };
2657
+ /** `index.runes` — the rune catalog, activity log, and balances. Runes is
2658
+ * more data on the existing Index plane, not a new product. Not configured
2659
+ * on hosted yet; every feed's `notes` says so when it isn't. Self-host with
2660
+ * the `bitcoin` compose profile has data today. */
2661
+ interface RunesResource {
2662
+ /** The rune catalog. `search` prefix-matches the name; `sort` is etch
2663
+ * order (default) or busiest-first. */
2664
+ list(params?: RunesListParams): Promise<RunesEnvelope>;
2665
+ /** One rune's full entry by id or name; `null` on 404 (no such rune, or
2666
+ * Runes isn't configured on this instance). */
2667
+ get(rune: RuneRef): Promise<RuneEnvelope | null>;
2668
+ /** Current per-outpoint rune balances by address or outpoint. */
2669
+ balances(params: RuneBalancesListParams): Promise<RuneBalancesEnvelope>;
2670
+ /** Etch/mint/transfer/burn events, cursor `<block_height>:<event_index>`. */
2671
+ activity: {
2672
+ list(params?: RuneActivityListParams): Promise<RuneActivityEnvelope>;
2673
+ walk(params?: RuneActivityWalkParams): AsyncIterable<IndexRuneEvent>;
2674
+ };
2675
+ }
2321
2676
  /**
2322
2677
  * `index.ftTransfers` — callable shorthand for `.list()`, with `.list`/`.walk`
2323
2678
  * still available: `await sl.index.ftTransfers({ contractId })`.
@@ -2505,6 +2860,9 @@ declare class Index extends BaseClient {
2505
2860
  /** Decoded PoX-5 print events (SIP-045 Bitcoin Staking) — the raw pox-5
2506
2861
  * boot-contract log, all 19 topics. Distinct from `stacking` (PoX-4 era). */
2507
2862
  readonly pox5: Pox5Resource;
2863
+ /** Rune catalog, activity and balances. Separate clock/reorg source from
2864
+ * the Stacks feeds above — see {@link RunesResource}. */
2865
+ readonly runes: RunesResource;
2508
2866
  private listFtTransfers;
2509
2867
  private listNftTransfers;
2510
2868
  /** Shared keyset-pagination loop for every `walk*` feed: seed the cursor from
@@ -2555,6 +2913,11 @@ declare class Index extends BaseClient {
2555
2913
  private listPoxCycles;
2556
2914
  private walkPoxCycles;
2557
2915
  private getPoxCycle;
2916
+ private listRunes;
2917
+ private getRune;
2918
+ private listRuneActivity;
2919
+ private walkRuneActivity;
2920
+ private listRuneBalances;
2558
2921
  }
2559
2922
  import { CreateWebhookRequest, CreateWebhookResponse, DeadRow, DeliveryRow, ReplayResult, RotateSecretResponse, UpdateWebhookRequest, WebhookActivity, WebhookDeliveryDetail, WebhookDetail, WebhookSummary, WebhookTestResult } from "@secondlayer/shared/schemas/webhooks";
2560
2923
  declare class Webhooks extends BaseClient {