@secondlayer/sdk 6.9.0 → 6.10.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.
@@ -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 ?? false,
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;
@@ -1484,5 +1638,5 @@ export {
1484
1638
  Subgraphs
1485
1639
  };
1486
1640
 
1487
- //# debugId=31D759DA70279D1564756E2164756E21
1641
+ //# debugId=B6D6E26DCF0C5E6064756E2164756E21
1488
1642
  //# sourceMappingURL=index.js.map