@secondlayer/sdk 6.9.1 → 6.11.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/dist/index.d.ts CHANGED
@@ -731,6 +731,7 @@ type StackingEnvelope = {
731
731
  stacking: IndexStackingAction[]
732
732
  next_cursor: string | null
733
733
  tip: IndexTip
734
+ reorgs: never[]
734
735
  /** Present only when the PoX-4 decoder is disabled, explaining an empty feed. */
735
736
  notes?: string
736
737
  };
@@ -1053,6 +1054,23 @@ type StreamsEventsStreamParams = {
1053
1054
  maxEmptyPolls?: number
1054
1055
  signal?: AbortSignal
1055
1056
  };
1057
+ type StreamsEventsSubscribeParams = {
1058
+ /** Resume strictly after this cursor; omit to live-tail from the tip. */
1059
+ fromCursor?: string | null
1060
+ types?: readonly StreamsEventType[]
1061
+ notTypes?: readonly StreamsEventType[]
1062
+ contractId?: StreamsFilterValue
1063
+ sender?: StreamsFilterValue
1064
+ recipient?: StreamsFilterValue
1065
+ assetIdentifier?: string
1066
+ /** Abort to unsubscribe (the returned function does the same). */
1067
+ signal?: AbortSignal
1068
+ /** Called for each pushed event, in order. */
1069
+ onEvent: (event: StreamsEvent) => void | Promise<void>
1070
+ /** Called on a connection error; the subscription auto-reconnects from the
1071
+ * last delivered cursor unless the signal has aborted. */
1072
+ onError?: (err: unknown) => void
1073
+ };
1056
1074
  /**
1057
1075
  * The checkpoint the SDK computes for a batch. Persist `cursor` inside the same
1058
1076
  * transaction as your projection writes, then resume from it via `fromCursor`.
@@ -1158,6 +1176,11 @@ type StreamsDumpsManifest = {
1158
1176
  to_block: number
1159
1177
  }
1160
1178
  files: StreamsDumpFile[]
1179
+ /** ed25519 signature over the manifest's canonical bytes. Absent on legacy
1180
+ * unsigned manifests. Verified by `list()` when `verifyDumpsManifest` is on. */
1181
+ signature?: string
1182
+ /** Short id of the signing public key. */
1183
+ key_id?: string
1161
1184
  };
1162
1185
  type StreamsDumps = {
1163
1186
  /** Fetch and parse the latest dumps manifest. */
@@ -1197,6 +1220,13 @@ type StreamsClient = {
1197
1220
  * `maxEmptyPolls` stops it.
1198
1221
  */
1199
1222
  stream(params?: StreamsEventsStreamParams): AsyncIterable<StreamsEvent>
1223
+ /**
1224
+ * Subscribe to the real-time SSE push surface. Calls `onEvent` for each new
1225
+ * canonical event as the server pushes it (chain cadence, not poll-bounded),
1226
+ * and verifies each frame's inline ed25519 signature when the client was
1227
+ * created with `verify`. Returns an unsubscribe function.
1228
+ */
1229
+ subscribe(params: StreamsEventsSubscribeParams): () => void
1200
1230
  }
1201
1231
  blocks: {
1202
1232
  events(heightOrHash: number | string): Promise<StreamsEventsListEnvelope>
@@ -1333,6 +1363,14 @@ type CreateStreamsClientOptions = {
1333
1363
  verify?: boolean | {
1334
1364
  publicKey: string
1335
1365
  }
1366
+ /**
1367
+ * Verify the bulk dumps manifest's ed25519 signature in `client.dumps.list()`
1368
+ * before trusting any file sha256 (default ON). Uses the same key source as
1369
+ * `verify` (fetches `/public/streams/signing-key`, or a pinned PEM). Pass
1370
+ * `false` to opt out. A missing or invalid signature throws
1371
+ * `StreamsSignatureError`.
1372
+ */
1373
+ verifyDumpsManifest?: boolean
1336
1374
  };
1337
1375
  declare function createStreamsClient(options: CreateStreamsClientOptions): StreamsClient;
1338
1376
  declare class AuthError extends Error {
@@ -1739,10 +1777,40 @@ type WebhookHeaderInput = HeaderLookup | StandardWebhooksHeaders | Record<string
1739
1777
  * ```
1740
1778
  */
1741
1779
  declare function verifyWebhookSignature(rawBody: string, headers: WebhookHeaderInput, secret: string, toleranceSeconds?: number): boolean;
1780
+ /**
1781
+ * Verify the universal Secondlayer authenticity signature that every delivery
1782
+ * carries, regardless of body format (`raw`, `cloudevents`, `standard-webhooks`,
1783
+ * …). This is the format-agnostic alternative to {@link verifyWebhookSignature}:
1784
+ * instead of a per-subscription HMAC secret, it checks an ed25519 signature over
1785
+ * `${webhook-id}.${rawBody}` against Secondlayer's published public key — so one
1786
+ * key proves authenticity for any format.
1787
+ *
1788
+ * @param rawBody The raw request body string (never re-stringify the parsed
1789
+ * JSON — whitespace/key-order changes break the signature).
1790
+ * @param headers Request headers — plain object, Fetch `Headers`, or a
1791
+ * lookup callback. Reads `webhook-id` + `x-secondlayer-signature`.
1792
+ * @param publicKeyPem Secondlayer's published ed25519 public key (SPKI PEM).
1793
+ * @returns true when the signature header is present and verifies.
1794
+ *
1795
+ * @example
1796
+ * ```ts
1797
+ * import { verifySecondlayerSignature } from "@secondlayer/sdk";
1798
+ *
1799
+ * app.post("/webhook", async (c) => {
1800
+ * const raw = await c.req.text();
1801
+ * if (!verifySecondlayerSignature(raw, c.req.raw.headers, SECONDLAYER_PUBLIC_KEY)) {
1802
+ * return c.text("Invalid signature", 401);
1803
+ * }
1804
+ * // ... process raw ...
1805
+ * return c.body(null, 204);
1806
+ * });
1807
+ * ```
1808
+ */
1809
+ declare function verifySecondlayerSignature(rawBody: string, headers: WebhookHeaderInput, publicKeyPem: string): boolean;
1742
1810
  /** Make a cvToValue result JSON-serializable: Clarity (u)ints decode to bigint,
1743
1811
  * which JSON.stringify can't handle — convert recursively to strings. */
1744
1812
  declare function toJsonSafe(value: unknown): unknown;
1745
1813
  /** Decode a hex-encoded Clarity value to JSON-safe JS (uints as strings,
1746
1814
  * buffers as `0x…` hex, tuples as objects). Returns the input hex on failure. */
1747
1815
  declare function decodeClarityValue(hex: string): unknown;
1748
- export { verifyWebhookSignature, trigger, 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, TransactionsWalkParams, TransactionsListParams, TransactionsEnvelope, TransactionEnvelope, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionKind, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphOperationStatus, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsUsage, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StackingWalkParams, StackingListParams, StackingEnvelope, SecondLayerOptions, SecondLayer, ScopedKeyProduct, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, MempoolWalkParams, MempoolTransactionEnvelope, MempoolListParams, MempoolEnvelope, IndexUsage, IndexTransaction, IndexTip, IndexStackingAction, IndexPostCondition, IndexMempoolTransaction, IndexEventType, IndexEvent, IndexContractCall, IndexCanonicalBlock, IndexBlock, 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, Cursor, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, CreateApiKeyResponse, CreateApiKeyParams, ContractsListParams, ContractsEnvelope, Contracts, ContractSummary, ContractConformance, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, ContextSnapshot, ContextAccount, ChainTriggerType, ChainTrigger, CanonicalWalkParams, CanonicalListParams, CanonicalEnvelope, CURSOR_SLUGS, BlocksWalkParams, BlocksListParams, BlocksEnvelope, BlockEnvelope, AuthError, ApiKeys, ApiError, ActiveSubgraphOperation };
1816
+ export { verifyWebhookSignature, verifySecondlayerSignature, trigger, 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, TransactionsWalkParams, TransactionsListParams, TransactionsEnvelope, TransactionEnvelope, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionKind, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphOperationStatus, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsUsage, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsSubscribeParams, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StackingWalkParams, StackingListParams, StackingEnvelope, SecondLayerOptions, SecondLayer, ScopedKeyProduct, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, MempoolWalkParams, MempoolTransactionEnvelope, MempoolListParams, MempoolEnvelope, IndexUsage, IndexTransaction, IndexTip, IndexStackingAction, IndexPostCondition, IndexMempoolTransaction, IndexEventType, IndexEvent, IndexContractCall, IndexCanonicalBlock, IndexBlock, 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, Cursor, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, CreateApiKeyResponse, CreateApiKeyParams, ContractsListParams, ContractsEnvelope, Contracts, ContractSummary, ContractConformance, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, ContextSnapshot, ContextAccount, ChainTriggerType, ChainTrigger, CanonicalWalkParams, CanonicalListParams, CanonicalEnvelope, CURSOR_SLUGS, BlocksWalkParams, BlocksListParams, BlocksEnvelope, BlockEnvelope, AuthError, ApiKeys, ApiError, ActiveSubgraphOperation };
package/dist/index.js CHANGED
@@ -891,7 +891,7 @@ class Index extends BaseClient {
891
891
  }
892
892
 
893
893
  // src/streams/client.ts
894
- import { ed25519 } from "@secondlayer/shared";
894
+ import { ed25519 as ed255192 } from "@secondlayer/shared";
895
895
 
896
896
  // src/streams/errors.ts
897
897
  class AuthError extends Error {
@@ -1078,6 +1078,7 @@ async function* streamStreamsEvents(opts) {
1078
1078
 
1079
1079
  // src/streams/dumps.ts
1080
1080
  import { createHash } from "node:crypto";
1081
+ import { verifyStreamsBulkManifestSignature } from "@secondlayer/shared/streams-bulk-manifest";
1081
1082
  function createStreamsDumps(opts) {
1082
1083
  const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
1083
1084
  function requireBaseUrl() {
@@ -1095,7 +1096,17 @@ function createStreamsDumps(opts) {
1095
1096
  if (!res.ok) {
1096
1097
  throw new StreamsServerError(`Could not fetch dumps manifest (${res.status}).`, res.status);
1097
1098
  }
1098
- return await res.json();
1099
+ const manifest = await res.json();
1100
+ if (opts.verifyManifest) {
1101
+ if (!opts.loadPublicKeyPem) {
1102
+ throw new StreamsSignatureError("Manifest verification is on but no signing key source is configured.");
1103
+ }
1104
+ const publicKeyPem = await opts.loadPublicKeyPem();
1105
+ if (!verifyStreamsBulkManifestSignature(manifest, publicKeyPem)) {
1106
+ throw new StreamsSignatureError("Dumps manifest signature is missing or invalid.");
1107
+ }
1108
+ }
1109
+ return manifest;
1099
1110
  }
1100
1111
  async function download(file) {
1101
1112
  const res = await opts.fetchImpl(fileUrl(file));
@@ -1112,6 +1123,135 @@ function createStreamsDumps(opts) {
1112
1123
  return { list, fileUrl, download };
1113
1124
  }
1114
1125
 
1126
+ // src/streams/subscribe.ts
1127
+ import { ed25519 } from "@secondlayer/shared";
1128
+ function subscribeStreamsEvents(opts) {
1129
+ const { params } = opts;
1130
+ const controller = new AbortController;
1131
+ const external = params.signal;
1132
+ if (external) {
1133
+ if (external.aborted)
1134
+ controller.abort();
1135
+ else
1136
+ external.addEventListener("abort", () => controller.abort(), {
1137
+ once: true
1138
+ });
1139
+ }
1140
+ let cursor = params.fromCursor ?? null;
1141
+ const reconnectDelayMs = opts.reconnectDelayMs ?? 1000;
1142
+ const run = async () => {
1143
+ while (!controller.signal.aborted) {
1144
+ try {
1145
+ const url = `${opts.baseUrl}/v1/streams/events/stream${buildQuery({
1146
+ from_cursor: cursor ?? undefined,
1147
+ types: params.types,
1148
+ not_types: params.notTypes,
1149
+ contract_id: params.contractId,
1150
+ sender: params.sender,
1151
+ recipient: params.recipient,
1152
+ asset_identifier: params.assetIdentifier
1153
+ })}`;
1154
+ const res = await opts.fetchImpl(url, {
1155
+ headers: {
1156
+ Authorization: `Bearer ${opts.apiKey}`,
1157
+ Accept: "text/event-stream"
1158
+ },
1159
+ signal: controller.signal
1160
+ });
1161
+ if (!res.ok) {
1162
+ throw new StreamsServerError(`Streams SSE returned ${res.status}.`, res.status);
1163
+ }
1164
+ if (!res.body) {
1165
+ throw new StreamsServerError("Streams SSE response has no body.", 0);
1166
+ }
1167
+ for await (const frame of parseSseFrames(res.body, controller.signal)) {
1168
+ if (frame.event === "ping" || !frame.data)
1169
+ continue;
1170
+ let parsed;
1171
+ try {
1172
+ parsed = JSON.parse(frame.data);
1173
+ } catch {
1174
+ continue;
1175
+ }
1176
+ if (!parsed.event)
1177
+ continue;
1178
+ if (opts.verify) {
1179
+ const key = await opts.loadKey();
1180
+ if (!parsed.sig || !ed25519.verifyEd25519(JSON.stringify(parsed.event), parsed.sig, key.publicKey)) {
1181
+ throw new StreamsSignatureError("Streams SSE frame signature is missing or invalid.");
1182
+ }
1183
+ }
1184
+ cursor = parsed.event.cursor ?? cursor;
1185
+ await params.onEvent(parsed.event);
1186
+ }
1187
+ } catch (err) {
1188
+ if (controller.signal.aborted)
1189
+ return;
1190
+ params.onError?.(err);
1191
+ await sleep(reconnectDelayMs, controller.signal);
1192
+ }
1193
+ }
1194
+ };
1195
+ run();
1196
+ return () => controller.abort();
1197
+ }
1198
+ function sleep(ms, signal) {
1199
+ return new Promise((resolve) => {
1200
+ if (signal.aborted)
1201
+ return resolve();
1202
+ const onAbort = () => {
1203
+ clearTimeout(timer);
1204
+ resolve();
1205
+ };
1206
+ const timer = setTimeout(() => {
1207
+ signal.removeEventListener("abort", onAbort);
1208
+ resolve();
1209
+ }, ms);
1210
+ signal.addEventListener("abort", onAbort, { once: true });
1211
+ });
1212
+ }
1213
+ async function* parseSseFrames(body, signal) {
1214
+ const reader = body.getReader();
1215
+ const decoder = new TextDecoder;
1216
+ let buffer = "";
1217
+ try {
1218
+ while (!signal.aborted) {
1219
+ const { value, done } = await reader.read();
1220
+ if (done)
1221
+ break;
1222
+ buffer += decoder.decode(value, { stream: true });
1223
+ let sep = buffer.indexOf(`
1224
+
1225
+ `);
1226
+ while (sep !== -1) {
1227
+ yield parseFrame(buffer.slice(0, sep));
1228
+ buffer = buffer.slice(sep + 2);
1229
+ sep = buffer.indexOf(`
1230
+
1231
+ `);
1232
+ }
1233
+ }
1234
+ } finally {
1235
+ try {
1236
+ await reader.cancel();
1237
+ } catch {}
1238
+ }
1239
+ }
1240
+ function parseFrame(raw) {
1241
+ let event;
1242
+ const data = [];
1243
+ for (const line of raw.split(`
1244
+ `)) {
1245
+ if (line.startsWith("data:")) {
1246
+ data.push(line.slice(line.startsWith("data: ") ? 6 : 5));
1247
+ } else if (line.startsWith("event:")) {
1248
+ event = line.slice(line.startsWith("event: ") ? 7 : 6).trim();
1249
+ }
1250
+ }
1251
+ return { event, data: data.length > 0 ? data.join(`
1252
+ `) : undefined };
1253
+ }
1254
+
1115
1255
  // src/streams/client.ts
1116
1256
  function cursorTuple(cursor) {
1117
1257
  if (!cursor)
@@ -1171,10 +1311,6 @@ function createStreamsClient(options) {
1171
1311
  const baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_STREAMS_BASE_URL);
1172
1312
  const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
1173
1313
  const verify = options.verify ?? false;
1174
- const dumps = createStreamsDumps({
1175
- baseUrl: options.dumpsBaseUrl,
1176
- fetchImpl
1177
- });
1178
1314
  let keyPromise = null;
1179
1315
  function loadKey() {
1180
1316
  if (keyPromise)
@@ -1182,8 +1318,9 @@ function createStreamsClient(options) {
1182
1318
  keyPromise = (async () => {
1183
1319
  if (typeof verify === "object") {
1184
1320
  return {
1185
- keyId: ed25519.ed25519KeyId(verify.publicKey),
1186
- publicKey: ed25519.loadEd25519PublicKey(verify.publicKey)
1321
+ keyId: ed255192.ed25519KeyId(verify.publicKey),
1322
+ publicKeyPem: verify.publicKey,
1323
+ publicKey: ed255192.loadEd25519PublicKey(verify.publicKey)
1187
1324
  };
1188
1325
  }
1189
1326
  const res = await fetchImpl(`${baseUrl}/public/streams/signing-key`);
@@ -1195,12 +1332,19 @@ function createStreamsClient(options) {
1195
1332
  throw new StreamsSignatureError("Signing key response missing key.");
1196
1333
  }
1197
1334
  return {
1198
- keyId: body.key_id ?? ed25519.ed25519KeyId(body.public_key_pem),
1199
- publicKey: ed25519.loadEd25519PublicKey(body.public_key_pem)
1335
+ keyId: body.key_id ?? ed255192.ed25519KeyId(body.public_key_pem),
1336
+ publicKeyPem: body.public_key_pem,
1337
+ publicKey: ed255192.loadEd25519PublicKey(body.public_key_pem)
1200
1338
  };
1201
1339
  })();
1202
1340
  return keyPromise;
1203
1341
  }
1342
+ const dumps = createStreamsDumps({
1343
+ baseUrl: options.dumpsBaseUrl,
1344
+ fetchImpl,
1345
+ verifyManifest: options.verifyDumpsManifest ?? true,
1346
+ loadPublicKeyPem: async () => (await loadKey()).publicKeyPem
1347
+ });
1204
1348
  async function request(path) {
1205
1349
  const response = await fetchImpl(`${baseUrl}${path}`, {
1206
1350
  headers: { Authorization: `Bearer ${options.apiKey}` }
@@ -1225,7 +1369,7 @@ function createStreamsClient(options) {
1225
1369
  throw new StreamsSignatureError(`Response signed with key '${responseKeyId}' not served by the signing-key endpoint.`);
1226
1370
  }
1227
1371
  }
1228
- if (!ed25519.verifyEd25519(text, signature, key.publicKey)) {
1372
+ if (!ed255192.verifyEd25519(text, signature, key.publicKey)) {
1229
1373
  throw new StreamsSignatureError;
1230
1374
  }
1231
1375
  }
@@ -1310,6 +1454,16 @@ function createStreamsClient(options) {
1310
1454
  fetchEvents
1311
1455
  });
1312
1456
  },
1457
+ subscribe(params) {
1458
+ return subscribeStreamsEvents({
1459
+ baseUrl,
1460
+ apiKey: options.apiKey,
1461
+ fetchImpl,
1462
+ verify: Boolean(verify),
1463
+ loadKey,
1464
+ params
1465
+ });
1466
+ },
1313
1467
  async replay(params) {
1314
1468
  const fromCursor = params.from === "genesis" ? null : params.from ?? null;
1315
1469
  const fromBlock = fromCursor ? cursorTuple(fromCursor)[0] : 0;
@@ -1763,6 +1917,7 @@ import {
1763
1917
  STREAMS_EVENT_TYPES
1764
1918
  } from "@secondlayer/shared";
1765
1919
  // src/webhooks.ts
1920
+ import { verifySecondlayerSignatureValues } from "@secondlayer/shared/crypto/secondlayer-webhook";
1766
1921
  import {
1767
1922
  verify
1768
1923
  } from "@secondlayer/shared/crypto/standard-webhooks";
@@ -1805,8 +1960,14 @@ function verifyWebhookSignature(rawBody, headers, secret, toleranceSeconds = 300
1805
1960
  "webhook-signature": signature
1806
1961
  }, secret, { toleranceSeconds });
1807
1962
  }
1963
+ function verifySecondlayerSignature(rawBody, headers, publicKeyPem) {
1964
+ const id = pickHeader(headers, "webhook-id");
1965
+ const signature = pickHeader(headers, "x-secondlayer-signature");
1966
+ return verifySecondlayerSignatureValues(rawBody, id, signature, publicKeyPem);
1967
+ }
1808
1968
  export {
1809
1969
  verifyWebhookSignature,
1970
+ verifySecondlayerSignature,
1810
1971
  trigger,
1811
1972
  toJsonSafe,
1812
1973
  isStxTransfer,
@@ -1852,5 +2013,5 @@ export {
1852
2013
  ApiError
1853
2014
  };
1854
2015
 
1855
- //# debugId=209F8CDE1560C10F64756E2164756E21
2016
+ //# debugId=7E891A303DCCD65B64756E2164756E21
1856
2017
  //# sourceMappingURL=index.js.map