diff --git a/src/client.ts b/src/client.ts index 073834f..c1c0f5a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1321,6 +1321,7 @@ export class StellarSplitClient { */ async getInvoice( invoiceId: string, + opts?: { retry?: PerMethodRetryOptions; dedupe?: boolean } opts?: { retry?: PerMethodRetryOptions; traceId?: string; timeout?: number } ): Promise { return this._withCache("getInvoice", [invoiceId], async () => { @@ -1328,18 +1329,28 @@ export class StellarSplitClient { ? () => this._batcher!.getInvoice(invoiceId) : () => this._fetchInvoice(invoiceId, opts?.traceId); + const useDedupe = opts?.dedupe !== false; const effectiveRetry = opts?.retry ?? (this._retryOptions ? {} : undefined); if (this._retryOptions && effectiveRetry !== undefined) { return await executeWithRetry( - () => this._dedup.dedupe(invoiceId, fetcher), + () => useDedupe ? this._dedup.dedupe(invoiceId, fetcher) : fetcher(), this._retryOptions, opts?.retry ); } - return await this._dedup.dedupe(invoiceId, fetcher); + return useDedupe ? this._dedup.dedupe(invoiceId, fetcher) : fetcher(); }); } + /** + * Returns deduplication statistics for observability. + * @returns { deduped: number, total: number } — deduped is how many calls were short-circuited. + */ + getDedupStats(): { deduped: number; total: number } { + return this._dedup.getDedupStats(); + } + + private async _fetchInvoice(invoiceId: string): Promise { private async _fetchInvoice(invoiceId: string, traceId?: string): Promise { const startTime = Date.now(); const req = { method: "getInvoice", params: [invoiceId], headers: traceId ? { "X-Trace-Id": traceId } : undefined }; diff --git a/src/dedup.ts b/src/dedup.ts index 5a1653f..485edcd 100644 --- a/src/dedup.ts +++ b/src/dedup.ts @@ -19,4 +19,8 @@ export class Deduplicator { const total = this._hits + this._misses; return total === 0 ? 0 : this._hits / total; } + + getDedupStats(): { deduped: number; total: number } { + return { deduped: this._hits, total: this._hits + this._misses }; + } }