@secondlayer/sdk 5.8.0 → 5.9.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/README.md +1 -1
- package/dist/index.d.ts +104 -1
- package/dist/index.js +152 -6
- package/dist/index.js.map +8 -7
- package/dist/streams/index.d.ts +104 -1
- package/dist/streams/index.js +152 -6
- package/dist/streams/index.js.map +8 -7
- package/dist/subgraphs/index.d.ts +85 -0
- package/dist/subgraphs/index.js +151 -6
- package/dist/subgraphs/index.js.map +7 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ console.log({ tip, firstCursor: page.events[0]?.cursor });
|
|
|
50
50
|
import { createStreamsClient } from "@secondlayer/sdk";
|
|
51
51
|
|
|
52
52
|
const streams = createStreamsClient({
|
|
53
|
-
apiKey: process.env.
|
|
53
|
+
apiKey: process.env.SL_API_KEY!, // sk-sl_...
|
|
54
54
|
});
|
|
55
55
|
```
|
|
56
56
|
|
package/dist/index.d.ts
CHANGED
|
@@ -359,11 +359,21 @@ type StreamsEvent = {
|
|
|
359
359
|
contract_id: string | null
|
|
360
360
|
payload: StreamsEventPayload
|
|
361
361
|
ts: string
|
|
362
|
+
/**
|
|
363
|
+
* True when this event's block is past the finality boundary (immutable).
|
|
364
|
+
* Optional for back-compat; the API always sets it on Streams responses.
|
|
365
|
+
*/
|
|
366
|
+
finalized?: boolean
|
|
362
367
|
};
|
|
363
368
|
type StreamsTip = {
|
|
364
369
|
block_height: number
|
|
365
370
|
block_hash: string
|
|
366
371
|
burn_block_height: number
|
|
372
|
+
/**
|
|
373
|
+
* Highest Stacks block past the burn-confirmation finality boundary.
|
|
374
|
+
* Optional for back-compat; the API always sets it.
|
|
375
|
+
*/
|
|
376
|
+
finalized_height?: number
|
|
367
377
|
lag_seconds: number
|
|
368
378
|
};
|
|
369
379
|
type StreamsCanonicalBlock = {
|
|
@@ -403,12 +413,18 @@ type StreamsEventsListParams = {
|
|
|
403
413
|
toHeight?: number
|
|
404
414
|
types?: readonly StreamsEventType[]
|
|
405
415
|
contractId?: string
|
|
416
|
+
sender?: string
|
|
417
|
+
recipient?: string
|
|
418
|
+
assetIdentifier?: string
|
|
406
419
|
limit?: number
|
|
407
420
|
};
|
|
408
421
|
type StreamsEventsStreamParams = {
|
|
409
422
|
fromCursor?: string | null
|
|
410
423
|
types?: readonly StreamsEventType[]
|
|
411
424
|
contractId?: string
|
|
425
|
+
sender?: string
|
|
426
|
+
recipient?: string
|
|
427
|
+
assetIdentifier?: string
|
|
412
428
|
batchSize?: number
|
|
413
429
|
emptyBackoffMs?: number
|
|
414
430
|
maxPages?: number
|
|
@@ -420,6 +436,9 @@ type StreamsEventsConsumeParams = {
|
|
|
420
436
|
mode?: "tail" | "bounded"
|
|
421
437
|
types?: readonly StreamsEventType[]
|
|
422
438
|
contractId?: string
|
|
439
|
+
sender?: string
|
|
440
|
+
recipient?: string
|
|
441
|
+
assetIdentifier?: string
|
|
423
442
|
batchSize?: number
|
|
424
443
|
onBatch: (events: StreamsEvent[], envelope: StreamsEventsEnvelope) => Promise<string | null | undefined> | string | null | undefined
|
|
425
444
|
emptyBackoffMs?: number
|
|
@@ -432,7 +451,63 @@ type StreamsEventsConsumeResult = {
|
|
|
432
451
|
pages: number
|
|
433
452
|
emptyPolls: number
|
|
434
453
|
};
|
|
454
|
+
type StreamsEventsReplayParams = {
|
|
455
|
+
/** Start point: `"genesis"` (default) or a `<block>:<index>` cursor. */
|
|
456
|
+
from?: "genesis" | string
|
|
457
|
+
/**
|
|
458
|
+
* Called once per finalized dump file, in block order, before live tailing.
|
|
459
|
+
* Process the parquet with your own tooling (e.g. DuckDB) — the SDK does not
|
|
460
|
+
* decode parquet. Use `client.dumps.download(file)` to fetch + verify bytes.
|
|
461
|
+
*/
|
|
462
|
+
onDumpFile: (file: StreamsDumpFile) => Promise<void> | void
|
|
463
|
+
/** Called per live page after the dump phase, like `consume`. */
|
|
464
|
+
onBatch: (events: StreamsEvent[], envelope: StreamsEventsEnvelope) => Promise<string | null | undefined> | string | null | undefined
|
|
465
|
+
mode?: "tail" | "bounded"
|
|
466
|
+
batchSize?: number
|
|
467
|
+
emptyBackoffMs?: number
|
|
468
|
+
maxPages?: number
|
|
469
|
+
maxEmptyPolls?: number
|
|
470
|
+
signal?: AbortSignal
|
|
471
|
+
};
|
|
435
472
|
type FetchLike2 = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
473
|
+
/** One bulk parquet file in the dumps manifest. `path` is the object key under
|
|
474
|
+
* the dumps base URL. */
|
|
475
|
+
type StreamsDumpFile = {
|
|
476
|
+
path: string
|
|
477
|
+
from_block: number
|
|
478
|
+
to_block: number
|
|
479
|
+
min_cursor: string | null
|
|
480
|
+
max_cursor: string | null
|
|
481
|
+
row_count: number
|
|
482
|
+
byte_size: number
|
|
483
|
+
sha256: string
|
|
484
|
+
schema_version: number
|
|
485
|
+
created_at: string
|
|
486
|
+
};
|
|
487
|
+
type StreamsDumpsManifest = {
|
|
488
|
+
dataset: string
|
|
489
|
+
network: string
|
|
490
|
+
version: string
|
|
491
|
+
schema_version: number
|
|
492
|
+
generated_at: string
|
|
493
|
+
producer_version: string
|
|
494
|
+
finality_lag_blocks: number
|
|
495
|
+
/** Cursor at the end of the finalized bulk coverage — hand to live tailing. */
|
|
496
|
+
latest_finalized_cursor: string | null
|
|
497
|
+
coverage: {
|
|
498
|
+
from_block: number
|
|
499
|
+
to_block: number
|
|
500
|
+
}
|
|
501
|
+
files: StreamsDumpFile[]
|
|
502
|
+
};
|
|
503
|
+
type StreamsDumps = {
|
|
504
|
+
/** Fetch and parse the latest dumps manifest. */
|
|
505
|
+
list(): Promise<StreamsDumpsManifest>
|
|
506
|
+
/** Absolute URL for a manifest file. */
|
|
507
|
+
fileUrl(file: StreamsDumpFile): string
|
|
508
|
+
/** Download a parquet file and verify its sha256 against the manifest. */
|
|
509
|
+
download(file: StreamsDumpFile): Promise<Uint8Array>
|
|
510
|
+
};
|
|
436
511
|
type StreamsClient = {
|
|
437
512
|
events: {
|
|
438
513
|
list(params?: StreamsEventsListParams): Promise<StreamsEventsEnvelope>
|
|
@@ -448,6 +523,14 @@ type StreamsClient = {
|
|
|
448
523
|
*/
|
|
449
524
|
consume(params: StreamsEventsConsumeParams): Promise<StreamsEventsConsumeResult>
|
|
450
525
|
/**
|
|
526
|
+
* Backfill from bulk dumps, then continue live from the dump→live seam in
|
|
527
|
+
* one call. Iterates finalized dump files (via `onDumpFile`) in block
|
|
528
|
+
* order, then tails live from the manifest's `latest_finalized_cursor`
|
|
529
|
+
* (exclusive input → no gap or duplicate at the seam). Requires
|
|
530
|
+
* `dumpsBaseUrl`.
|
|
531
|
+
*/
|
|
532
|
+
replay(params: StreamsEventsReplayParams): Promise<StreamsEventsConsumeResult>
|
|
533
|
+
/**
|
|
451
534
|
* Follow Streams as an async iterator.
|
|
452
535
|
*
|
|
453
536
|
* Use `stream` for live processors and watch-style apps. It tails
|
|
@@ -462,6 +545,8 @@ type StreamsClient = {
|
|
|
462
545
|
reorgs: {
|
|
463
546
|
list(params: StreamsReorgsListParams): Promise<StreamsReorgsListEnvelope>
|
|
464
547
|
}
|
|
548
|
+
/** Bulk parquet dumps. Requires `dumpsBaseUrl` on the client. */
|
|
549
|
+
dumps: StreamsDumps
|
|
465
550
|
canonical(height: number): Promise<StreamsCanonicalBlock>
|
|
466
551
|
tip(): Promise<StreamsTip>
|
|
467
552
|
};
|
|
@@ -523,6 +608,20 @@ type CreateStreamsClientOptions = {
|
|
|
523
608
|
apiKey: string
|
|
524
609
|
baseUrl?: string
|
|
525
610
|
fetchImpl?: FetchLike2
|
|
611
|
+
/**
|
|
612
|
+
* Public base URL for bulk parquet dumps (the R2/CDN bucket root). Required
|
|
613
|
+
* to use `client.dumps`. See `GET /public/streams/dumps/manifest`.
|
|
614
|
+
*/
|
|
615
|
+
dumpsBaseUrl?: string
|
|
616
|
+
/**
|
|
617
|
+
* Verify the ed25519 `X-Signature` on every response (default off). Pass
|
|
618
|
+
* `true` to fetch the server's public key from
|
|
619
|
+
* `/public/streams/signing-key`, or `{ publicKey }` to pin a known PEM. A
|
|
620
|
+
* failed or missing signature throws `StreamsSignatureError`.
|
|
621
|
+
*/
|
|
622
|
+
verify?: boolean | {
|
|
623
|
+
publicKey: string
|
|
624
|
+
}
|
|
526
625
|
};
|
|
527
626
|
declare function createStreamsClient(options: CreateStreamsClientOptions): StreamsClient;
|
|
528
627
|
declare class AuthError extends Error {
|
|
@@ -544,6 +643,10 @@ declare class StreamsServerError extends Error {
|
|
|
544
643
|
readonly body?: unknown;
|
|
545
644
|
constructor(message: string, status: number, body?: unknown);
|
|
546
645
|
}
|
|
646
|
+
/** Thrown when response signature verification is enabled and fails. */
|
|
647
|
+
declare class StreamsSignatureError extends Error {
|
|
648
|
+
constructor(message?: string);
|
|
649
|
+
}
|
|
547
650
|
type FtTransferPayload = {
|
|
548
651
|
asset_identifier: string
|
|
549
652
|
sender: string
|
|
@@ -1054,4 +1157,4 @@ declare function toJsonSafe(value: unknown): unknown;
|
|
|
1054
1157
|
/** Decode a hex-encoded Clarity value to JSON-safe JS (uints as strings,
|
|
1055
1158
|
* buffers as `0x…` hex, tuples as objects). Returns the input hex on failure. */
|
|
1056
1159
|
declare function decodeClarityValue(hex: string): unknown;
|
|
1057
|
-
export { verifyWebhookSignature, 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, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsTip, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorg, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsClient, StreamsCanonicalBlock, SecondLayerOptions, SecondLayer, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, IndexTip, IndexEventType, IndexEvent, IndexContractCall, 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, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, CURSOR_SLUGS, AuthError, ApiError };
|
|
1160
|
+
export { verifyWebhookSignature, 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, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorg, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, SecondLayerOptions, SecondLayer, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, IndexTip, IndexEventType, IndexEvent, IndexContractCall, 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, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, CURSOR_SLUGS, AuthError, ApiError };
|
package/dist/index.js
CHANGED
|
@@ -501,6 +501,9 @@ class Index extends BaseClient {
|
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
// src/streams/client.ts
|
|
505
|
+
import { ed25519 } from "@secondlayer/shared";
|
|
506
|
+
|
|
504
507
|
// src/streams/consumer.ts
|
|
505
508
|
async function defaultSleep(ms, signal) {
|
|
506
509
|
if (signal?.aborted)
|
|
@@ -529,7 +532,10 @@ async function consumeStreamsEvents(opts) {
|
|
|
529
532
|
cursor,
|
|
530
533
|
limit: opts.batchSize,
|
|
531
534
|
types: opts.types,
|
|
532
|
-
contractId: opts.contractId
|
|
535
|
+
contractId: opts.contractId,
|
|
536
|
+
sender: opts.sender,
|
|
537
|
+
recipient: opts.recipient,
|
|
538
|
+
assetIdentifier: opts.assetIdentifier
|
|
533
539
|
});
|
|
534
540
|
pages++;
|
|
535
541
|
const returnedCursor = await opts.onBatch(envelope.events, envelope);
|
|
@@ -564,7 +570,10 @@ async function* streamStreamsEvents(opts) {
|
|
|
564
570
|
cursor,
|
|
565
571
|
limit: opts.batchSize,
|
|
566
572
|
types: opts.types,
|
|
567
|
-
contractId: opts.contractId
|
|
573
|
+
contractId: opts.contractId,
|
|
574
|
+
sender: opts.sender,
|
|
575
|
+
recipient: opts.recipient,
|
|
576
|
+
assetIdentifier: opts.assetIdentifier
|
|
568
577
|
});
|
|
569
578
|
pages++;
|
|
570
579
|
for (const event of envelope.events) {
|
|
@@ -589,6 +598,9 @@ async function* streamStreamsEvents(opts) {
|
|
|
589
598
|
}
|
|
590
599
|
}
|
|
591
600
|
|
|
601
|
+
// src/streams/dumps.ts
|
|
602
|
+
import { createHash } from "node:crypto";
|
|
603
|
+
|
|
592
604
|
// src/streams/errors.ts
|
|
593
605
|
class AuthError extends Error {
|
|
594
606
|
status = 401;
|
|
@@ -630,7 +642,60 @@ class StreamsServerError extends Error {
|
|
|
630
642
|
}
|
|
631
643
|
}
|
|
632
644
|
|
|
645
|
+
class StreamsSignatureError extends Error {
|
|
646
|
+
constructor(message = "Streams response signature verification failed.") {
|
|
647
|
+
super(message);
|
|
648
|
+
this.name = "StreamsSignatureError";
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/streams/dumps.ts
|
|
653
|
+
function createStreamsDumps(opts) {
|
|
654
|
+
const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
|
|
655
|
+
function requireBaseUrl() {
|
|
656
|
+
if (!baseUrl) {
|
|
657
|
+
throw new StreamsServerError("Streams dumps require `dumpsBaseUrl` on createStreamsClient.", 0);
|
|
658
|
+
}
|
|
659
|
+
return baseUrl;
|
|
660
|
+
}
|
|
661
|
+
function fileUrl(file) {
|
|
662
|
+
return `${requireBaseUrl()}/${file.path.replace(/^\/+/, "")}`;
|
|
663
|
+
}
|
|
664
|
+
async function list() {
|
|
665
|
+
const url = `${requireBaseUrl()}/manifest/latest.json`;
|
|
666
|
+
const res = await opts.fetchImpl(url);
|
|
667
|
+
if (!res.ok) {
|
|
668
|
+
throw new StreamsServerError(`Could not fetch dumps manifest (${res.status}).`, res.status);
|
|
669
|
+
}
|
|
670
|
+
return await res.json();
|
|
671
|
+
}
|
|
672
|
+
async function download(file) {
|
|
673
|
+
const res = await opts.fetchImpl(fileUrl(file));
|
|
674
|
+
if (!res.ok) {
|
|
675
|
+
throw new StreamsServerError(`Could not download dump ${file.path} (${res.status}).`, res.status);
|
|
676
|
+
}
|
|
677
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
678
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
679
|
+
if (digest !== file.sha256) {
|
|
680
|
+
throw new StreamsSignatureError(`Dump ${file.path} sha256 mismatch (expected ${file.sha256}, got ${digest}).`);
|
|
681
|
+
}
|
|
682
|
+
return bytes;
|
|
683
|
+
}
|
|
684
|
+
return { list, fileUrl, download };
|
|
685
|
+
}
|
|
686
|
+
|
|
633
687
|
// src/streams/client.ts
|
|
688
|
+
function cursorTuple(cursor) {
|
|
689
|
+
if (!cursor)
|
|
690
|
+
return [-1, -1];
|
|
691
|
+
const [block, index] = cursor.split(":");
|
|
692
|
+
return [Number(block), Number(index)];
|
|
693
|
+
}
|
|
694
|
+
function maxCursor(a, b) {
|
|
695
|
+
const [ah, ai] = cursorTuple(a);
|
|
696
|
+
const [bh, bi] = cursorTuple(b);
|
|
697
|
+
return ah > bh || ah === bh && ai >= bi ? a : b;
|
|
698
|
+
}
|
|
634
699
|
var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
|
|
635
700
|
function normalizeBaseUrl(baseUrl) {
|
|
636
701
|
return baseUrl.replace(/\/+$/, "");
|
|
@@ -678,21 +743,68 @@ async function mapStreamsError(response) {
|
|
|
678
743
|
function createStreamsClient(options) {
|
|
679
744
|
const baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_STREAMS_BASE_URL);
|
|
680
745
|
const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
746
|
+
const verify = options.verify ?? false;
|
|
747
|
+
const dumps = createStreamsDumps({
|
|
748
|
+
baseUrl: options.dumpsBaseUrl,
|
|
749
|
+
fetchImpl
|
|
750
|
+
});
|
|
751
|
+
let publicKeyPromise = null;
|
|
752
|
+
function getPublicKey() {
|
|
753
|
+
if (publicKeyPromise)
|
|
754
|
+
return publicKeyPromise;
|
|
755
|
+
publicKeyPromise = (async () => {
|
|
756
|
+
if (typeof verify === "object") {
|
|
757
|
+
return ed25519.loadEd25519PublicKey(verify.publicKey);
|
|
758
|
+
}
|
|
759
|
+
const res = await fetchImpl(`${baseUrl}/public/streams/signing-key`);
|
|
760
|
+
if (!res.ok) {
|
|
761
|
+
throw new StreamsSignatureError(`Could not fetch signing key (${res.status}).`);
|
|
762
|
+
}
|
|
763
|
+
const body = await res.json();
|
|
764
|
+
if (!body.public_key_pem) {
|
|
765
|
+
throw new StreamsSignatureError("Signing key response missing key.");
|
|
766
|
+
}
|
|
767
|
+
return ed25519.loadEd25519PublicKey(body.public_key_pem);
|
|
768
|
+
})();
|
|
769
|
+
return publicKeyPromise;
|
|
770
|
+
}
|
|
681
771
|
async function request(path) {
|
|
682
772
|
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
683
773
|
headers: { Authorization: `Bearer ${options.apiKey}` }
|
|
684
774
|
});
|
|
685
775
|
if (!response.ok)
|
|
686
776
|
await mapStreamsError(response);
|
|
687
|
-
|
|
777
|
+
const text = await response.text();
|
|
778
|
+
if (verify) {
|
|
779
|
+
const signature = response.headers.get("X-Signature");
|
|
780
|
+
if (!signature) {
|
|
781
|
+
throw new StreamsSignatureError("Response is missing X-Signature.");
|
|
782
|
+
}
|
|
783
|
+
const publicKey = await getPublicKey();
|
|
784
|
+
if (!ed25519.verifyEd25519(text, signature, publicKey)) {
|
|
785
|
+
throw new StreamsSignatureError;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return JSON.parse(text);
|
|
688
789
|
}
|
|
689
790
|
const fetchEvents = async ({
|
|
690
791
|
cursor,
|
|
691
792
|
limit,
|
|
692
793
|
types,
|
|
693
|
-
contractId
|
|
794
|
+
contractId,
|
|
795
|
+
sender,
|
|
796
|
+
recipient,
|
|
797
|
+
assetIdentifier
|
|
694
798
|
}) => {
|
|
695
|
-
return listEvents({
|
|
799
|
+
return listEvents({
|
|
800
|
+
cursor,
|
|
801
|
+
limit,
|
|
802
|
+
types,
|
|
803
|
+
contractId,
|
|
804
|
+
sender,
|
|
805
|
+
recipient,
|
|
806
|
+
assetIdentifier
|
|
807
|
+
});
|
|
696
808
|
};
|
|
697
809
|
async function listEvents(params = {}) {
|
|
698
810
|
const searchParams = new URLSearchParams;
|
|
@@ -701,6 +813,9 @@ function createStreamsClient(options) {
|
|
|
701
813
|
appendSearchParam2(searchParams, "to_height", params.toHeight);
|
|
702
814
|
appendSearchParam2(searchParams, "limit", params.limit);
|
|
703
815
|
appendSearchParam2(searchParams, "contract_id", params.contractId);
|
|
816
|
+
appendSearchParam2(searchParams, "sender", params.sender);
|
|
817
|
+
appendSearchParam2(searchParams, "recipient", params.recipient);
|
|
818
|
+
appendSearchParam2(searchParams, "asset_identifier", params.assetIdentifier);
|
|
704
819
|
if (params.types?.length) {
|
|
705
820
|
searchParams.set("types", params.types.join(","));
|
|
706
821
|
}
|
|
@@ -719,6 +834,9 @@ function createStreamsClient(options) {
|
|
|
719
834
|
mode: params.mode,
|
|
720
835
|
types: params.types,
|
|
721
836
|
contractId: params.contractId,
|
|
837
|
+
sender: params.sender,
|
|
838
|
+
recipient: params.recipient,
|
|
839
|
+
assetIdentifier: params.assetIdentifier,
|
|
722
840
|
batchSize: params.batchSize ?? 100,
|
|
723
841
|
fetchEvents,
|
|
724
842
|
onBatch: params.onBatch,
|
|
@@ -733,6 +851,9 @@ function createStreamsClient(options) {
|
|
|
733
851
|
fromCursor: params.fromCursor,
|
|
734
852
|
types: params.types,
|
|
735
853
|
contractId: params.contractId,
|
|
854
|
+
sender: params.sender,
|
|
855
|
+
recipient: params.recipient,
|
|
856
|
+
assetIdentifier: params.assetIdentifier,
|
|
736
857
|
batchSize: params.batchSize ?? 100,
|
|
737
858
|
emptyBackoffMs: params.emptyBackoffMs,
|
|
738
859
|
maxPages: params.maxPages,
|
|
@@ -740,6 +861,29 @@ function createStreamsClient(options) {
|
|
|
740
861
|
signal: params.signal,
|
|
741
862
|
fetchEvents
|
|
742
863
|
});
|
|
864
|
+
},
|
|
865
|
+
async replay(params) {
|
|
866
|
+
const fromCursor = params.from === "genesis" ? null : params.from ?? null;
|
|
867
|
+
const fromBlock = fromCursor ? cursorTuple(fromCursor)[0] : 0;
|
|
868
|
+
const manifest = await dumps.list();
|
|
869
|
+
const files = manifest.files.filter((file) => file.to_block >= fromBlock).sort((a, b) => a.from_block - b.from_block || a.to_block - b.to_block);
|
|
870
|
+
for (const file of files) {
|
|
871
|
+
if (params.signal?.aborted)
|
|
872
|
+
break;
|
|
873
|
+
await params.onDumpFile(file);
|
|
874
|
+
}
|
|
875
|
+
const seam = maxCursor(fromCursor, manifest.latest_finalized_cursor);
|
|
876
|
+
return consumeStreamsEvents({
|
|
877
|
+
fromCursor: seam,
|
|
878
|
+
mode: params.mode ?? "tail",
|
|
879
|
+
batchSize: params.batchSize ?? 100,
|
|
880
|
+
fetchEvents,
|
|
881
|
+
onBatch: params.onBatch,
|
|
882
|
+
emptyBackoffMs: params.emptyBackoffMs,
|
|
883
|
+
maxPages: params.maxPages,
|
|
884
|
+
maxEmptyPolls: params.maxEmptyPolls,
|
|
885
|
+
signal: params.signal
|
|
886
|
+
});
|
|
743
887
|
}
|
|
744
888
|
},
|
|
745
889
|
blocks: {
|
|
@@ -756,6 +900,7 @@ function createStreamsClient(options) {
|
|
|
756
900
|
return request(`/v1/streams/reorgs${query ? `?${query}` : ""}`);
|
|
757
901
|
}
|
|
758
902
|
},
|
|
903
|
+
dumps,
|
|
759
904
|
canonical(height) {
|
|
760
905
|
return request(`/v1/streams/canonical/${height}`);
|
|
761
906
|
},
|
|
@@ -1402,6 +1547,7 @@ export {
|
|
|
1402
1547
|
ValidationError,
|
|
1403
1548
|
Subscriptions,
|
|
1404
1549
|
Subgraphs,
|
|
1550
|
+
StreamsSignatureError,
|
|
1405
1551
|
StreamsServerError,
|
|
1406
1552
|
SecondLayer,
|
|
1407
1553
|
RateLimitError,
|
|
@@ -1412,5 +1558,5 @@ export {
|
|
|
1412
1558
|
ApiError
|
|
1413
1559
|
};
|
|
1414
1560
|
|
|
1415
|
-
//# debugId=
|
|
1561
|
+
//# debugId=54289A1292072EB864756E2164756E21
|
|
1416
1562
|
//# sourceMappingURL=index.js.map
|