@secondlayer/sdk 10.5.0 → 11.0.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.js CHANGED
@@ -1,3 +1,10 @@
1
+ // src/archive/client.ts
2
+ import { createHash } from "node:crypto";
3
+ import {
4
+ checkSignature,
5
+ loadReference
6
+ } from "@secondlayer/shared/archive/reference";
7
+
1
8
  // src/errors.ts
2
9
  var DOCS_BASE = "https://www.secondlayer.tools";
3
10
 
@@ -424,604 +431,109 @@ class BaseClient {
424
431
  }
425
432
  }
426
433
 
427
- // src/streams/subscribe.ts
428
- import { ed25519 } from "@secondlayer/shared";
429
-
430
- // src/streams/errors.ts
431
- class StreamsServerError extends ApiError {
432
- constructor(message, status, body, code) {
433
- super(status, message, body, code, { retryable: true });
434
- this.name = "StreamsServerError";
435
- }
434
+ // src/archive/client.ts
435
+ var OFFICIAL_ARCHIVE_HOST = "archive.secondlayer.tools";
436
+ var DEFAULT_ARCHIVE_BASE_URL = "https://archive.secondlayer.tools";
437
+ var DEFAULT_ARCHIVE_OPS_URL = "https://api.secondlayer.tools";
438
+ var MAX_FETCH_BATCH = 64;
439
+ var ARCHIVE_CREDENTIAL_RE = /^s[ks]-sl_/;
440
+ function quoteFromBody(body) {
441
+ return {
442
+ partitions: body.partitions,
443
+ bundles: body.bundles,
444
+ usdMicros: body.usd_micros,
445
+ usd: body.usd,
446
+ freeAllowanceAppliedMicros: body.free_allowance_applied_micros,
447
+ allowanceRemainingBundles: body.allowance_remaining_bundles,
448
+ balanceUsdMicros: body.balance_usd_micros,
449
+ sufficient: body.sufficient
450
+ };
436
451
  }
437
-
438
- class StreamsSignatureError extends SecondLayerError {
439
- constructor(message = "Streams response signature verification failed.") {
440
- super(message);
441
- this.name = "StreamsSignatureError";
442
- }
452
+ function stripSlash(url) {
453
+ return url.replace(/\/+$/, "");
443
454
  }
444
- async function mapStreamsError(response) {
445
- const { message, code, body } = parseErrorEnvelope(await response.text());
446
- if (response.status === 401) {
447
- throw new AuthError(message ?? "API key invalid or expired.", body, code);
448
- }
449
- if (response.status === 429) {
450
- const retryAfter = response.headers.get("Retry-After") ?? undefined;
451
- throw new RateLimitError(message ?? "Rate limited. Try again later.", retryAfter, body, code);
452
- }
453
- if (response.status >= 500) {
454
- throw new StreamsServerError(message ?? `Streams server returned ${response.status}.`, response.status, body, code);
455
+ function createArchiveClient(options = {}) {
456
+ const accountKey = resolveAccountKey(options.accountKey ?? options.apiKey);
457
+ const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
458
+ const archiveBaseUrl = stripSlash(options.archiveBaseUrl ?? DEFAULT_ARCHIVE_BASE_URL);
459
+ const archiveOpsUrl = stripSlash(options.archiveOpsUrl ?? DEFAULT_ARCHIVE_OPS_URL);
460
+ const verifyManifest = options.verifyManifest ?? true;
461
+ const pinnedPem = options.publicKeyPem;
462
+ let keyPromise = null;
463
+ async function fetchSigningKey() {
464
+ if (pinnedPem)
465
+ return pinnedPem;
466
+ if (keyPromise)
467
+ return keyPromise;
468
+ const pending = (async () => {
469
+ const res = await fetchImpl(`${archiveOpsUrl}/public/streams/signing-key`);
470
+ if (!res.ok) {
471
+ throw new ArchiveSignatureError(`Could not fetch archive signing key (${res.status}).`);
472
+ }
473
+ const body = await res.json();
474
+ if (typeof body.public_key_pem !== "string") {
475
+ throw new ArchiveSignatureError("Signing key response missing key.");
476
+ }
477
+ return body.public_key_pem;
478
+ })();
479
+ keyPromise = pending;
480
+ pending.catch(() => {
481
+ if (keyPromise === pending)
482
+ keyPromise = null;
483
+ });
484
+ return pending;
455
485
  }
456
- throw new ValidationError(message ?? `Streams request returned ${response.status}.`, response.status, body, code);
457
- }
458
-
459
- // src/streams/subscribe.ts
460
- var SUBSCRIBE_MAX_RECONNECT_DELAY_MS = 30000;
461
- var SUBSCRIBE_DEFAULT_STALE_AFTER_MS = 60000;
462
- function reconnectDelay(attempt, baseMs, retryAfterSeconds, random = Math.random) {
463
- const capped = Math.min(baseMs * 2 ** Math.max(0, attempt), SUBSCRIBE_MAX_RECONNECT_DELAY_MS);
464
- const jittered = capped * (0.5 + random() * 0.5);
465
- const floor = (retryAfterSeconds ?? 0) * 1000;
466
- return Math.max(jittered, floor);
467
- }
468
- function isTerminal(err) {
469
- if (err instanceof StreamsSignatureError)
470
- return true;
471
- return err?.retryable === false;
472
- }
473
- function subscribeStreamsEvents(opts) {
474
- const { params } = opts;
475
- const controller = new AbortController;
476
- const external = params.signal;
477
- if (external) {
478
- if (external.aborted)
479
- controller.abort();
480
- else
481
- external.addEventListener("abort", () => controller.abort(), {
482
- once: true
483
- });
486
+ async function throwOpsError(response) {
487
+ const envelope = parseErrorEnvelope(await response.text());
488
+ if (response.status === 401) {
489
+ throw new ArchiveAuthError(envelope.message ?? "Archive ops request was not authenticated.");
490
+ }
491
+ if (response.status === 402) {
492
+ const raw = envelope.body && typeof envelope.body === "object" && "shortfall_usd_micros" in envelope.body ? Number(envelope.body.shortfall_usd_micros) : undefined;
493
+ throw new InsufficientArchiveCreditsError(envelope.message ?? "Insufficient archive credits.", Number.isFinite(raw) ? raw : undefined);
494
+ }
495
+ if (response.status === 503) {
496
+ throw new ArchiveGateNotConfiguredError(envelope.message ?? "The archive gate is not configured on the server.");
497
+ }
498
+ throw new ApiError(response.status, envelope.message ?? `Archive ops returned ${response.status}.`, envelope.body, envelope.code);
484
499
  }
485
- let cursor = params.fromCursor ?? null;
486
- const baseDelayMs = opts.reconnectDelayMs ?? params.reconnectDelayMs ?? 1000;
487
- const staleAfterMs = opts.staleAfterMs ?? params.staleAfterMs ?? SUBSCRIBE_DEFAULT_STALE_AFTER_MS;
488
- let attempt = 0;
489
- const run = async () => {
490
- while (!controller.signal.aborted) {
491
- try {
492
- const url = `${opts.baseUrl}/v1/streams/events/stream${buildQuery({
493
- from_cursor: cursor ?? undefined,
494
- types: params.types,
495
- not_types: params.notTypes,
496
- contract_id: params.contractId,
497
- sender: params.sender,
498
- recipient: params.recipient,
499
- asset_identifier: params.assetIdentifier,
500
- filters: params.filters ? JSON.stringify(params.filters) : undefined
501
- })}`;
502
- await readSse({
503
- url,
504
- headers: opts.headers ?? {},
505
- signal: controller.signal,
506
- fetchImpl: opts.fetchImpl,
507
- staleAfterMs,
508
- mapError: mapStreamsError,
509
- onFrame: async (frame) => {
510
- attempt = 0;
511
- if (frame.event === "ping" || !frame.data)
512
- return;
513
- let parsed;
514
- try {
515
- parsed = JSON.parse(frame.data);
516
- } catch {
517
- return;
518
- }
519
- if (!parsed.event)
520
- return;
521
- if (opts.verify !== "off") {
522
- if (!parsed.sig) {
523
- if (opts.verify === "strict") {
524
- throw new StreamsSignatureError("Streams SSE frame signature is missing.");
525
- }
526
- } else {
527
- const key = await opts.loadKey(parsed.key_id);
528
- if (!ed25519.verifyEd25519(JSON.stringify(parsed.event), parsed.sig, key.publicKey)) {
529
- throw new StreamsSignatureError("Streams SSE frame signature is invalid.");
530
- }
531
- }
532
- }
533
- await params.onEvent(parsed.event);
534
- cursor = parsed.event.cursor ?? cursor;
535
- }
536
- });
537
- await sleep(reconnectDelay(attempt, baseDelayMs, undefined, opts.random), controller.signal);
538
- attempt += 1;
539
- } catch (err) {
540
- if (controller.signal.aborted)
541
- return;
542
- params.onError?.(err);
543
- if (isTerminal(err))
544
- throw err;
545
- const retryAfterSeconds = err.retryAfterSeconds;
546
- await sleep(reconnectDelay(attempt, baseDelayMs, retryAfterSeconds, opts.random), controller.signal);
547
- attempt += 1;
500
+ async function opsRequest(method, path, body, authed = true) {
501
+ const headers = {
502
+ "Content-Type": "application/json"
503
+ };
504
+ if (authed && accountKey) {
505
+ if (!ARCHIVE_CREDENTIAL_RE.test(accountKey)) {
506
+ throw new ArchiveAuthError("archive ops need SECONDLAYER_API_KEY (sk-sl_*), not INSTANCE_TOKEN");
548
507
  }
508
+ headers.Authorization = `Bearer ${accountKey}`;
549
509
  }
550
- };
551
- const done = run();
552
- done.catch(() => {});
553
- const unsubscribe = () => controller.abort();
554
- return Object.assign(unsubscribe, { done });
555
- }
556
- async function readSse(opts) {
557
- const res = await opts.fetchImpl(opts.url, {
558
- headers: { ...opts.headers ?? {}, Accept: "text/event-stream" },
559
- signal: opts.signal
560
- });
561
- if (!res.ok) {
562
- if (opts.mapError)
563
- await opts.mapError(res);
564
- throw new StreamsServerError(`Streams SSE returned ${res.status}.`, res.status);
510
+ const response = await fetchImpl(`${archiveOpsUrl}${path}`, {
511
+ method,
512
+ headers,
513
+ body: body !== undefined ? JSON.stringify(body) : undefined
514
+ });
515
+ if (!response.ok)
516
+ await throwOpsError(response);
517
+ return await response.json();
565
518
  }
566
- if (!res.body) {
567
- throw new StreamsServerError("Streams SSE response has no body.", 0);
519
+ async function load(against, opts = {}) {
520
+ if (!/^https?:\/\//i.test(against)) {
521
+ throw new ValidationError("archive.load() accepts only http(s) URLs; local paths stay on the CLI.", 400);
522
+ }
523
+ const insecure = opts.insecure === true;
524
+ const publicKeyPem = pinnedPem ?? (verifyManifest && !insecure ? await fetchSigningKey() : undefined);
525
+ const ref = await loadReference(against, {
526
+ publicKeyPem,
527
+ fetchImpl
528
+ });
529
+ const signature = checkSignature(ref.manifest, publicKeyPem, insecure);
530
+ if (verifyManifest && !insecure && !signature.verified) {
531
+ throw new ArchiveSignatureError(signature.reason ?? "Archive manifest signature is missing or invalid.");
532
+ }
533
+ return { ...ref, signature };
568
534
  }
569
- for await (const frame of parseSseFrames(res.body, opts.signal, opts.staleAfterMs)) {
570
- await opts.onFrame(frame);
571
- }
572
- }
573
- function sleep(ms, signal) {
574
- return new Promise((resolve) => {
575
- if (signal.aborted)
576
- return resolve();
577
- const onAbort = () => {
578
- clearTimeout(timer);
579
- resolve();
580
- };
581
- const timer = setTimeout(() => {
582
- signal.removeEventListener("abort", onAbort);
583
- resolve();
584
- }, ms);
585
- signal.addEventListener("abort", onAbort, { once: true });
586
- });
587
- }
588
- async function readWithStaleTimer(reader, staleAfterMs) {
589
- if (staleAfterMs === undefined || !Number.isFinite(staleAfterMs)) {
590
- return reader.read();
591
- }
592
- let timer;
593
- const stale = new Promise((_, reject) => {
594
- timer = setTimeout(() => reject(new StreamsServerError(`Streams SSE went quiet for ${staleAfterMs} ms; reconnecting.`, 0, undefined, "STREAM_STALE")), staleAfterMs);
595
- });
596
- try {
597
- return await Promise.race([reader.read(), stale]);
598
- } finally {
599
- clearTimeout(timer);
600
- }
601
- }
602
- async function* parseSseFrames(body, signal, staleAfterMs) {
603
- const reader = body.getReader();
604
- const decoder = new TextDecoder;
605
- let buffer = "";
606
- try {
607
- while (!signal.aborted) {
608
- const { value, done } = await readWithStaleTimer(reader, staleAfterMs);
609
- if (done)
610
- break;
611
- buffer += decoder.decode(value, { stream: true });
612
- let sep = buffer.indexOf(`
613
-
614
- `);
615
- while (sep !== -1) {
616
- yield parseFrame(buffer.slice(0, sep));
617
- buffer = buffer.slice(sep + 2);
618
- sep = buffer.indexOf(`
619
-
620
- `);
621
- }
622
- }
623
- } finally {
624
- try {
625
- await reader.cancel();
626
- } catch {}
627
- }
628
- }
629
- function parseFrame(raw) {
630
- let event;
631
- const data = [];
632
- for (const line of raw.split(`
633
- `)) {
634
- if (line.startsWith("data:")) {
635
- data.push(line.slice(line.startsWith("data: ") ? 6 : 5));
636
- } else if (line.startsWith("event:")) {
637
- event = line.slice(line.startsWith("event: ") ? 7 : 6).trim();
638
- }
639
- }
640
- return { event, data: data.length > 0 ? data.join(`
641
- `) : undefined };
642
- }
643
-
644
- // src/subgraphs/serialize.ts
645
- var SYSTEM_COLUMN_MAP = {
646
- _blockHeight: "_block_height",
647
- _txId: "_tx_id",
648
- _createdAt: "_created_at",
649
- _id: "_id"
650
- };
651
- var SYSTEM_COLUMN_ALIASES = {
652
- blockHeight: "_block_height",
653
- txId: "_tx_id",
654
- createdAt: "_created_at",
655
- id: "_id"
656
- };
657
- function resolveColumn(col, columns) {
658
- const canonical = SYSTEM_COLUMN_MAP[col];
659
- if (canonical)
660
- return canonical;
661
- if (columns?.has(col))
662
- return col;
663
- return SYSTEM_COLUMN_ALIASES[col] ?? col;
664
- }
665
- function serializeWhere(where, columns) {
666
- const filters = {};
667
- for (const [column, value] of Object.entries(where)) {
668
- if (value === null || value === undefined)
669
- continue;
670
- const col = resolveColumn(column, columns);
671
- if (typeof value === "object" && !Array.isArray(value)) {
672
- const ops = value;
673
- for (const [op, opValue] of Object.entries(ops)) {
674
- if (opValue === null || opValue === undefined)
675
- continue;
676
- if (op === "eq") {
677
- filters[col] = String(opValue);
678
- } else if (op === "in" || op === "notIn") {
679
- const arr = Array.isArray(opValue) ? opValue : [opValue];
680
- filters[`${col}.${op}`] = arr.map((v) => String(v)).join(",");
681
- } else if (["neq", "gt", "gte", "lt", "lte", "like"].includes(op)) {
682
- filters[`${col}.${op}`] = String(opValue);
683
- }
684
- }
685
- } else {
686
- filters[col] = String(value);
687
- }
688
- }
689
- return filters;
690
- }
691
- function resolveOrderByColumn(col, columns) {
692
- return resolveColumn(col, columns);
693
- }
694
-
695
- // src/subgraphs/client.ts
696
- var SUBSCRIBE_RECONNECT_DELAY_MS = 1000;
697
- function declaredColumns(table) {
698
- if (!table || typeof table !== "object")
699
- return;
700
- const columns = table.columns;
701
- if (!columns || typeof columns !== "object")
702
- return;
703
- return new Set(Object.keys(columns));
704
- }
705
- function rowBlockHeight(row) {
706
- const r = row;
707
- const raw = r._block_height ?? r._blockHeight;
708
- if (typeof raw !== "number" && typeof raw !== "string")
709
- return;
710
- const height = Number(raw);
711
- return Number.isFinite(height) ? height : undefined;
712
- }
713
- function sleep2(ms, signal) {
714
- return new Promise((resolve) => {
715
- if (signal.aborted)
716
- return resolve();
717
- const onAbort = () => {
718
- clearTimeout(timer);
719
- resolve();
720
- };
721
- const timer = setTimeout(() => {
722
- signal.removeEventListener("abort", onAbort);
723
- resolve();
724
- }, ms);
725
- signal.addEventListener("abort", onAbort, { once: true });
726
- });
727
- }
728
- function buildSubgraphQueryString(params) {
729
- return buildQuery({
730
- _sort: params.sort,
731
- _order: params.order,
732
- _limit: params.limit,
733
- _offset: params.offset,
734
- _fields: params.fields,
735
- ...params.filters
736
- });
737
- }
738
- function buildAggregateQueryString(params) {
739
- return buildQuery({
740
- ...params.filters,
741
- _count: params.count ? "true" : undefined,
742
- _countDistinct: params.countDistinct,
743
- _sum: params.sum,
744
- _min: params.min,
745
- _max: params.max
746
- });
747
- }
748
- function buildSpecQueryString(options) {
749
- return buildQuery({ server: options?.serverUrl });
750
- }
751
-
752
- class Subgraphs extends BaseClient {
753
- async list() {
754
- return this.request("GET", "/api/subgraphs");
755
- }
756
- async status(name) {
757
- return this.request("GET", `/api/subgraphs/${seg(name)}`);
758
- }
759
- async openapi(name, options) {
760
- return this.request("GET", `/api/subgraphs/${seg(name)}/openapi.json${buildSpecQueryString(options)}`);
761
- }
762
- async schema(name, options) {
763
- return this.request("GET", `/api/subgraphs/${seg(name)}/schema.json${buildSpecQueryString(options)}`);
764
- }
765
- async markdown(name, options) {
766
- return this.requestText("GET", `/api/subgraphs/${seg(name)}/docs.md${buildSpecQueryString(options)}`);
767
- }
768
- async reindex(name) {
769
- return this.request("POST", `/api/subgraphs/${seg(name)}/reindex`);
770
- }
771
- async stop(name) {
772
- return this.request("POST", `/api/subgraphs/${seg(name)}/stop`);
773
- }
774
- async backfill(name, options) {
775
- return this.request("POST", `/api/subgraphs/${seg(name)}/backfill`, options);
776
- }
777
- async gaps(name, opts) {
778
- const qs = buildQuery({
779
- _limit: opts?.limit,
780
- _offset: opts?.offset,
781
- resolved: opts?.resolved
782
- });
783
- return this.request("GET", `/api/subgraphs/${seg(name)}/gaps${qs}`);
784
- }
785
- async delete(name, options) {
786
- const qs = buildQuery({ force: options?.force ? true : undefined });
787
- return this.request("DELETE", `/api/subgraphs/${seg(name)}${qs}`);
788
- }
789
- async rows(name, table, params = {}) {
790
- const { cursor, ...rest } = params;
791
- const qs = buildSubgraphQueryString(rest);
792
- const sep = qs ? "&" : "?";
793
- const cursorQs = cursor ? `${sep}cursor=${encodeURIComponent(cursor)}` : "";
794
- return this.request("GET", `/v1/subgraphs/${seg(name)}/${seg(table)}${qs}${cursorQs}`);
795
- }
796
- async operations(name) {
797
- return this.request("GET", `/api/subgraphs/${seg(name)}/operations`);
798
- }
799
- async getOperation(name, operationId) {
800
- return this.request("GET", `/api/subgraphs/${seg(name)}/operations/${seg(operationId)}`);
801
- }
802
- async deploy(data) {
803
- return this.request("POST", "/api/subgraphs", data);
804
- }
805
- async getSource(name) {
806
- return this.request("GET", `/api/subgraphs/${seg(name)}/source`);
807
- }
808
- async bundle(data) {
809
- return this.request("POST", "/api/subgraphs/bundle", data);
810
- }
811
- async queryTable(name, table, params = {}) {
812
- const result = await this.request("GET", `/api/subgraphs/${seg(name)}/${seg(table)}${buildSubgraphQueryString(params)}`);
813
- return Array.isArray(result) ? result : result.data;
814
- }
815
- async queryTableCount(name, table, params = {}) {
816
- return this.request("GET", `/api/subgraphs/${seg(name)}/${seg(table)}/count${buildSubgraphQueryString(params)}`);
817
- }
818
- async queryTableAggregate(name, table, params = {}) {
819
- return this.request("GET", `/api/subgraphs/${seg(name)}/${seg(table)}/aggregate${buildAggregateQueryString(params)}`);
820
- }
821
- typed(def) {
822
- const result = {};
823
- for (const [tableName, table] of Object.entries(def.schema)) {
824
- result[tableName] = this.createTableClient(def.name, tableName, declaredColumns(table));
825
- }
826
- return result;
827
- }
828
- createTableClient(subgraphName, tableName, columns) {
829
- const self = this;
830
- return {
831
- async findMany(options = {}) {
832
- const filters = options.where ? serializeWhere(options.where, columns) : undefined;
833
- let sort;
834
- let order;
835
- if (options.orderBy) {
836
- const entries = Array.isArray(options.orderBy) ? options.orderBy : Object.entries(options.orderBy);
837
- if (entries.length > 0) {
838
- sort = entries.map(([col]) => resolveOrderByColumn(col, columns)).join(",");
839
- order = entries.map(([, dir]) => dir ?? "asc").join(",");
840
- }
841
- }
842
- const params = {
843
- sort,
844
- order,
845
- limit: options.limit,
846
- offset: options.offset,
847
- fields: options.fields?.join(","),
848
- filters
849
- };
850
- return self.queryTable(subgraphName, tableName, params);
851
- },
852
- async count(where) {
853
- const filters = where ? serializeWhere(where, columns) : undefined;
854
- const result = await self.queryTableCount(subgraphName, tableName, {
855
- filters
856
- });
857
- return result.count;
858
- },
859
- async aggregate(spec) {
860
- const filters = spec.where ? serializeWhere(spec.where, columns) : undefined;
861
- const result = await self.queryTableAggregate(subgraphName, tableName, {
862
- filters,
863
- count: spec.count,
864
- countDistinct: spec.countDistinct,
865
- sum: spec.sum,
866
- min: spec.min,
867
- max: spec.max
868
- });
869
- return result;
870
- },
871
- subscribe(onRow, options = {}) {
872
- const filters = options.where ? serializeWhere(options.where, columns) : {};
873
- const controller = new AbortController;
874
- let since = options.since ?? undefined;
875
- const run = async () => {
876
- while (!controller.signal.aborted) {
877
- try {
878
- const qs = buildQuery({ ...filters, since });
879
- await readSse({
880
- url: `${self.baseUrl}/v1/subgraphs/${seg(subgraphName)}/${seg(tableName)}/stream${qs}`,
881
- headers: {
882
- ...BaseClient.authHeaders(self.apiKey),
883
- "x-sl-origin": self.origin
884
- },
885
- signal: controller.signal,
886
- fetchImpl: self.fetchImpl,
887
- onFrame: (frame) => {
888
- if (frame.event === "ping" || !frame.data)
889
- return;
890
- let row;
891
- try {
892
- row = JSON.parse(frame.data);
893
- } catch {
894
- return;
895
- }
896
- const height = rowBlockHeight(row);
897
- if (height !== undefined)
898
- since = height;
899
- onRow(row);
900
- }
901
- });
902
- await sleep2(SUBSCRIBE_RECONNECT_DELAY_MS, controller.signal);
903
- } catch (err) {
904
- if (controller.signal.aborted)
905
- return;
906
- options.onError?.(err);
907
- await sleep2(SUBSCRIBE_RECONNECT_DELAY_MS, controller.signal);
908
- }
909
- }
910
- };
911
- run();
912
- return () => controller.abort();
913
- }
914
- };
915
- }
916
- }
917
- // src/archive/client.ts
918
- import { createHash } from "node:crypto";
919
- import {
920
- checkSignature,
921
- loadReference
922
- } from "@secondlayer/shared/archive/reference";
923
- var OFFICIAL_ARCHIVE_HOST = "archive.secondlayer.tools";
924
- var DEFAULT_ARCHIVE_BASE_URL = "https://archive.secondlayer.tools";
925
- var DEFAULT_ARCHIVE_OPS_URL = "https://api.secondlayer.tools";
926
- var MAX_FETCH_BATCH = 64;
927
- var ARCHIVE_CREDENTIAL_RE = /^s[ks]-sl_/;
928
- function quoteFromBody(body) {
929
- return {
930
- partitions: body.partitions,
931
- bundles: body.bundles,
932
- usdMicros: body.usd_micros,
933
- usd: body.usd,
934
- freeAllowanceAppliedMicros: body.free_allowance_applied_micros,
935
- allowanceRemainingBundles: body.allowance_remaining_bundles,
936
- balanceUsdMicros: body.balance_usd_micros,
937
- sufficient: body.sufficient
938
- };
939
- }
940
- function stripSlash(url) {
941
- return url.replace(/\/+$/, "");
942
- }
943
- function createArchiveClient(options = {}) {
944
- const accountKey = resolveAccountKey(options.accountKey ?? options.apiKey);
945
- const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
946
- const archiveBaseUrl = stripSlash(options.archiveBaseUrl ?? DEFAULT_ARCHIVE_BASE_URL);
947
- const archiveOpsUrl = stripSlash(options.archiveOpsUrl ?? DEFAULT_ARCHIVE_OPS_URL);
948
- const verifyManifest = options.verifyManifest ?? true;
949
- const pinnedPem = options.publicKeyPem;
950
- let keyPromise = null;
951
- async function fetchSigningKey() {
952
- if (pinnedPem)
953
- return pinnedPem;
954
- if (keyPromise)
955
- return keyPromise;
956
- const pending = (async () => {
957
- const res = await fetchImpl(`${archiveOpsUrl}/public/streams/signing-key`);
958
- if (!res.ok) {
959
- throw new ArchiveSignatureError(`Could not fetch archive signing key (${res.status}).`);
960
- }
961
- const body = await res.json();
962
- if (typeof body.public_key_pem !== "string") {
963
- throw new ArchiveSignatureError("Signing key response missing key.");
964
- }
965
- return body.public_key_pem;
966
- })();
967
- keyPromise = pending;
968
- pending.catch(() => {
969
- if (keyPromise === pending)
970
- keyPromise = null;
971
- });
972
- return pending;
973
- }
974
- async function throwOpsError(response) {
975
- const envelope = parseErrorEnvelope(await response.text());
976
- if (response.status === 401) {
977
- throw new ArchiveAuthError(envelope.message ?? "Archive ops request was not authenticated.");
978
- }
979
- if (response.status === 402) {
980
- const raw = envelope.body && typeof envelope.body === "object" && "shortfall_usd_micros" in envelope.body ? Number(envelope.body.shortfall_usd_micros) : undefined;
981
- throw new InsufficientArchiveCreditsError(envelope.message ?? "Insufficient archive credits.", Number.isFinite(raw) ? raw : undefined);
982
- }
983
- if (response.status === 503) {
984
- throw new ArchiveGateNotConfiguredError(envelope.message ?? "The archive gate is not configured on the server.");
985
- }
986
- throw new ApiError(response.status, envelope.message ?? `Archive ops returned ${response.status}.`, envelope.body, envelope.code);
987
- }
988
- async function opsRequest(method, path, body, authed = true) {
989
- const headers = {
990
- "Content-Type": "application/json"
991
- };
992
- if (authed && accountKey) {
993
- if (!ARCHIVE_CREDENTIAL_RE.test(accountKey)) {
994
- throw new ArchiveAuthError("archive ops need SECONDLAYER_API_KEY (sk-sl_*), not INSTANCE_TOKEN");
995
- }
996
- headers.Authorization = `Bearer ${accountKey}`;
997
- }
998
- const response = await fetchImpl(`${archiveOpsUrl}${path}`, {
999
- method,
1000
- headers,
1001
- body: body !== undefined ? JSON.stringify(body) : undefined
1002
- });
1003
- if (!response.ok)
1004
- await throwOpsError(response);
1005
- return await response.json();
1006
- }
1007
- async function load(against, opts = {}) {
1008
- if (!/^https?:\/\//i.test(against)) {
1009
- throw new ValidationError("archive.load() accepts only http(s) URLs; local paths stay on the CLI.", 400);
1010
- }
1011
- const insecure = opts.insecure === true;
1012
- const publicKeyPem = pinnedPem ?? (verifyManifest && !insecure ? await fetchSigningKey() : undefined);
1013
- const ref = await loadReference(against, {
1014
- publicKeyPem,
1015
- fetchImpl
1016
- });
1017
- const signature = checkSignature(ref.manifest, publicKeyPem, insecure);
1018
- if (verifyManifest && !insecure && !signature.verified) {
1019
- throw new ArchiveSignatureError(signature.reason ?? "Archive manifest signature is missing or invalid.");
1020
- }
1021
- return { ...ref, signature };
1022
- }
1023
- async function latest(against, opts = {}) {
1024
- return load(against ?? `${archiveBaseUrl}/latest.json`, opts);
535
+ async function latest(against, opts = {}) {
536
+ return load(against ?? `${archiveBaseUrl}/latest.json`, opts);
1025
537
  }
1026
538
  async function status() {
1027
539
  const response = await fetchImpl(`${archiveBaseUrl}/status.json`);
@@ -1178,6 +690,37 @@ import {
1178
690
  decodeStreamsCursor,
1179
691
  encodeStreamsCursor
1180
692
  } from "@secondlayer/shared";
693
+
694
+ // src/streams/errors.ts
695
+ class StreamsServerError extends ApiError {
696
+ constructor(message, status, body, code) {
697
+ super(status, message, body, code, { retryable: true });
698
+ this.name = "StreamsServerError";
699
+ }
700
+ }
701
+
702
+ class StreamsSignatureError extends SecondLayerError {
703
+ constructor(message = "Streams response signature verification failed.") {
704
+ super(message);
705
+ this.name = "StreamsSignatureError";
706
+ }
707
+ }
708
+ async function mapStreamsError(response) {
709
+ const { message, code, body } = parseErrorEnvelope(await response.text());
710
+ if (response.status === 401) {
711
+ throw new AuthError(message ?? "API key invalid or expired.", body, code);
712
+ }
713
+ if (response.status === 429) {
714
+ const retryAfter = response.headers.get("Retry-After") ?? undefined;
715
+ throw new RateLimitError(message ?? "Rate limited. Try again later.", retryAfter, body, code);
716
+ }
717
+ if (response.status >= 500) {
718
+ throw new StreamsServerError(message ?? `Streams server returned ${response.status}.`, response.status, body, code);
719
+ }
720
+ throw new ValidationError(message ?? `Streams request returned ${response.status}.`, response.status, body, code);
721
+ }
722
+
723
+ // src/streams/cursor.ts
1181
724
  var Cursor = {
1182
725
  atHeight(height) {
1183
726
  if (height <= 0)
@@ -1325,7 +868,7 @@ async function defaultSleep(ms, signal) {
1325
868
  });
1326
869
  }
1327
870
  async function consumeStreamsEvents(opts) {
1328
- const sleep3 = opts.sleep ?? defaultSleep;
871
+ const sleep = opts.sleep ?? defaultSleep;
1329
872
  const mode = opts.mode ?? "tail";
1330
873
  const finalizedOnly = opts.finalizedOnly ?? false;
1331
874
  const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
@@ -1355,7 +898,7 @@ async function consumeStreamsEvents(opts) {
1355
898
  retryCount: opts.retryCount,
1356
899
  retryDelay: opts.retryDelay,
1357
900
  onError: opts.onError,
1358
- sleep: sleep3,
901
+ sleep,
1359
902
  signal: opts.signal
1360
903
  });
1361
904
  pages++;
@@ -1387,7 +930,7 @@ async function consumeStreamsEvents(opts) {
1387
930
  retryCount: opts.retryCount,
1388
931
  retryDelay: opts.retryDelay,
1389
932
  onError: opts.onError,
1390
- sleep: sleep3,
933
+ sleep,
1391
934
  signal: opts.signal
1392
935
  });
1393
936
  if (listed.next_since !== null)
@@ -1450,7 +993,7 @@ async function consumeStreamsEvents(opts) {
1450
993
  if (mode === "bounded") {
1451
994
  return { cursor, pages, emptyPolls };
1452
995
  }
1453
- await sleep3(emptyBackoffMs, opts.signal);
996
+ await sleep(emptyBackoffMs, opts.signal);
1454
997
  continue;
1455
998
  }
1456
999
  return { cursor, pages, emptyPolls };
@@ -1458,7 +1001,7 @@ async function consumeStreamsEvents(opts) {
1458
1001
  return { cursor, pages, emptyPolls };
1459
1002
  }
1460
1003
  async function* iterateStreamsBatches(opts) {
1461
- const sleep3 = opts.sleep ?? defaultSleep;
1004
+ const sleep = opts.sleep ?? defaultSleep;
1462
1005
  let cursor = opts.fromCursor ?? null;
1463
1006
  while (!opts.signal?.aborted) {
1464
1007
  const envelope = await opts.fetchEvents({
@@ -1486,12 +1029,12 @@ async function* iterateStreamsBatches(opts) {
1486
1029
  if (!advanced && envelope.events.length === 0) {
1487
1030
  if (opts.signal?.aborted)
1488
1031
  return;
1489
- await sleep3(opts.intervalMs, opts.signal);
1032
+ await sleep(opts.intervalMs, opts.signal);
1490
1033
  }
1491
1034
  }
1492
1035
  }
1493
1036
  async function* streamStreamsEvents(opts) {
1494
- const sleep3 = opts.sleep ?? defaultSleep;
1037
+ const sleep = opts.sleep ?? defaultSleep;
1495
1038
  const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
1496
1039
  const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
1497
1040
  const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
@@ -1526,7 +1069,7 @@ async function* streamStreamsEvents(opts) {
1526
1069
  emptyPolls++;
1527
1070
  if (emptyPolls >= maxEmptyPolls || pages >= maxPages)
1528
1071
  return;
1529
- await sleep3(emptyBackoffMs, opts.signal);
1072
+ await sleep(emptyBackoffMs, opts.signal);
1530
1073
  continue;
1531
1074
  }
1532
1075
  return;
@@ -1535,7 +1078,7 @@ async function* streamStreamsEvents(opts) {
1535
1078
 
1536
1079
  // src/index-api/consumer.ts
1537
1080
  async function consumeIndexFeed(opts) {
1538
- const sleep3 = opts.sleep ?? defaultSleep;
1081
+ const sleep = opts.sleep ?? defaultSleep;
1539
1082
  const mode = opts.mode ?? "tail";
1540
1083
  const finalizedOnly = opts.finalizedOnly ?? false;
1541
1084
  const batchSize = opts.batchSize ?? 200;
@@ -1559,7 +1102,7 @@ async function consumeIndexFeed(opts) {
1559
1102
  retryCount: opts.retryCount,
1560
1103
  retryDelay: opts.retryDelay,
1561
1104
  onError: opts.onError,
1562
- sleep: sleep3,
1105
+ sleep,
1563
1106
  signal: opts.signal
1564
1107
  });
1565
1108
  pages++;
@@ -1622,7 +1165,7 @@ async function consumeIndexFeed(opts) {
1622
1165
  if (mode === "bounded") {
1623
1166
  return { cursor, pages, emptyPolls };
1624
1167
  }
1625
- await sleep3(emptyBackoffMs, opts.signal);
1168
+ await sleep(emptyBackoffMs, opts.signal);
1626
1169
  continue;
1627
1170
  }
1628
1171
  return { cursor, pages, emptyPolls };
@@ -2194,24 +1737,210 @@ function createStreamsDumps(opts) {
2194
1737
  chunks.push(value);
2195
1738
  total += value.byteLength;
2196
1739
  }
2197
- } catch {
2198
- throw new StreamsServerError(`Download of dump ${file.path} was interrupted after ${total} bytes.`, 0);
2199
- }
2200
- const digest = hash.digest("hex");
2201
- if (digest !== file.sha256) {
2202
- throw new StreamsSignatureError(`Dump ${file.path} sha256 mismatch (expected ${file.sha256}, got ${digest}).`);
1740
+ } catch {
1741
+ throw new StreamsServerError(`Download of dump ${file.path} was interrupted after ${total} bytes.`, 0);
1742
+ }
1743
+ const digest = hash.digest("hex");
1744
+ if (digest !== file.sha256) {
1745
+ throw new StreamsSignatureError(`Dump ${file.path} sha256 mismatch (expected ${file.sha256}, got ${digest}).`);
1746
+ }
1747
+ if (chunks.length === 1 && chunks[0])
1748
+ return chunks[0];
1749
+ const bytes = new Uint8Array(total);
1750
+ let offset = 0;
1751
+ for (const chunk of chunks) {
1752
+ bytes.set(chunk, offset);
1753
+ offset += chunk.byteLength;
1754
+ }
1755
+ return bytes;
1756
+ }
1757
+ return { list, fileUrl, download };
1758
+ }
1759
+
1760
+ // src/streams/subscribe.ts
1761
+ import { ed25519 } from "@secondlayer/shared";
1762
+ var SUBSCRIBE_MAX_RECONNECT_DELAY_MS = 30000;
1763
+ var SUBSCRIBE_DEFAULT_STALE_AFTER_MS = 60000;
1764
+ function reconnectDelay(attempt, baseMs, retryAfterSeconds, random = Math.random) {
1765
+ const capped = Math.min(baseMs * 2 ** Math.max(0, attempt), SUBSCRIBE_MAX_RECONNECT_DELAY_MS);
1766
+ const jittered = capped * (0.5 + random() * 0.5);
1767
+ const floor = (retryAfterSeconds ?? 0) * 1000;
1768
+ return Math.max(jittered, floor);
1769
+ }
1770
+ function isTerminal(err) {
1771
+ if (err instanceof StreamsSignatureError)
1772
+ return true;
1773
+ return err?.retryable === false;
1774
+ }
1775
+ function subscribeStreamsEvents(opts) {
1776
+ const { params } = opts;
1777
+ const controller = new AbortController;
1778
+ const external = params.signal;
1779
+ if (external) {
1780
+ if (external.aborted)
1781
+ controller.abort();
1782
+ else
1783
+ external.addEventListener("abort", () => controller.abort(), {
1784
+ once: true
1785
+ });
1786
+ }
1787
+ let cursor = params.fromCursor ?? null;
1788
+ const baseDelayMs = opts.reconnectDelayMs ?? params.reconnectDelayMs ?? 1000;
1789
+ const staleAfterMs = opts.staleAfterMs ?? params.staleAfterMs ?? SUBSCRIBE_DEFAULT_STALE_AFTER_MS;
1790
+ let attempt = 0;
1791
+ const run = async () => {
1792
+ while (!controller.signal.aborted) {
1793
+ try {
1794
+ const url = `${opts.baseUrl}/v1/streams/events/stream${buildQuery({
1795
+ from_cursor: cursor ?? undefined,
1796
+ types: params.types,
1797
+ not_types: params.notTypes,
1798
+ contract_id: params.contractId,
1799
+ sender: params.sender,
1800
+ recipient: params.recipient,
1801
+ asset_identifier: params.assetIdentifier,
1802
+ filters: params.filters ? JSON.stringify(params.filters) : undefined
1803
+ })}`;
1804
+ await readSse({
1805
+ url,
1806
+ headers: opts.headers ?? {},
1807
+ signal: controller.signal,
1808
+ fetchImpl: opts.fetchImpl,
1809
+ staleAfterMs,
1810
+ mapError: mapStreamsError,
1811
+ onFrame: async (frame) => {
1812
+ attempt = 0;
1813
+ if (frame.event === "ping" || !frame.data)
1814
+ return;
1815
+ let parsed;
1816
+ try {
1817
+ parsed = JSON.parse(frame.data);
1818
+ } catch {
1819
+ return;
1820
+ }
1821
+ if (!parsed.event)
1822
+ return;
1823
+ if (opts.verify !== "off") {
1824
+ if (!parsed.sig) {
1825
+ if (opts.verify === "strict") {
1826
+ throw new StreamsSignatureError("Streams SSE frame signature is missing.");
1827
+ }
1828
+ } else {
1829
+ const key = await opts.loadKey(parsed.key_id);
1830
+ if (!ed25519.verifyEd25519(JSON.stringify(parsed.event), parsed.sig, key.publicKey)) {
1831
+ throw new StreamsSignatureError("Streams SSE frame signature is invalid.");
1832
+ }
1833
+ }
1834
+ }
1835
+ await params.onEvent(parsed.event);
1836
+ cursor = parsed.event.cursor ?? cursor;
1837
+ }
1838
+ });
1839
+ await sleep(reconnectDelay(attempt, baseDelayMs, undefined, opts.random), controller.signal);
1840
+ attempt += 1;
1841
+ } catch (err) {
1842
+ if (controller.signal.aborted)
1843
+ return;
1844
+ params.onError?.(err);
1845
+ if (isTerminal(err))
1846
+ throw err;
1847
+ const retryAfterSeconds = err.retryAfterSeconds;
1848
+ await sleep(reconnectDelay(attempt, baseDelayMs, retryAfterSeconds, opts.random), controller.signal);
1849
+ attempt += 1;
1850
+ }
1851
+ }
1852
+ };
1853
+ const done = run();
1854
+ done.catch(() => {});
1855
+ const unsubscribe = () => controller.abort();
1856
+ return Object.assign(unsubscribe, { done });
1857
+ }
1858
+ async function readSse(opts) {
1859
+ const res = await opts.fetchImpl(opts.url, {
1860
+ headers: { ...opts.headers ?? {}, Accept: "text/event-stream" },
1861
+ signal: opts.signal
1862
+ });
1863
+ if (!res.ok) {
1864
+ if (opts.mapError)
1865
+ await opts.mapError(res);
1866
+ throw new StreamsServerError(`Streams SSE returned ${res.status}.`, res.status);
1867
+ }
1868
+ if (!res.body) {
1869
+ throw new StreamsServerError("Streams SSE response has no body.", 0);
1870
+ }
1871
+ for await (const frame of parseSseFrames(res.body, opts.signal, opts.staleAfterMs)) {
1872
+ await opts.onFrame(frame);
1873
+ }
1874
+ }
1875
+ function sleep(ms, signal) {
1876
+ return new Promise((resolve) => {
1877
+ if (signal.aborted)
1878
+ return resolve();
1879
+ const onAbort = () => {
1880
+ clearTimeout(timer);
1881
+ resolve();
1882
+ };
1883
+ const timer = setTimeout(() => {
1884
+ signal.removeEventListener("abort", onAbort);
1885
+ resolve();
1886
+ }, ms);
1887
+ signal.addEventListener("abort", onAbort, { once: true });
1888
+ });
1889
+ }
1890
+ async function readWithStaleTimer(reader, staleAfterMs) {
1891
+ if (staleAfterMs === undefined || !Number.isFinite(staleAfterMs)) {
1892
+ return reader.read();
1893
+ }
1894
+ let timer;
1895
+ const stale = new Promise((_, reject) => {
1896
+ timer = setTimeout(() => reject(new StreamsServerError(`Streams SSE went quiet for ${staleAfterMs} ms; reconnecting.`, 0, undefined, "STREAM_STALE")), staleAfterMs);
1897
+ });
1898
+ try {
1899
+ return await Promise.race([reader.read(), stale]);
1900
+ } finally {
1901
+ clearTimeout(timer);
1902
+ }
1903
+ }
1904
+ async function* parseSseFrames(body, signal, staleAfterMs) {
1905
+ const reader = body.getReader();
1906
+ const decoder = new TextDecoder;
1907
+ let buffer = "";
1908
+ try {
1909
+ while (!signal.aborted) {
1910
+ const { value, done } = await readWithStaleTimer(reader, staleAfterMs);
1911
+ if (done)
1912
+ break;
1913
+ buffer += decoder.decode(value, { stream: true });
1914
+ let sep = buffer.indexOf(`
1915
+
1916
+ `);
1917
+ while (sep !== -1) {
1918
+ yield parseFrame(buffer.slice(0, sep));
1919
+ buffer = buffer.slice(sep + 2);
1920
+ sep = buffer.indexOf(`
1921
+
1922
+ `);
1923
+ }
2203
1924
  }
2204
- if (chunks.length === 1 && chunks[0])
2205
- return chunks[0];
2206
- const bytes = new Uint8Array(total);
2207
- let offset = 0;
2208
- for (const chunk of chunks) {
2209
- bytes.set(chunk, offset);
2210
- offset += chunk.byteLength;
1925
+ } finally {
1926
+ try {
1927
+ await reader.cancel();
1928
+ } catch {}
1929
+ }
1930
+ }
1931
+ function parseFrame(raw) {
1932
+ let event;
1933
+ const data = [];
1934
+ for (const line of raw.split(`
1935
+ `)) {
1936
+ if (line.startsWith("data:")) {
1937
+ data.push(line.slice(line.startsWith("data: ") ? 6 : 5));
1938
+ } else if (line.startsWith("event:")) {
1939
+ event = line.slice(line.startsWith("event: ") ? 7 : 6).trim();
2211
1940
  }
2212
- return bytes;
2213
1941
  }
2214
- return { list, fileUrl, download };
1942
+ return { event, data: data.length > 0 ? data.join(`
1943
+ `) : undefined };
2215
1944
  }
2216
1945
 
2217
1946
  // src/streams/client.ts
@@ -2482,21 +2211,299 @@ function createStreamsClient(options) {
2482
2211
  signal: params.signal
2483
2212
  });
2484
2213
  }
2485
- },
2486
- blocks: {
2487
- events(heightOrHash) {
2488
- return request(`/v1/streams/blocks/${encodeURIComponent(String(heightOrHash))}/events`);
2489
- }
2490
- },
2491
- reorgs: { list: listReorgs },
2492
- dumps,
2493
- canonical(height) {
2494
- return request(`/v1/streams/canonical/${height}`);
2495
- },
2496
- tip() {
2497
- return request("/v1/streams/tip");
2498
- }
2499
- };
2214
+ },
2215
+ blocks: {
2216
+ events(heightOrHash) {
2217
+ return request(`/v1/streams/blocks/${encodeURIComponent(String(heightOrHash))}/events`);
2218
+ }
2219
+ },
2220
+ reorgs: { list: listReorgs },
2221
+ dumps,
2222
+ canonical(height) {
2223
+ return request(`/v1/streams/canonical/${height}`);
2224
+ },
2225
+ tip() {
2226
+ return request("/v1/streams/tip");
2227
+ }
2228
+ };
2229
+ }
2230
+
2231
+ // src/subgraphs/serialize.ts
2232
+ var SYSTEM_COLUMN_MAP = {
2233
+ _blockHeight: "_block_height",
2234
+ _txId: "_tx_id",
2235
+ _createdAt: "_created_at",
2236
+ _id: "_id"
2237
+ };
2238
+ var SYSTEM_COLUMN_ALIASES = {
2239
+ blockHeight: "_block_height",
2240
+ txId: "_tx_id",
2241
+ createdAt: "_created_at",
2242
+ id: "_id"
2243
+ };
2244
+ function resolveColumn(col, columns) {
2245
+ const canonical = SYSTEM_COLUMN_MAP[col];
2246
+ if (canonical)
2247
+ return canonical;
2248
+ if (columns?.has(col))
2249
+ return col;
2250
+ return SYSTEM_COLUMN_ALIASES[col] ?? col;
2251
+ }
2252
+ function serializeWhere(where, columns) {
2253
+ const filters = {};
2254
+ for (const [column, value] of Object.entries(where)) {
2255
+ if (value === null || value === undefined)
2256
+ continue;
2257
+ const col = resolveColumn(column, columns);
2258
+ if (typeof value === "object" && !Array.isArray(value)) {
2259
+ const ops = value;
2260
+ for (const [op, opValue] of Object.entries(ops)) {
2261
+ if (opValue === null || opValue === undefined)
2262
+ continue;
2263
+ if (op === "eq") {
2264
+ filters[col] = String(opValue);
2265
+ } else if (op === "in" || op === "notIn") {
2266
+ const arr = Array.isArray(opValue) ? opValue : [opValue];
2267
+ filters[`${col}.${op}`] = arr.map((v) => String(v)).join(",");
2268
+ } else if (["neq", "gt", "gte", "lt", "lte", "like"].includes(op)) {
2269
+ filters[`${col}.${op}`] = String(opValue);
2270
+ }
2271
+ }
2272
+ } else {
2273
+ filters[col] = String(value);
2274
+ }
2275
+ }
2276
+ return filters;
2277
+ }
2278
+ function resolveOrderByColumn(col, columns) {
2279
+ return resolveColumn(col, columns);
2280
+ }
2281
+
2282
+ // src/subgraphs/client.ts
2283
+ var SUBSCRIBE_RECONNECT_DELAY_MS = 1000;
2284
+ function declaredColumns(table) {
2285
+ if (!table || typeof table !== "object")
2286
+ return;
2287
+ const columns = table.columns;
2288
+ if (!columns || typeof columns !== "object")
2289
+ return;
2290
+ return new Set(Object.keys(columns));
2291
+ }
2292
+ function rowBlockHeight(row) {
2293
+ const r = row;
2294
+ const raw = r._block_height ?? r._blockHeight;
2295
+ if (typeof raw !== "number" && typeof raw !== "string")
2296
+ return;
2297
+ const height = Number(raw);
2298
+ return Number.isFinite(height) ? height : undefined;
2299
+ }
2300
+ function sleep2(ms, signal) {
2301
+ return new Promise((resolve) => {
2302
+ if (signal.aborted)
2303
+ return resolve();
2304
+ const onAbort = () => {
2305
+ clearTimeout(timer);
2306
+ resolve();
2307
+ };
2308
+ const timer = setTimeout(() => {
2309
+ signal.removeEventListener("abort", onAbort);
2310
+ resolve();
2311
+ }, ms);
2312
+ signal.addEventListener("abort", onAbort, { once: true });
2313
+ });
2314
+ }
2315
+ function buildSubgraphQueryString(params) {
2316
+ return buildQuery({
2317
+ _sort: params.sort,
2318
+ _order: params.order,
2319
+ _limit: params.limit,
2320
+ _fields: params.fields,
2321
+ ...params.filters
2322
+ });
2323
+ }
2324
+ function buildAggregateQueryString(params) {
2325
+ return buildQuery({
2326
+ ...params.filters,
2327
+ _count: params.count ? "true" : undefined,
2328
+ _countDistinct: params.countDistinct,
2329
+ _sum: params.sum,
2330
+ _min: params.min,
2331
+ _max: params.max
2332
+ });
2333
+ }
2334
+ function buildSpecQueryString(options) {
2335
+ return buildQuery({ server: options?.serverUrl });
2336
+ }
2337
+
2338
+ class Subgraphs extends BaseClient {
2339
+ async list() {
2340
+ return this.request("GET", "/api/subgraphs");
2341
+ }
2342
+ async status(name) {
2343
+ return this.request("GET", `/api/subgraphs/${seg(name)}`);
2344
+ }
2345
+ async openapi(name, options) {
2346
+ return this.request("GET", `/api/subgraphs/${seg(name)}/openapi.json${buildSpecQueryString(options)}`);
2347
+ }
2348
+ async schema(name, options) {
2349
+ return this.request("GET", `/api/subgraphs/${seg(name)}/schema.json${buildSpecQueryString(options)}`);
2350
+ }
2351
+ async markdown(name, options) {
2352
+ return this.requestText("GET", `/api/subgraphs/${seg(name)}/docs.md${buildSpecQueryString(options)}`);
2353
+ }
2354
+ async reindex(name) {
2355
+ return this.request("POST", `/api/subgraphs/${seg(name)}/reindex`);
2356
+ }
2357
+ async stop(name) {
2358
+ return this.request("POST", `/api/subgraphs/${seg(name)}/stop`);
2359
+ }
2360
+ async backfill(name, options) {
2361
+ return this.request("POST", `/api/subgraphs/${seg(name)}/backfill`, options);
2362
+ }
2363
+ async gaps(name, opts) {
2364
+ const qs = buildQuery({
2365
+ _limit: opts?.limit,
2366
+ _offset: opts?.offset,
2367
+ resolved: opts?.resolved
2368
+ });
2369
+ return this.request("GET", `/api/subgraphs/${seg(name)}/gaps${qs}`);
2370
+ }
2371
+ async delete(name, options) {
2372
+ const qs = buildQuery({ force: options?.force ? true : undefined });
2373
+ return this.request("DELETE", `/api/subgraphs/${seg(name)}${qs}`);
2374
+ }
2375
+ async rows(name, table, params = {}) {
2376
+ const { cursor, ...rest } = params;
2377
+ const qs = buildSubgraphQueryString(rest);
2378
+ const sep = qs ? "&" : "?";
2379
+ const cursorQs = cursor ? `${sep}cursor=${encodeURIComponent(cursor)}` : "";
2380
+ return this.request("GET", `/v1/subgraphs/${seg(name)}/${seg(table)}${qs}${cursorQs}`);
2381
+ }
2382
+ async operations(name) {
2383
+ return this.request("GET", `/api/subgraphs/${seg(name)}/operations`);
2384
+ }
2385
+ async getOperation(name, operationId) {
2386
+ return this.request("GET", `/api/subgraphs/${seg(name)}/operations/${seg(operationId)}`);
2387
+ }
2388
+ async deploy(data) {
2389
+ return this.request("POST", "/api/subgraphs", data);
2390
+ }
2391
+ async getSource(name) {
2392
+ return this.request("GET", `/api/subgraphs/${seg(name)}/source`);
2393
+ }
2394
+ async bundle(data) {
2395
+ return this.request("POST", "/api/subgraphs/bundle", data);
2396
+ }
2397
+ async count(name, table, params = {}) {
2398
+ return this.request("GET", `/v1/subgraphs/${seg(name)}/${seg(table)}/count${buildSubgraphQueryString(params)}`);
2399
+ }
2400
+ async aggregate(name, table, params = {}) {
2401
+ return this.request("GET", `/v1/subgraphs/${seg(name)}/${seg(table)}/aggregate${buildAggregateQueryString(params)}`);
2402
+ }
2403
+ typed(def) {
2404
+ const result = {};
2405
+ for (const [tableName, table] of Object.entries(def.schema)) {
2406
+ result[tableName] = this.createTableClient(def.name, tableName, declaredColumns(table));
2407
+ }
2408
+ return result;
2409
+ }
2410
+ createTableClient(subgraphName, tableName, columns) {
2411
+ const self = this;
2412
+ return {
2413
+ async findMany(options = {}) {
2414
+ const filters = options.where ? serializeWhere(options.where, columns) : undefined;
2415
+ let sort;
2416
+ let order;
2417
+ if (options.orderBy) {
2418
+ const entries = Object.entries(options.orderBy);
2419
+ if (entries.length > 1) {
2420
+ throw new Error(`findMany orderBy accepts a single column; got ${entries.length}: ${entries.map(([col]) => col).join(", ")}. Sort one column — the cursor pairs it with _id as a tiebreaker.`);
2421
+ }
2422
+ const entry = entries[0];
2423
+ if (entry) {
2424
+ const [col, dir] = entry;
2425
+ sort = resolveOrderByColumn(col, columns);
2426
+ order = dir ?? "asc";
2427
+ }
2428
+ }
2429
+ const result = await self.rows(subgraphName, tableName, {
2430
+ sort,
2431
+ order,
2432
+ limit: options.limit,
2433
+ fields: options.fields?.join(","),
2434
+ filters,
2435
+ cursor: options.cursor
2436
+ });
2437
+ return {
2438
+ rows: result.rows,
2439
+ nextCursor: result.next_cursor,
2440
+ tip: result.tip
2441
+ };
2442
+ },
2443
+ async count(where) {
2444
+ const filters = where ? serializeWhere(where, columns) : undefined;
2445
+ const result = await self.count(subgraphName, tableName, {
2446
+ filters
2447
+ });
2448
+ return result.count;
2449
+ },
2450
+ async aggregate(spec) {
2451
+ const filters = spec.where ? serializeWhere(spec.where, columns) : undefined;
2452
+ const result = await self.aggregate(subgraphName, tableName, {
2453
+ filters,
2454
+ count: spec.count,
2455
+ countDistinct: spec.countDistinct,
2456
+ sum: spec.sum,
2457
+ min: spec.min,
2458
+ max: spec.max
2459
+ });
2460
+ return result;
2461
+ },
2462
+ subscribe(onRow, options = {}) {
2463
+ const filters = options.where ? serializeWhere(options.where, columns) : {};
2464
+ const controller = new AbortController;
2465
+ let since = options.since ?? undefined;
2466
+ const run = async () => {
2467
+ while (!controller.signal.aborted) {
2468
+ try {
2469
+ const qs = buildQuery({ ...filters, since });
2470
+ await readSse({
2471
+ url: `${self.baseUrl}/v1/subgraphs/${seg(subgraphName)}/${seg(tableName)}/stream${qs}`,
2472
+ headers: {
2473
+ ...BaseClient.authHeaders(self.apiKey),
2474
+ "x-sl-origin": self.origin
2475
+ },
2476
+ signal: controller.signal,
2477
+ fetchImpl: self.fetchImpl,
2478
+ onFrame: (frame) => {
2479
+ if (frame.event === "ping" || !frame.data)
2480
+ return;
2481
+ let row;
2482
+ try {
2483
+ row = JSON.parse(frame.data);
2484
+ } catch {
2485
+ return;
2486
+ }
2487
+ const height = rowBlockHeight(row);
2488
+ if (height !== undefined)
2489
+ since = height;
2490
+ onRow(row);
2491
+ }
2492
+ });
2493
+ await sleep2(SUBSCRIBE_RECONNECT_DELAY_MS, controller.signal);
2494
+ } catch (err) {
2495
+ if (controller.signal.aborted)
2496
+ return;
2497
+ options.onError?.(err);
2498
+ await sleep2(SUBSCRIBE_RECONNECT_DELAY_MS, controller.signal);
2499
+ }
2500
+ }
2501
+ };
2502
+ run();
2503
+ return () => controller.abort();
2504
+ }
2505
+ };
2506
+ }
2500
2507
  }
2501
2508
 
2502
2509
  // src/webhooks/client.ts
@@ -2672,7 +2679,6 @@ class SecondLayer extends BaseClient {
2672
2679
  };
2673
2680
  }
2674
2681
  }
2675
-
2676
2682
  // src/subgraphs/get-subgraph.ts
2677
2683
  function getSubgraph(def, options = {}) {
2678
2684
  if (options instanceof Subgraphs) {
@@ -2683,36 +2689,6 @@ function getSubgraph(def, options = {}) {
2683
2689
  }
2684
2690
  return new Subgraphs(options).typed(def);
2685
2691
  }
2686
- // src/streams/index.ts
2687
- import {
2688
- decodeFtTransfer,
2689
- isFtTransfer,
2690
- decodeNftTransfer,
2691
- isNftTransfer,
2692
- decodeStxBurn,
2693
- decodeStxLock,
2694
- decodeStxMint,
2695
- decodeStxTransfer,
2696
- isStxBurn,
2697
- isStxLock,
2698
- isStxMint,
2699
- isStxTransfer,
2700
- decodeFtBurn,
2701
- decodeFtMint,
2702
- decodeNftBurn,
2703
- decodeNftMint,
2704
- isFtBurn,
2705
- isFtMint,
2706
- isNftBurn,
2707
- isNftMint,
2708
- decodePrint,
2709
- isPrint
2710
- } from "@secondlayer/shared/streams-rows";
2711
-
2712
- // src/streams/types.ts
2713
- import {
2714
- STREAMS_EVENT_TYPES
2715
- } from "@secondlayer/shared/streams-rows";
2716
2692
  // src/consumer-ops.ts
2717
2693
  function consumerHealth(options = {}) {
2718
2694
  const staleAfterMs = options.staleAfterMs ?? 120000;
@@ -2764,12 +2740,12 @@ import {
2764
2740
  } from "@secondlayer/shared/crypto/standard-webhooks";
2765
2741
  function pickHeader(headers, name) {
2766
2742
  if (typeof headers === "function") {
2767
- const v2 = headers(name);
2768
- return typeof v2 === "string" ? v2 : undefined;
2743
+ const v = headers(name);
2744
+ return typeof v === "string" ? v : undefined;
2769
2745
  }
2770
2746
  if (typeof headers.get === "function" && !(name in headers)) {
2771
- const v2 = headers.get(name);
2772
- return typeof v2 === "string" ? v2 : undefined;
2747
+ const v = headers.get(name);
2748
+ return typeof v === "string" ? v : undefined;
2773
2749
  }
2774
2750
  const bag = headers;
2775
2751
  let v = bag[name] ?? bag[name.toLowerCase()];
@@ -2906,79 +2882,109 @@ function verifyTransactionProof(proof, opts) {
2906
2882
  errors
2907
2883
  };
2908
2884
  }
2885
+ // src/streams/index.ts
2886
+ import {
2887
+ decodeFtTransfer,
2888
+ isFtTransfer,
2889
+ decodeNftTransfer,
2890
+ isNftTransfer,
2891
+ decodeStxBurn,
2892
+ decodeStxLock,
2893
+ decodeStxMint,
2894
+ decodeStxTransfer,
2895
+ isStxBurn,
2896
+ isStxLock,
2897
+ isStxMint,
2898
+ isStxTransfer,
2899
+ decodeFtBurn,
2900
+ decodeFtMint,
2901
+ decodeNftBurn,
2902
+ decodeNftMint,
2903
+ isFtBurn,
2904
+ isFtMint,
2905
+ isNftBurn,
2906
+ isNftMint,
2907
+ decodePrint,
2908
+ isPrint
2909
+ } from "@secondlayer/shared/streams-rows";
2910
+
2911
+ // src/streams/types.ts
2912
+ import {
2913
+ STREAMS_EVENT_TYPES
2914
+ } from "@secondlayer/shared/streams-rows";
2909
2915
  // src/clarity.ts
2910
2916
  import {
2911
2917
  decodeClarityValue,
2912
2918
  toJsonSafe
2913
2919
  } from "@secondlayer/shared/streams-rows";
2914
2920
  export {
2915
- verifyWebhookSignature,
2916
- verifyTransactionProof,
2917
- verify2 as verifyStandardWebhooksHeaders,
2918
- verifySecondlayerSignature,
2919
- trigger,
2920
- toJsonSafe,
2921
- shutdownSignal,
2922
- resolveBaseUrl,
2923
- resolveApiKey,
2924
- resolveAccountKey,
2925
- parseRetryAfter,
2926
- isStxTransfer,
2927
- isStxMint,
2928
- isStxLock,
2929
- isStxBurn,
2930
- isPrint,
2931
- isNftTransfer,
2932
- isNftMint,
2933
- isNftBurn,
2934
- isFtTransfer,
2935
- isFtMint,
2936
- isFtBurn,
2937
- getSubgraph,
2938
- fetchRewardSet,
2939
- decodeStxTransfer,
2940
- decodeStxMint,
2941
- decodeStxLock,
2942
- decodeStxBurn,
2943
- decodePrint,
2944
- decodeNftTransfer,
2945
- decodeNftMint,
2946
- decodeNftBurn,
2947
- decodeFtTransfer,
2948
- decodeFtMint,
2949
- decodeFtBurn,
2950
- decodeClarityValue,
2951
- decodeChainWebhook,
2952
- decode,
2953
- createStreamsClient,
2954
- createArchiveClient,
2955
- contextField,
2956
- consumerHealth,
2957
- consumeIndexFeed,
2958
- Webhooks,
2959
- ValidationError,
2960
- Subgraphs,
2961
- StreamsSignatureError,
2962
- StreamsServerError,
2963
- SecondLayerError,
2964
- SecondLayer,
2965
- RateLimitError,
2966
- MemoryEtagCache,
2967
- LOCAL_API_URL,
2968
- InsufficientArchiveCreditsError,
2969
- Index,
2970
- INSTANCE_TOKEN_ENV,
2971
- INDEX_MAX_PAGE_SIZE,
2972
- DEFAULT_REQUEST_TIMEOUT_MS,
2973
- Cursor,
2974
- Contracts,
2975
- AuthError,
2976
- ArchiveSignatureError,
2977
- ArchiveGateNotConfiguredError,
2978
- ArchiveAuthError,
2921
+ ACCOUNT_KEY_ENV,
2979
2922
  ApiError,
2980
- ACCOUNT_KEY_ENV
2923
+ ArchiveAuthError,
2924
+ ArchiveGateNotConfiguredError,
2925
+ ArchiveSignatureError,
2926
+ AuthError,
2927
+ Contracts,
2928
+ Cursor,
2929
+ DEFAULT_REQUEST_TIMEOUT_MS,
2930
+ INDEX_MAX_PAGE_SIZE,
2931
+ INSTANCE_TOKEN_ENV,
2932
+ Index,
2933
+ InsufficientArchiveCreditsError,
2934
+ LOCAL_API_URL,
2935
+ MemoryEtagCache,
2936
+ RateLimitError,
2937
+ SecondLayer,
2938
+ SecondLayerError,
2939
+ StreamsServerError,
2940
+ StreamsSignatureError,
2941
+ Subgraphs,
2942
+ ValidationError,
2943
+ Webhooks,
2944
+ consumeIndexFeed,
2945
+ consumerHealth,
2946
+ contextField,
2947
+ createArchiveClient,
2948
+ createStreamsClient,
2949
+ decode,
2950
+ decodeChainWebhook,
2951
+ decodeClarityValue,
2952
+ decodeFtBurn,
2953
+ decodeFtMint,
2954
+ decodeFtTransfer,
2955
+ decodeNftBurn,
2956
+ decodeNftMint,
2957
+ decodeNftTransfer,
2958
+ decodePrint,
2959
+ decodeStxBurn,
2960
+ decodeStxLock,
2961
+ decodeStxMint,
2962
+ decodeStxTransfer,
2963
+ fetchRewardSet,
2964
+ getSubgraph,
2965
+ isFtBurn,
2966
+ isFtMint,
2967
+ isFtTransfer,
2968
+ isNftBurn,
2969
+ isNftMint,
2970
+ isNftTransfer,
2971
+ isPrint,
2972
+ isStxBurn,
2973
+ isStxLock,
2974
+ isStxMint,
2975
+ isStxTransfer,
2976
+ parseRetryAfter,
2977
+ resolveAccountKey,
2978
+ resolveApiKey,
2979
+ resolveBaseUrl,
2980
+ shutdownSignal,
2981
+ toJsonSafe,
2982
+ trigger,
2983
+ verifySecondlayerSignature,
2984
+ verify2 as verifyStandardWebhooksHeaders,
2985
+ verifyTransactionProof,
2986
+ verifyWebhookSignature
2981
2987
  };
2982
2988
 
2983
- //# debugId=0B87C7F2FE748A2E64756E2164756E21
2989
+ //# debugId=8B8A60AF0DDA966664756E2164756E21
2984
2990
  //# sourceMappingURL=index.js.map