@secondlayer/sdk 12.1.0 → 12.3.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 +50 -3
- package/dist/index.js +291 -12
- package/dist/index.js.map +6 -6
- package/dist/streams/index.d.ts +11 -0
- package/dist/streams/index.js +7 -3
- package/dist/streams/index.js.map +5 -5
- package/dist/subgraphs/index.d.ts +11 -0
- package/dist/subgraphs/index.js +7 -3
- package/dist/subgraphs/index.js.map +4 -4
- package/dist/webhooks/doctor.d.ts +39 -3
- package/dist/webhooks/doctor.js +285 -10
- package/dist/webhooks/doctor.js.map +3 -3
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -871,6 +871,17 @@ type StreamsEventsConsumeParams<
|
|
|
871
871
|
maxPages?: number;
|
|
872
872
|
maxEmptyPolls?: number;
|
|
873
873
|
signal?: AbortSignal;
|
|
874
|
+
/**
|
|
875
|
+
* Cuts the empty-poll backoff short: on an empty page, the loop races
|
|
876
|
+
* `emptyBackoffMs` against this promise instead of sleeping the full
|
|
877
|
+
* amount. Pass a function that resolves once when new data MIGHT be
|
|
878
|
+
* available (e.g. a Postgres NOTIFY listener) — it is called fresh on
|
|
879
|
+
* every empty page, never awaited more than once concurrently per page.
|
|
880
|
+
* The backoff timer still runs underneath, so a wake source that never
|
|
881
|
+
* resolves (or a `wake` this call omits) degrades to the same polling
|
|
882
|
+
* behavior as before.
|
|
883
|
+
*/
|
|
884
|
+
wake?: () => Promise<void>;
|
|
874
885
|
};
|
|
875
886
|
/**
|
|
876
887
|
* One yielded page from {@link StreamsClient.consume} — the
|
|
@@ -2789,17 +2800,40 @@ declare function getSubgraph<T extends {
|
|
|
2789
2800
|
}>(def: T, options?: Partial<SecondLayerOptions> | SecondLayer | Subgraphs): InferSubgraphClient2<T>;
|
|
2790
2801
|
import { SubgraphAgentSchema as SubgraphAgentSchema3, SubgraphSpecFormat as SubgraphSpecFormat2, SubgraphSpecOptions as SubgraphSpecOptions3 } from "@secondlayer/shared/subgraphs/spec";
|
|
2791
2802
|
import { SubgraphDetail as SubgraphDetail2 } from "@secondlayer/shared/schemas/subgraphs";
|
|
2792
|
-
import { DeadRow as DeadRow3, DeliveryRow as DeliveryRow3, WebhookDetail as WebhookDetail3 } from "@secondlayer/shared/schemas/webhooks";
|
|
2803
|
+
import { DeadRow as DeadRow3, DeliveryRow as DeliveryRow3, WebhookDetail as WebhookDetail3, WebhookSummary as WebhookSummary3 } from "@secondlayer/shared/schemas/webhooks";
|
|
2793
2804
|
/**
|
|
2794
2805
|
* Webhook diagnosis, shared by `secondlayer webhooks doctor` and the
|
|
2795
2806
|
* dashboard's webhook detail page. Moved out of the CLI (unchanged behavior)
|
|
2796
2807
|
* so the web can build the same judgment without printing CLI-command text.
|
|
2808
|
+
*
|
|
2809
|
+
* Plan 068 adds five deterministic, numbers-only detectors on top of the
|
|
2810
|
+
* existing flag checks: no model, no history beyond the deliveries window
|
|
2811
|
+
* already fetched. Thresholds are named constants below, conservative on
|
|
2812
|
+
* purpose — a wrong insight costs more trust than a missed one.
|
|
2797
2813
|
*/
|
|
2798
2814
|
declare function isSuccessDelivery(row: DeliveryRow3): boolean;
|
|
2799
|
-
type
|
|
2815
|
+
type DoctorSeverity = "bad" | "warn" | "calm";
|
|
2816
|
+
interface DoctorEvidence {
|
|
2817
|
+
label: string;
|
|
2818
|
+
value: string;
|
|
2819
|
+
}
|
|
2820
|
+
interface DoctorFix {
|
|
2821
|
+
text: string;
|
|
2822
|
+
/** A `secondlayer webhooks ...` command the reader can copy. */
|
|
2823
|
+
command?: string;
|
|
2824
|
+
/** Anchor into the docs' webhooks troubleshooting section. */
|
|
2825
|
+
docsPath?: string;
|
|
2826
|
+
}
|
|
2827
|
+
type DoctorIssueCode = "warning" | "paused" | "last_error" | "circuit" | "dead_letters" | "subgraph_gaps" | "subgraph_catching_up" | "no_deliveries" | "receiver_rate_limited" | "receiver_down" | "receiver_rejects" | "receiver_slow" | "delivery_lag";
|
|
2800
2828
|
interface DoctorIssue {
|
|
2801
2829
|
code: DoctorIssueCode;
|
|
2802
2830
|
detail?: string;
|
|
2831
|
+
severity: DoctorSeverity;
|
|
2832
|
+
/** 2-4 numbers-only facts backing the issue. Only the five new detectors
|
|
2833
|
+
* and the list-summary circuit issue carry this — the older flag checks
|
|
2834
|
+
* keep their plain `detail` string. */
|
|
2835
|
+
evidence?: DoctorEvidence[];
|
|
2836
|
+
fix?: DoctorFix;
|
|
2803
2837
|
}
|
|
2804
2838
|
interface DoctorReport {
|
|
2805
2839
|
webhook: WebhookDetail3;
|
|
@@ -2822,6 +2856,10 @@ interface DoctorReport {
|
|
|
2822
2856
|
/** CLI-facing next steps, one per issue, in the same order as `issues`. */
|
|
2823
2857
|
hints: string[];
|
|
2824
2858
|
issues: DoctorIssue[];
|
|
2859
|
+
/** The single most severe issue (bad > warn > calm, then rule priority),
|
|
2860
|
+
* or `null` when there are none. The UI leads with this and collapses
|
|
2861
|
+
* the rest underneath. */
|
|
2862
|
+
primary: DoctorIssue | null;
|
|
2825
2863
|
}
|
|
2826
2864
|
declare function buildDoctorReport(input: {
|
|
2827
2865
|
webhook: WebhookDetail3;
|
|
@@ -2830,6 +2868,15 @@ declare function buildDoctorReport(input: {
|
|
|
2830
2868
|
subgraph?: SubgraphDetail2 | null;
|
|
2831
2869
|
}): DoctorReport;
|
|
2832
2870
|
/**
|
|
2871
|
+
* The list page's own reduced detector: computed from `WebhookSummary` alone
|
|
2872
|
+
* (no deliveries, no dead-letter rows — the list endpoint returns neither,
|
|
2873
|
+
* and fetching per-row detail or deliveries here would fan out one request
|
|
2874
|
+
* per row). Only `status` and `circuitOpenedAt` distinguish a real problem
|
|
2875
|
+
* from a manual pause at this level; deliveries-based rules (rate limits,
|
|
2876
|
+
* downtime, rejects, slowness, lag) only ever show on the detail page.
|
|
2877
|
+
*/
|
|
2878
|
+
declare function buildListIssue(webhook: Pick<WebhookSummary3, "status" | "circuitOpenedAt" | "lastSuccessAt">): DoctorIssue | null;
|
|
2879
|
+
/**
|
|
2833
2880
|
* The two pieces every "deploy a consumer" guide leaves out, packaged:
|
|
2834
2881
|
* a liveness endpoint and a graceful-shutdown signal. Both are
|
|
2835
2882
|
* runtime-agnostic — the SDK never binds a port or assumes Bun/Node.
|
|
@@ -3258,4 +3305,4 @@ declare const Cursor: {
|
|
|
3258
3305
|
};
|
|
3259
3306
|
};
|
|
3260
3307
|
import { decodeClarityValue, toJsonSafe } from "@secondlayer/shared/streams-rows";
|
|
3261
|
-
export { ACCOUNT_KEY_ENV, ActiveSubgraphOperation, ApiError, ArchiveAuthError, ArchiveClient, ArchiveCreditsBalance, ArchiveDataset, ArchiveFetchItem, ArchiveFetchResult, ArchiveFlow, ArchiveGateNotConfiguredError, ArchiveLoadOptions, ArchiveManifest, ArchivePartition, ArchiveQuote, ArchiveSignatureError, ArchiveStatus, ArchiveVerifyInput, ArchiveVerifyRangeStatus, ArchiveVerifyResult, AuthError, BlockEnvelope, BlocksEnvelope, BlocksListParams, BlocksWalkParams, CanonicalEnvelope, CanonicalListParams, CanonicalWalkParams, ChainApplyDeliveryOf, ChainApplyEnvelope, ChainApplyEnvelopeOf, ChainEventEnvelope, ChainFtBurnData, ChainFtMintData, ChainFtTransferData, ChainNftBurnData, ChainNftMintData, ChainNftTransferData, ChainPrintEventData, ChainReorgOrphanedEntry, ChainReorgRollbackDelivery, ChainReorgRollbackEnvelope, ChainStxBurnData, ChainStxLockData, ChainStxMintData, ChainStxTransferData, ChainTestDelivery, ChainTrigger, ChainTriggerType, ChainTxLevelEvent, ChainWebhookDelivery2 as ChainWebhookDelivery, ChainWebhookEnvelope, ConsumerBatchContext, ConsumerHealth, ConsumerSink, ContextAccount, ContextField, ContextFieldError, ContextSnapshot, ContractCallsConsumeParams, ContractCallsEnvelope, ContractCallsListParams, ContractCallsWalkParams, ContractConformance, ContractSummary, Contracts, ContractsEnvelope, ContractsListParams, CreateArchiveClientOptions, CreateWebhookRequest2 as CreateWebhookRequest, CreateWebhookResponse2 as CreateWebhookResponse, Cursor, DEFAULT_REQUEST_TIMEOUT_MS, DeadRow2 as DeadRow, DecodedEventColumns, DecodedEventRow, DecodedFtBurn, DecodedFtBurnPayload, DecodedFtMint, DecodedFtMintPayload, DecodedFtTransfer, DecodedFtTransferPayload, DecodedNftBurn, DecodedNftBurnPayload, DecodedNftMint, DecodedNftMintPayload, DecodedNftTransfer, DecodedNftTransferPayload, DecodedPrint, DecodedPrintPayload, DecodedPrintValue, DecodedStxBurn, DecodedStxBurnPayload, DecodedStxLock, DecodedStxLockPayload, DecodedStxMint, DecodedStxMintPayload, DecodedStxTransfer, DecodedStxTransferPayload, DeliveryRow2 as DeliveryRow, DoctorIssue, DoctorIssueCode, DoctorReport, EtagCache, EtagEntry, EventsConsumeParams, EventsEnvelope, EventsListParams, EventsWalkParams, FetchLike2 as FetchLike, FtTransfer, FtTransferEvent, FtTransferPayload2 as FtTransferPayload, FtTransfersEnvelope, FtTransfersListParams, FtTransfersResource, FtTransfersWalkParams, INDEX_MAX_PAGE_SIZE, INSTANCE_TOKEN_ENV, Index, IndexAlwaysFields, IndexBlock, IndexCanonicalBlock, IndexConsumeOptions, IndexContractCall, IndexDiscovery, IndexEvent, IndexEventFields, IndexEventOf, IndexEventType, IndexEventTypeFilters, IndexEventsResource, IndexFeedEnvelope, IndexFeedFetcher, IndexFeedItem, IndexFtBurn, IndexFtMint, IndexFtTransfer, IndexMapDelete, IndexMapInsert, IndexMapSet, IndexMempoolTransaction, IndexNestedContractCall, IndexNftBurn, IndexNftMint, IndexNftTransfer, IndexPostCondition, IndexPox5Event, IndexPox5EventTopic, IndexPoxCycle, IndexPoxFunctionCount, IndexPrint, IndexReorg, IndexSbtcDeposit, IndexSbtcDepositDetail, IndexSbtcEvent, IndexSbtcSummary, IndexSbtcWithdrawal, IndexSbtcWithdrawalDetail, IndexSbtcWithdrawalPhase, IndexStackingAction, IndexStxBurn, IndexStxLock, IndexStxMint, IndexStxTransfer, IndexTip, IndexTransaction, IndexVarSet, InstanceClient, InstanceDiagnosis3 as InstanceDiagnosis, InstanceIssue, InstanceState, InstanceStatus, InsufficientArchiveCreditsError, LOCAL_API_URL, LoadedArchive, MemoryEtagCache, MempoolEnvelope, MempoolListParams, MempoolTransactionEnvelope, MempoolWalkParams, NftTransfer, NftTransferEvent, NftTransferPayload2 as NftTransferPayload, NftTransfersEnvelope, NftTransfersListParams, NftTransfersResource, NftTransfersWalkParams, Pox5EventsEnvelope, Pox5EventsListParams, Pox5EventsWalkParams, Pox5Resource, PoxCycleEnvelope, PoxCyclesEnvelope, PoxCyclesListParams, PoxCyclesWalkParams, PoxResource, PrintSchemaResponse, PublicStatus2 as PublicStatus, RateLimitError, ReplayResult2 as ReplayResult, RequestOptions, RewardSet2 as RewardSet, RotateSecretResponse2 as RotateSecretResponse, SbtcDepositEnvelope, SbtcDepositEvent, SbtcDepositsConsumeParams, SbtcDepositsEnvelope, SbtcDepositsListParams, SbtcDepositsWalkParams, SbtcEventTopic2 as SbtcEventTopic, SbtcEventsConsumeParams, SbtcEventsEnvelope, SbtcEventsListParams, SbtcEventsWalkParams, SbtcResource, SbtcSummaryEnvelope, SbtcWithdrawalEnvelope, SbtcWithdrawalEvent, SbtcWithdrawalStatus, SbtcWithdrawalSweptConfirmedEvent, SbtcWithdrawalsEnvelope, SbtcWithdrawalsListParams, SbtcWithdrawalsWalkParams, SecondLayer, SecondLayerArchive, SecondLayerError, SecondLayerErrorOptions, SecondLayerOptions, SinkTx, StackingEnvelope, StackingListParams, StackingWalkParams, StandardWebhooksHeaders2 as StandardWebhooksHeaders, StreamsBatch, StreamsBatchContext, StreamsCanonicalBlock, StreamsClient, StreamsConsumeParams, StreamsDumpFile, StreamsDumps, StreamsDumpsManifest, StreamsEvent, StreamsEventPayload, StreamsEventType, StreamsEventsConsumeParams, StreamsEventsConsumeResult, StreamsEventsEnvelope, StreamsEventsListEnvelope, StreamsEventsListParams, StreamsEventsStreamParams, StreamsEventsSubscribeParams, StreamsReorg, StreamsReorgContext, StreamsReorgsListEnvelope, StreamsReorgsListParams, StreamsServerError, StreamsSignatureError, StreamsSubscription, StreamsTip, StreamsWireEvent2 as StreamsWireEvent, SubgraphAgentSchema3 as SubgraphAgentSchema, SubgraphOperationStatus, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphSpecOptions3 as SubgraphSpecOptions, Subgraphs, TransactionEnvelope, TransactionProof, TransactionProofVerifyResult, TransactionsEnvelope, TransactionsListParams, TransactionsWalkParams, UpdateWebhookRequest2 as UpdateWebhookRequest, ValidationError, VmStreamsEvent, WalkOptions, WebhookDetail2 as WebhookDetail, WebhookFormat, WebhookHeaderInput, WebhookKind, WebhookRuntime, WebhookStatus, WebhookSummary2 as WebhookSummary, WebhookTestResult2 as WebhookTestResult, Webhooks, WithSinkTx, buildDoctorReport, consumeIndexFeed, consumerHealth, contextField, createArchiveClient, createStreamsClient, decode, decodeChainWebhook, decodeClarityValue, decodeFtBurn, decodeFtMint, decodeFtTransfer, decodeNftBurn, decodeNftMint, decodeNftTransfer, decodePrint, decodeStxBurn, decodeStxLock, decodeStxMint, decodeStxTransfer, fetchRewardSet, getSubgraph, isFtBurn, isFtMint, isFtTransfer, isNftBurn, isNftMint, isNftTransfer, isPrint, isStxBurn, isStxLock, isStxMint, isStxTransfer, isSuccessDelivery, parseRetryAfter, resolveAccountKey, resolveApiKey, resolveBaseUrl, shutdownSignal, toJsonSafe, trigger, verifySecondlayerSignature, verify as verifyStandardWebhooksHeaders, verifyTransactionProof, verifyWebhookSignature };
|
|
3308
|
+
export { ACCOUNT_KEY_ENV, ActiveSubgraphOperation, ApiError, ArchiveAuthError, ArchiveClient, ArchiveCreditsBalance, ArchiveDataset, ArchiveFetchItem, ArchiveFetchResult, ArchiveFlow, ArchiveGateNotConfiguredError, ArchiveLoadOptions, ArchiveManifest, ArchivePartition, ArchiveQuote, ArchiveSignatureError, ArchiveStatus, ArchiveVerifyInput, ArchiveVerifyRangeStatus, ArchiveVerifyResult, AuthError, BlockEnvelope, BlocksEnvelope, BlocksListParams, BlocksWalkParams, CanonicalEnvelope, CanonicalListParams, CanonicalWalkParams, ChainApplyDeliveryOf, ChainApplyEnvelope, ChainApplyEnvelopeOf, ChainEventEnvelope, ChainFtBurnData, ChainFtMintData, ChainFtTransferData, ChainNftBurnData, ChainNftMintData, ChainNftTransferData, ChainPrintEventData, ChainReorgOrphanedEntry, ChainReorgRollbackDelivery, ChainReorgRollbackEnvelope, ChainStxBurnData, ChainStxLockData, ChainStxMintData, ChainStxTransferData, ChainTestDelivery, ChainTrigger, ChainTriggerType, ChainTxLevelEvent, ChainWebhookDelivery2 as ChainWebhookDelivery, ChainWebhookEnvelope, ConsumerBatchContext, ConsumerHealth, ConsumerSink, ContextAccount, ContextField, ContextFieldError, ContextSnapshot, ContractCallsConsumeParams, ContractCallsEnvelope, ContractCallsListParams, ContractCallsWalkParams, ContractConformance, ContractSummary, Contracts, ContractsEnvelope, ContractsListParams, CreateArchiveClientOptions, CreateWebhookRequest2 as CreateWebhookRequest, CreateWebhookResponse2 as CreateWebhookResponse, Cursor, DEFAULT_REQUEST_TIMEOUT_MS, DeadRow2 as DeadRow, DecodedEventColumns, DecodedEventRow, DecodedFtBurn, DecodedFtBurnPayload, DecodedFtMint, DecodedFtMintPayload, DecodedFtTransfer, DecodedFtTransferPayload, DecodedNftBurn, DecodedNftBurnPayload, DecodedNftMint, DecodedNftMintPayload, DecodedNftTransfer, DecodedNftTransferPayload, DecodedPrint, DecodedPrintPayload, DecodedPrintValue, DecodedStxBurn, DecodedStxBurnPayload, DecodedStxLock, DecodedStxLockPayload, DecodedStxMint, DecodedStxMintPayload, DecodedStxTransfer, DecodedStxTransferPayload, DeliveryRow2 as DeliveryRow, DoctorEvidence, DoctorFix, DoctorIssue, DoctorIssueCode, DoctorReport, DoctorSeverity, EtagCache, EtagEntry, EventsConsumeParams, EventsEnvelope, EventsListParams, EventsWalkParams, FetchLike2 as FetchLike, FtTransfer, FtTransferEvent, FtTransferPayload2 as FtTransferPayload, FtTransfersEnvelope, FtTransfersListParams, FtTransfersResource, FtTransfersWalkParams, INDEX_MAX_PAGE_SIZE, INSTANCE_TOKEN_ENV, Index, IndexAlwaysFields, IndexBlock, IndexCanonicalBlock, IndexConsumeOptions, IndexContractCall, IndexDiscovery, IndexEvent, IndexEventFields, IndexEventOf, IndexEventType, IndexEventTypeFilters, IndexEventsResource, IndexFeedEnvelope, IndexFeedFetcher, IndexFeedItem, IndexFtBurn, IndexFtMint, IndexFtTransfer, IndexMapDelete, IndexMapInsert, IndexMapSet, IndexMempoolTransaction, IndexNestedContractCall, IndexNftBurn, IndexNftMint, IndexNftTransfer, IndexPostCondition, IndexPox5Event, IndexPox5EventTopic, IndexPoxCycle, IndexPoxFunctionCount, IndexPrint, IndexReorg, IndexSbtcDeposit, IndexSbtcDepositDetail, IndexSbtcEvent, IndexSbtcSummary, IndexSbtcWithdrawal, IndexSbtcWithdrawalDetail, IndexSbtcWithdrawalPhase, IndexStackingAction, IndexStxBurn, IndexStxLock, IndexStxMint, IndexStxTransfer, IndexTip, IndexTransaction, IndexVarSet, InstanceClient, InstanceDiagnosis3 as InstanceDiagnosis, InstanceIssue, InstanceState, InstanceStatus, InsufficientArchiveCreditsError, LOCAL_API_URL, LoadedArchive, MemoryEtagCache, MempoolEnvelope, MempoolListParams, MempoolTransactionEnvelope, MempoolWalkParams, NftTransfer, NftTransferEvent, NftTransferPayload2 as NftTransferPayload, NftTransfersEnvelope, NftTransfersListParams, NftTransfersResource, NftTransfersWalkParams, Pox5EventsEnvelope, Pox5EventsListParams, Pox5EventsWalkParams, Pox5Resource, PoxCycleEnvelope, PoxCyclesEnvelope, PoxCyclesListParams, PoxCyclesWalkParams, PoxResource, PrintSchemaResponse, PublicStatus2 as PublicStatus, RateLimitError, ReplayResult2 as ReplayResult, RequestOptions, RewardSet2 as RewardSet, RotateSecretResponse2 as RotateSecretResponse, SbtcDepositEnvelope, SbtcDepositEvent, SbtcDepositsConsumeParams, SbtcDepositsEnvelope, SbtcDepositsListParams, SbtcDepositsWalkParams, SbtcEventTopic2 as SbtcEventTopic, SbtcEventsConsumeParams, SbtcEventsEnvelope, SbtcEventsListParams, SbtcEventsWalkParams, SbtcResource, SbtcSummaryEnvelope, SbtcWithdrawalEnvelope, SbtcWithdrawalEvent, SbtcWithdrawalStatus, SbtcWithdrawalSweptConfirmedEvent, SbtcWithdrawalsEnvelope, SbtcWithdrawalsListParams, SbtcWithdrawalsWalkParams, SecondLayer, SecondLayerArchive, SecondLayerError, SecondLayerErrorOptions, SecondLayerOptions, SinkTx, StackingEnvelope, StackingListParams, StackingWalkParams, StandardWebhooksHeaders2 as StandardWebhooksHeaders, StreamsBatch, StreamsBatchContext, StreamsCanonicalBlock, StreamsClient, StreamsConsumeParams, StreamsDumpFile, StreamsDumps, StreamsDumpsManifest, StreamsEvent, StreamsEventPayload, StreamsEventType, StreamsEventsConsumeParams, StreamsEventsConsumeResult, StreamsEventsEnvelope, StreamsEventsListEnvelope, StreamsEventsListParams, StreamsEventsStreamParams, StreamsEventsSubscribeParams, StreamsReorg, StreamsReorgContext, StreamsReorgsListEnvelope, StreamsReorgsListParams, StreamsServerError, StreamsSignatureError, StreamsSubscription, StreamsTip, StreamsWireEvent2 as StreamsWireEvent, SubgraphAgentSchema3 as SubgraphAgentSchema, SubgraphOperationStatus, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphSpecOptions3 as SubgraphSpecOptions, Subgraphs, TransactionEnvelope, TransactionProof, TransactionProofVerifyResult, TransactionsEnvelope, TransactionsListParams, TransactionsWalkParams, UpdateWebhookRequest2 as UpdateWebhookRequest, ValidationError, VmStreamsEvent, WalkOptions, WebhookDetail2 as WebhookDetail, WebhookFormat, WebhookHeaderInput, WebhookKind, WebhookRuntime, WebhookStatus, WebhookSummary2 as WebhookSummary, WebhookTestResult2 as WebhookTestResult, Webhooks, WithSinkTx, buildDoctorReport, buildListIssue, consumeIndexFeed, consumerHealth, contextField, createArchiveClient, createStreamsClient, decode, decodeChainWebhook, decodeClarityValue, decodeFtBurn, decodeFtMint, decodeFtTransfer, decodeNftBurn, decodeNftMint, decodeNftTransfer, decodePrint, decodeStxBurn, decodeStxLock, decodeStxMint, decodeStxTransfer, fetchRewardSet, getSubgraph, isFtBurn, isFtMint, isFtTransfer, isNftBurn, isNftMint, isNftTransfer, isPrint, isStxBurn, isStxLock, isStxMint, isStxTransfer, isSuccessDelivery, parseRetryAfter, resolveAccountKey, resolveApiKey, resolveBaseUrl, shutdownSignal, toJsonSafe, trigger, verifySecondlayerSignature, verify as verifyStandardWebhooksHeaders, verifyTransactionProof, verifyWebhookSignature };
|
package/dist/index.js
CHANGED
|
@@ -970,7 +970,10 @@ async function consumeStreamsEvents(opts) {
|
|
|
970
970
|
if (mode === "bounded") {
|
|
971
971
|
return { cursor, pages, emptyPolls };
|
|
972
972
|
}
|
|
973
|
-
await
|
|
973
|
+
await (opts.wake ? Promise.race([
|
|
974
|
+
sleep(emptyBackoffMs, opts.signal),
|
|
975
|
+
opts.wake().catch(() => new Promise(() => {}))
|
|
976
|
+
]) : sleep(emptyBackoffMs, opts.signal));
|
|
974
977
|
continue;
|
|
975
978
|
}
|
|
976
979
|
return { cursor, pages, emptyPolls };
|
|
@@ -2117,7 +2120,8 @@ function createStreamsClient(options) {
|
|
|
2117
2120
|
signal: params.signal,
|
|
2118
2121
|
retryCount: params.retryCount,
|
|
2119
2122
|
retryDelay: params.retryDelay,
|
|
2120
|
-
onError: params.onError
|
|
2123
|
+
onError: params.onError,
|
|
2124
|
+
wake: params.wake
|
|
2121
2125
|
});
|
|
2122
2126
|
}
|
|
2123
2127
|
return {
|
|
@@ -2670,6 +2674,229 @@ function getSubgraph(def, options = {}) {
|
|
|
2670
2674
|
function isSuccessDelivery(row) {
|
|
2671
2675
|
return row.statusCode !== null && row.statusCode >= 200 && row.statusCode < 300;
|
|
2672
2676
|
}
|
|
2677
|
+
var RATE_LIMITED_MIN_ATTEMPTS = 10;
|
|
2678
|
+
var RATE_LIMITED_MIN_RATIO = 0.5;
|
|
2679
|
+
var DOWN_MIN_CONSECUTIVE = 5;
|
|
2680
|
+
var REJECTS_MIN_CONSECUTIVE = 5;
|
|
2681
|
+
var SLOW_WINDOW = 20;
|
|
2682
|
+
var SLOW_MIN_SAMPLES = 5;
|
|
2683
|
+
var SLOW_TIMEOUT_RATIO = 0.5;
|
|
2684
|
+
var LAG_WINDOW = 20;
|
|
2685
|
+
var LAG_MIN_SAMPLES = 5;
|
|
2686
|
+
var LAG_THRESHOLD_MS = 60000;
|
|
2687
|
+
var ERROR_SNIPPET_MAX = 200;
|
|
2688
|
+
function median(values) {
|
|
2689
|
+
if (values.length === 0)
|
|
2690
|
+
return 0;
|
|
2691
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
2692
|
+
return sorted[Math.floor(sorted.length / 2)] ?? 0;
|
|
2693
|
+
}
|
|
2694
|
+
function percentile(values, p) {
|
|
2695
|
+
if (values.length === 0)
|
|
2696
|
+
return 0;
|
|
2697
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
2698
|
+
const idx = Math.min(sorted.length - 1, Math.floor(p * sorted.length));
|
|
2699
|
+
return sorted[idx] ?? 0;
|
|
2700
|
+
}
|
|
2701
|
+
function formatMs(ms) {
|
|
2702
|
+
return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${Math.round(ms)}ms`;
|
|
2703
|
+
}
|
|
2704
|
+
function formatShortDate(iso) {
|
|
2705
|
+
return `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC`;
|
|
2706
|
+
}
|
|
2707
|
+
function consecutiveFromStart(rows, predicate) {
|
|
2708
|
+
const out = [];
|
|
2709
|
+
for (const row of rows) {
|
|
2710
|
+
if (!predicate(row))
|
|
2711
|
+
break;
|
|
2712
|
+
out.push(row);
|
|
2713
|
+
}
|
|
2714
|
+
return out;
|
|
2715
|
+
}
|
|
2716
|
+
function summarizeStatusBreakdown(rows) {
|
|
2717
|
+
const counts = new Map;
|
|
2718
|
+
for (const row of rows) {
|
|
2719
|
+
const key = row.statusCode === null ? "timeout/no response" : String(row.statusCode);
|
|
2720
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
2721
|
+
}
|
|
2722
|
+
return [...counts.entries()].map(([key, n]) => `${n}× ${key}`).join(", ");
|
|
2723
|
+
}
|
|
2724
|
+
var RETRY_AFTER_RE = /retry-after:?\s*(\d+)/i;
|
|
2725
|
+
function extractRetryAfterSeconds(rows) {
|
|
2726
|
+
for (const row of rows) {
|
|
2727
|
+
if (row.statusCode !== 429 || !row.errorMessage)
|
|
2728
|
+
continue;
|
|
2729
|
+
const match = row.errorMessage.match(RETRY_AFTER_RE);
|
|
2730
|
+
if (match?.[1])
|
|
2731
|
+
return match[1];
|
|
2732
|
+
}
|
|
2733
|
+
return null;
|
|
2734
|
+
}
|
|
2735
|
+
function detectReceiverRateLimited(webhook, deliveries) {
|
|
2736
|
+
if (deliveries.length < RATE_LIMITED_MIN_ATTEMPTS)
|
|
2737
|
+
return null;
|
|
2738
|
+
const count429 = deliveries.filter((d) => d.statusCode === 429).length;
|
|
2739
|
+
if (count429 / deliveries.length < RATE_LIMITED_MIN_RATIO)
|
|
2740
|
+
return null;
|
|
2741
|
+
const oldest = deliveries[deliveries.length - 1];
|
|
2742
|
+
const newest = deliveries[0];
|
|
2743
|
+
const evidence = [
|
|
2744
|
+
{
|
|
2745
|
+
label: "429 responses",
|
|
2746
|
+
value: `${count429} of ${deliveries.length} attempts`
|
|
2747
|
+
},
|
|
2748
|
+
{
|
|
2749
|
+
label: "window",
|
|
2750
|
+
value: oldest && newest ? `${formatShortDate(oldest.dispatchedAt)} → ${formatShortDate(newest.dispatchedAt)}` : "unknown"
|
|
2751
|
+
}
|
|
2752
|
+
];
|
|
2753
|
+
const retryAfter = extractRetryAfterSeconds(deliveries);
|
|
2754
|
+
if (retryAfter) {
|
|
2755
|
+
evidence.push({ label: "latest Retry-After", value: `${retryAfter}s` });
|
|
2756
|
+
}
|
|
2757
|
+
const suggestedConcurrency = Math.max(1, Math.floor(webhook.concurrency / 2));
|
|
2758
|
+
return {
|
|
2759
|
+
code: "receiver_rate_limited",
|
|
2760
|
+
severity: "warn",
|
|
2761
|
+
evidence,
|
|
2762
|
+
fix: {
|
|
2763
|
+
text: "Lower concurrency, or honor the Retry-After header on your receiver.",
|
|
2764
|
+
command: `secondlayer webhooks update ${webhook.id} --concurrency ${suggestedConcurrency}`,
|
|
2765
|
+
docsPath: "/docs/webhooks#receiver-rate-limited"
|
|
2766
|
+
}
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
function detectReceiverDown(webhook, deliveries) {
|
|
2770
|
+
const streak = consecutiveFromStart(deliveries, (d) => d.statusCode === null || d.statusCode >= 500);
|
|
2771
|
+
if (streak.length < DOWN_MIN_CONSECUTIVE)
|
|
2772
|
+
return null;
|
|
2773
|
+
return {
|
|
2774
|
+
code: "receiver_down",
|
|
2775
|
+
severity: "bad",
|
|
2776
|
+
evidence: [
|
|
2777
|
+
{ label: "consecutive failures", value: String(streak.length) },
|
|
2778
|
+
{ label: "breakdown", value: summarizeStatusBreakdown(streak) },
|
|
2779
|
+
{
|
|
2780
|
+
label: "last success",
|
|
2781
|
+
value: webhook.lastSuccessAt ? formatShortDate(webhook.lastSuccessAt) : "never"
|
|
2782
|
+
}
|
|
2783
|
+
],
|
|
2784
|
+
fix: {
|
|
2785
|
+
text: "Check your receiver's health and logs, then send a test event.",
|
|
2786
|
+
command: `secondlayer webhooks test ${webhook.id}`,
|
|
2787
|
+
docsPath: "/docs/webhooks#receiver-down"
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
function detectReceiverRejects(webhook, deliveries) {
|
|
2792
|
+
const streak = consecutiveFromStart(deliveries, (d) => d.statusCode !== null && d.statusCode >= 400 && d.statusCode < 500 && d.statusCode !== 429);
|
|
2793
|
+
if (streak.length < REJECTS_MIN_CONSECUTIVE)
|
|
2794
|
+
return null;
|
|
2795
|
+
const evidence = [
|
|
2796
|
+
{ label: "consecutive rejects", value: String(streak.length) },
|
|
2797
|
+
{ label: "breakdown", value: summarizeStatusBreakdown(streak) }
|
|
2798
|
+
];
|
|
2799
|
+
const withText = streak.find((d) => d.errorMessage || d.responseBody);
|
|
2800
|
+
const snippet = withText ? (withText.errorMessage ?? withText.responseBody ?? "").slice(0, ERROR_SNIPPET_MAX) : null;
|
|
2801
|
+
if (snippet)
|
|
2802
|
+
evidence.push({ label: "first error", value: snippet });
|
|
2803
|
+
return {
|
|
2804
|
+
code: "receiver_rejects",
|
|
2805
|
+
severity: "bad",
|
|
2806
|
+
evidence,
|
|
2807
|
+
fix: {
|
|
2808
|
+
text: "Check your receiver's auth config or signature verification.",
|
|
2809
|
+
command: `secondlayer webhooks test ${webhook.id}`,
|
|
2810
|
+
docsPath: "/docs/webhooks#receiver-rejects"
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
function detectReceiverSlow(webhook, deliveries) {
|
|
2815
|
+
const durations = deliveries.slice(0, SLOW_WINDOW).map((d) => d.durationMs).filter((d) => d !== null);
|
|
2816
|
+
if (durations.length < SLOW_MIN_SAMPLES)
|
|
2817
|
+
return null;
|
|
2818
|
+
const med = median(durations);
|
|
2819
|
+
if (med < webhook.timeoutMs * SLOW_TIMEOUT_RATIO)
|
|
2820
|
+
return null;
|
|
2821
|
+
return {
|
|
2822
|
+
code: "receiver_slow",
|
|
2823
|
+
severity: "warn",
|
|
2824
|
+
evidence: [
|
|
2825
|
+
{ label: "median response time", value: formatMs(med) },
|
|
2826
|
+
{
|
|
2827
|
+
label: "p95 response time",
|
|
2828
|
+
value: formatMs(percentile(durations, 0.95))
|
|
2829
|
+
},
|
|
2830
|
+
{ label: "timeout", value: formatMs(webhook.timeoutMs) }
|
|
2831
|
+
],
|
|
2832
|
+
fix: {
|
|
2833
|
+
text: "Speed up your receiver, or raise the timeout (up to 30s hosted).",
|
|
2834
|
+
command: `secondlayer webhooks update ${webhook.id} --timeout-ms 30000`,
|
|
2835
|
+
docsPath: "/docs/webhooks#receiver-slow"
|
|
2836
|
+
}
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
function detectDeliveryLag(deliveries) {
|
|
2840
|
+
const window = deliveries.slice(0, LAG_WINDOW).filter((d) => d.blockTime !== null);
|
|
2841
|
+
if (window.length < LAG_MIN_SAMPLES)
|
|
2842
|
+
return null;
|
|
2843
|
+
const lags = window.map((d) => new Date(d.dispatchedAt).getTime() - new Date(d.blockTime).getTime());
|
|
2844
|
+
const med = median(lags);
|
|
2845
|
+
if (med <= LAG_THRESHOLD_MS)
|
|
2846
|
+
return null;
|
|
2847
|
+
const blockTimes = window.map((d) => d.blockTime).sort();
|
|
2848
|
+
const oldest = blockTimes[0];
|
|
2849
|
+
const newest = blockTimes[blockTimes.length - 1];
|
|
2850
|
+
return {
|
|
2851
|
+
code: "delivery_lag",
|
|
2852
|
+
severity: "warn",
|
|
2853
|
+
evidence: [
|
|
2854
|
+
{ label: "median lag", value: formatMs(med) },
|
|
2855
|
+
{
|
|
2856
|
+
label: "block time range",
|
|
2857
|
+
value: oldest && newest ? `${formatShortDate(oldest)} → ${formatShortDate(newest)}` : "unknown"
|
|
2858
|
+
}
|
|
2859
|
+
],
|
|
2860
|
+
fix: {
|
|
2861
|
+
text: "We're behind on delivery; no action needed on your end."
|
|
2862
|
+
}
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
var SEVERITY_RANK = {
|
|
2866
|
+
bad: 0,
|
|
2867
|
+
warn: 1,
|
|
2868
|
+
calm: 2
|
|
2869
|
+
};
|
|
2870
|
+
var CODE_PRIORITY = [
|
|
2871
|
+
"circuit",
|
|
2872
|
+
"receiver_down",
|
|
2873
|
+
"receiver_rejects",
|
|
2874
|
+
"dead_letters",
|
|
2875
|
+
"paused",
|
|
2876
|
+
"last_error",
|
|
2877
|
+
"warning",
|
|
2878
|
+
"receiver_rate_limited",
|
|
2879
|
+
"receiver_slow",
|
|
2880
|
+
"delivery_lag",
|
|
2881
|
+
"no_deliveries",
|
|
2882
|
+
"subgraph_gaps",
|
|
2883
|
+
"subgraph_catching_up"
|
|
2884
|
+
];
|
|
2885
|
+
function pickPrimary(issues) {
|
|
2886
|
+
if (issues.length === 0)
|
|
2887
|
+
return null;
|
|
2888
|
+
return [...issues].sort((a, b) => {
|
|
2889
|
+
const sevDiff = SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
|
|
2890
|
+
if (sevDiff !== 0)
|
|
2891
|
+
return sevDiff;
|
|
2892
|
+
return CODE_PRIORITY.indexOf(a.code) - CODE_PRIORITY.indexOf(b.code);
|
|
2893
|
+
})[0] ?? null;
|
|
2894
|
+
}
|
|
2895
|
+
function hintFor(issue) {
|
|
2896
|
+
if (!issue.fix)
|
|
2897
|
+
return issue.detail ?? issue.code;
|
|
2898
|
+
return issue.fix.command ? `${issue.fix.text} ${issue.fix.command}` : issue.fix.text;
|
|
2899
|
+
}
|
|
2673
2900
|
function buildDoctorReport(input) {
|
|
2674
2901
|
const successful = input.deliveries.filter(isSuccessDelivery).length;
|
|
2675
2902
|
const failed = input.deliveries.length - successful;
|
|
@@ -2686,35 +2913,60 @@ function buildDoctorReport(input) {
|
|
|
2686
2913
|
const issues = [];
|
|
2687
2914
|
if (input.webhook.warning) {
|
|
2688
2915
|
hints.push(input.webhook.warning);
|
|
2689
|
-
issues.push({
|
|
2916
|
+
issues.push({
|
|
2917
|
+
code: "warning",
|
|
2918
|
+
detail: input.webhook.warning,
|
|
2919
|
+
severity: "warn"
|
|
2920
|
+
});
|
|
2690
2921
|
}
|
|
2691
2922
|
if (input.webhook.status === "paused") {
|
|
2692
2923
|
hints.push(`Resume when the receiver is healthy: secondlayer webhooks resume ${input.webhook.id}`);
|
|
2693
|
-
issues.push({ code: "paused" });
|
|
2924
|
+
issues.push({ code: "paused", severity: "warn" });
|
|
2694
2925
|
}
|
|
2695
2926
|
if (input.webhook.lastError) {
|
|
2696
2927
|
hints.push("Run secondlayer webhooks test to reproduce the receiver request.");
|
|
2697
|
-
issues.push({
|
|
2928
|
+
issues.push({
|
|
2929
|
+
code: "last_error",
|
|
2930
|
+
detail: input.webhook.lastError,
|
|
2931
|
+
severity: "warn"
|
|
2932
|
+
});
|
|
2698
2933
|
}
|
|
2699
2934
|
if (input.webhook.circuitOpenedAt || input.webhook.circuitFailures > 0) {
|
|
2700
2935
|
hints.push("Circuit breaker has failures; inspect receiver logs and delivery status codes.");
|
|
2701
|
-
issues.push({ code: "circuit" });
|
|
2936
|
+
issues.push({ code: "circuit", severity: "bad" });
|
|
2702
2937
|
}
|
|
2703
2938
|
if (input.dead.length > 0) {
|
|
2704
2939
|
hints.push(`Dead-letter rows exist; inspect with secondlayer webhooks dead ${input.webhook.id} and requeue selected rows.`);
|
|
2705
|
-
issues.push({ code: "dead_letters" });
|
|
2940
|
+
issues.push({ code: "dead_letters", severity: "bad" });
|
|
2706
2941
|
}
|
|
2707
2942
|
if (subgraph?.gapCount && subgraph.gapCount > 0) {
|
|
2708
2943
|
hints.push(`Linked subgraph has gaps; run secondlayer subgraphs gaps ${input.webhook.subgraphName}.`);
|
|
2709
|
-
issues.push({ code: "subgraph_gaps" });
|
|
2944
|
+
issues.push({ code: "subgraph_gaps", severity: "calm" });
|
|
2710
2945
|
}
|
|
2711
2946
|
if (subgraph?.syncStatus === "catching_up") {
|
|
2712
2947
|
hints.push("Linked subgraph is still catching up; new matching rows may arrive later.");
|
|
2713
|
-
issues.push({ code: "subgraph_catching_up" });
|
|
2948
|
+
issues.push({ code: "subgraph_catching_up", severity: "calm" });
|
|
2949
|
+
}
|
|
2950
|
+
for (const detector of [
|
|
2951
|
+
detectReceiverRateLimited,
|
|
2952
|
+
detectReceiverDown,
|
|
2953
|
+
detectReceiverRejects,
|
|
2954
|
+
detectReceiverSlow
|
|
2955
|
+
]) {
|
|
2956
|
+
const issue = detector(input.webhook, input.deliveries);
|
|
2957
|
+
if (issue) {
|
|
2958
|
+
issues.push(issue);
|
|
2959
|
+
hints.push(hintFor(issue));
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
const lagIssue = detectDeliveryLag(input.deliveries);
|
|
2963
|
+
if (lagIssue) {
|
|
2964
|
+
issues.push(lagIssue);
|
|
2965
|
+
hints.push(hintFor(lagIssue));
|
|
2714
2966
|
}
|
|
2715
2967
|
if (input.deliveries.length === 0) {
|
|
2716
2968
|
hints.push("No deliveries yet; confirm the table is receiving inserted rows that match the filter.");
|
|
2717
|
-
issues.push({ code: "no_deliveries" });
|
|
2969
|
+
issues.push({ code: "no_deliveries", severity: "calm" });
|
|
2718
2970
|
}
|
|
2719
2971
|
if (hints.length === 0) {
|
|
2720
2972
|
hints.push("No immediate action needed.");
|
|
@@ -2730,9 +2982,35 @@ function buildDoctorReport(input) {
|
|
|
2730
2982
|
deadCount: input.dead.length,
|
|
2731
2983
|
subgraph,
|
|
2732
2984
|
hints,
|
|
2733
|
-
issues
|
|
2985
|
+
issues,
|
|
2986
|
+
primary: pickPrimary(issues)
|
|
2734
2987
|
};
|
|
2735
2988
|
}
|
|
2989
|
+
function buildListIssue(webhook) {
|
|
2990
|
+
if (webhook.status !== "paused")
|
|
2991
|
+
return null;
|
|
2992
|
+
if (webhook.circuitOpenedAt) {
|
|
2993
|
+
return {
|
|
2994
|
+
code: "circuit",
|
|
2995
|
+
severity: "bad",
|
|
2996
|
+
evidence: [
|
|
2997
|
+
{
|
|
2998
|
+
label: "circuit opened",
|
|
2999
|
+
value: formatShortDate(webhook.circuitOpenedAt)
|
|
3000
|
+
},
|
|
3001
|
+
{
|
|
3002
|
+
label: "last success",
|
|
3003
|
+
value: webhook.lastSuccessAt ? formatShortDate(webhook.lastSuccessAt) : "never"
|
|
3004
|
+
}
|
|
3005
|
+
],
|
|
3006
|
+
fix: {
|
|
3007
|
+
text: "Check your receiver's health and logs, then send a test event.",
|
|
3008
|
+
docsPath: "/docs/webhooks#receiver-down"
|
|
3009
|
+
}
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
return { code: "paused", severity: "warn" };
|
|
3013
|
+
}
|
|
2736
3014
|
// src/consumer-ops.ts
|
|
2737
3015
|
function consumerHealth(options = {}) {
|
|
2738
3016
|
const staleAfterMs = options.staleAfterMs ?? 120000;
|
|
@@ -2986,6 +3264,7 @@ export {
|
|
|
2986
3264
|
ValidationError,
|
|
2987
3265
|
Webhooks,
|
|
2988
3266
|
buildDoctorReport,
|
|
3267
|
+
buildListIssue,
|
|
2989
3268
|
consumeIndexFeed,
|
|
2990
3269
|
consumerHealth,
|
|
2991
3270
|
contextField,
|
|
@@ -3032,5 +3311,5 @@ export {
|
|
|
3032
3311
|
verifyWebhookSignature
|
|
3033
3312
|
};
|
|
3034
3313
|
|
|
3035
|
-
//# debugId=
|
|
3314
|
+
//# debugId=17DA719CAEA1958964756E2164756E21
|
|
3036
3315
|
//# sourceMappingURL=index.js.map
|