diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..6abd84d --- /dev/null +++ b/docs/API.md @@ -0,0 +1,2820 @@ +# API Reference + +Auto-generated API documentation for @stellar-split/sdk. Total exports: 156 + +## Table of Contents + +- [addRequestInterceptor](#addrequestinterceptor) +- [addResponseInterceptor](#addresponseinterceptor) +- [applyFilter](#applyfilter) +- [BatcherConfig](#batcherconfig) +- [BatchPayment](#batchpayment) +- [builtInNotificationTemplates](#builtinnotificationtemplates) +- [calculateFee](#calculatefee) +- [checkRPCHealth](#checkrpchealth) +- [CircuitBreakerMonitor](#circuitbreakermonitor) +- [CircuitBreakerStatus](#circuitbreakerstatus) +- [clearFeatureCache](#clearfeaturecache) +- [CloneOverrides](#cloneoverrides) +- [CompiledFilter](#compiledfilter) +- [compileFilter](#compilefilter) +- [ComplianceReport](#compliancereport) +- [CompressedPayload](#compressedpayload) +- [CompressionAlgorithm](#compressionalgorithm) +- [CompressionConfig](#compressionconfig) +- [CompressionPayload](#compressionpayload) +- [compressPayload](#compresspayload) +- [connectWallet](#connectwallet) +- [ContractFeatures](#contractfeatures) +- [createCompressionRequestInterceptor](#createcompressionrequestinterceptor) +- [createCompressionResponseInterceptor](#createcompressionresponseinterceptor) +- [CreateInvoiceParams](#createinvoiceparams) +- [createRequestSigningInterceptor](#createrequestsigninginterceptor) +- [DeadlineEngine](#deadlineengine) +- [deadlineFromDays](#deadlinefromdays) +- [DeadlinePassedError](#deadlinepassederror) +- [decompressPayload](#decompresspayload) +- [Deduplicator](#deduplicator) +- [defaultCircuitBreakerMonitor](#defaultcircuitbreakermonitor) +- [detectContractFeatures](#detectcontractfeatures) +- [DIContainer](#dicontainer) +- [diffInvoice](#diffinvoice) +- [diffSimulations](#diffsimulations) +- [EndpointState](#endpointstate) +- [EnrichedInvoice](#enrichedinvoice) +- [enrichInvoice](#enrichinvoice) +- [ExpiryCallback](#expirycallback) +- [ExpiryEvent](#expiryevent) +- [ExportPipeline](#exportpipeline) +- [FallbackChain](#fallbackchain) +- [FallbackExhaustedError](#fallbackexhaustederror) +- [FeeBreakdown](#feebreakdown) +- [FilterCriteria](#filtercriteria) +- [FilterIndex](#filterindex) +- [formatAmount](#formatamount) +- [generateFlowDiagram](#generateflowdiagram) +- [generateGraphQLSchema](#generategraphqlschema) +- [generateMerkleProof](#generatemerkleproof) +- [getInvoiceAtTime](#getinvoiceattime) +- [getOptimisticInvoice](#getoptimisticinvoice) +- [getPublicKey](#getpublickey) +- [getSDKHealth](#getsdkhealth) +- [groupInvoicesByPattern](#groupinvoicesbypattern) +- [HistoricalInvoice](#historicalinvoice) +- [ICacheStore](#icachestore) +- [initPoller](#initpoller) +- [InvalidTransitionError](#invalidtransitionerror) +- [Invoice](#invoice) +- [InvoiceCluster](#invoicecluster) +- [InvoiceDiff](#invoicediff) +- [InvoiceEvent](#invoiceevent) +- [InvoiceEventCallbacks](#invoiceeventcallbacks) +- [InvoiceEventType](#invoiceeventtype) +- [InvoiceExt](#invoiceext) +- [InvoiceFlowFetcher](#invoiceflowfetcher) +- [InvoiceFrozenError](#invoicefrozenerror) +- [InvoiceNotFoundError](#invoicenotfounderror) +- [InvoiceNotPendingError](#invoicenotpendingerror) +- [InvoiceReceipt](#invoicereceipt) +- [InvoiceStatus](#invoicestatus) +- [InvoiceTemplate](#invoicetemplate) +- [IRPCClient](#irpcclient) +- [isExpired](#isexpired) +- [isValidAddress](#isvalidaddress) +- [IWalletAdapter](#iwalletadapter) +- [LoadBalancer](#loadbalancer) +- [LoadBalancerOptions](#loadbalanceroptions) +- [MerkleProof](#merkleproof) +- [MultiplexedClient](#multiplexedclient) +- [MultiTenantClient](#multitenantclient) +- [negotiateVersion](#negotiateversion) +- [NetworkConfig](#networkconfig) +- [NotificationCenter](#notificationcenter) +- [OverflowBehavior](#overflowbehavior) +- [PaginatedResult](#paginatedresult) +- [PaginationOptions](#paginationoptions) +- [parseAmount](#parseamount) +- [parseSorobanError](#parsesorobanerror) +- [Payment](#payment) +- [PaymentAggregator](#paymentaggregator) +- [PaymentExceedsRemainingError](#paymentexceedsremainingerror) +- [PaymentLedger](#paymentledger) +- [PaymentProof](#paymentproof) +- [PaymentSnapshot](#paymentsnapshot) +- [PaymentSnapshotPayer](#paymentsnapshotpayer) +- [PaymentSnapshotPayment](#paymentsnapshotpayment) +- [PaymentSummary](#paymentsummary) +- [PaymentValidation](#paymentvalidation) +- [PayParams](#payparams) +- [PipelineSink](#pipelinesink) +- [PipelineStage](#pipelinestage) +- [pollUSDCBalance](#pollusdcbalance) +- [ProfileReport](#profilereport) +- [ProfilerSession](#profilersession) +- [Recipient](#recipient) +- [registerInvoiceFetcher](#registerinvoicefetcher) +- [registerInvoiceFlowFetcher](#registerinvoiceflowfetcher) +- [registerWebhook](#registerwebhook) +- [renderTemplate](#rendertemplate) +- [replayEvents](#replayevents) +- [RequestBatcher](#requestbatcher) +- [RequestInterceptor](#requestinterceptor) +- [resetSDKHealth](#resetsdkhealth) +- [resolveToken](#resolvetoken) +- [ResourceDelta](#resourcedelta) +- [ResponseInterceptor](#responseinterceptor) +- [RPCRequest](#rpcrequest) +- [RPCResponse](#rpcresponse) +- [ScheduledPayment](#scheduledpayment) +- [ScheduledPaymentManager](#scheduledpaymentmanager) +- [SDK_CONTRACT_VERSION](#sdk_contract_version) +- [SDKHealth](#sdkhealth) +- [signTransaction](#signtransaction) +- [SimpleCache](#simplecache) +- [SimulateCreateInvoiceResult](#simulatecreateinvoiceresult) +- [SimulatePayResult](#simulatepayresult) +- [SimulationDiff](#simulationdiff) +- [SimulationDiffNotComparable](#simulationdiffnotcomparable) +- [SimulationDiffSuccess](#simulationdiffsuccess) +- [StellarSplitClient](#stellarsplitclient) +- [StellarSplitClientConfig](#stellarsplitclientconfig) +- [StellarSplitError](#stellarspliterror) +- [StellarSplitTxBuilder](#stellarsplittxbuilder) +- [telemetry](#telemetry) +- [TelemetryCollector](#telemetrycollector) +- [TelemetryReport](#telemetryreport) +- [TokenInfo](#tokeninfo) +- [TopPayer](#toppayer) +- [triggerWebhook](#triggerwebhook) +- [truncateAddress](#truncateaddress) +- [TxQueue](#txqueue) +- [TxResult](#txresult) +- [validateTransition](#validatetransition) +- [validateWebhookSignature](#validatewebhooksignature) +- [verifyMerkleProof](#verifymerkleproof) +- [VersionInfo](#versioninfo) +- [WalletAdapter](#walletadapter) +- [WalletConnectAdapter](#walletconnectadapter) +- [watchContractUpgrade](#watchcontractupgrade) +- [watchExpiry](#watchexpiry) +- [WebhookConfig](#webhookconfig) +- [WebhookEvent](#webhookevent) +- [WeightedEndpoint](#weightedendpoint) + +--- + +## addRequestInterceptor + +**Kind:** `function` + +### Signature + +```typescript +export function addRequestInterceptor(fn: RequestInterceptor): void { + requestInterceptors.push(fn); +} +``` + +--- + +## addResponseInterceptor + +**Kind:** `function` + +### Signature + +```typescript +export function addResponseInterceptor(fn: ResponseInterceptor): void { + responseInterceptors.push(fn); +} +``` + +--- + +## applyFilter + +**Kind:** `function` + +### Signature + +```typescript +export function applyFilter(invoices: Invoice[], filter: CompiledFilter): Invoice[] { + return invoices.filter(filter.predicate); +} +``` + +--- + +## BatcherConfig + +**Kind:** `interface` + +Configuration for the request batcher. +/ + +### Signature + +```typescript +export interface BatcherConfig { + /** Time window in milliseconds to collect requests before batching */ + windowMs: number; + /** Maximum number of requests to include in a single batch */ + maxBatchSize: number; ... +``` + +--- + +## BatchPayment + +**Kind:** `interface` + +### Signature + +```typescript +export interface BatchPayment { + /** Invoice ID to pay toward. */ + invoiceId: string; + /** Amount to pay in stroops. */ + amount: bigint; ... +``` + +--- + +## builtInNotificationTemplates + +**Kind:** `const` + +### Signature + +```typescript +builtInNotificationTemplates: Record = { + created: "Invoice {{invoiceId}} was created by {{creator}} for {{amount}}.", + payment: "Payment of {{amount}} received for invoice {{invoiceId}}.", + released: "Invoice {{invoiceId}} has been released to recipients.", + refunded: "Invoice {{invoiceId}} has been refunded by {{creator}}.", ... +``` + +--- + +## calculateFee + +**Kind:** `function` + +Calculate the protocol fee for a given amount. +Fetches the current fee basis points from the contract and computes +the fee and net amounts. + +### Signature + +```typescript +export async function calculateFee( + amount: bigint, + config: StellarSplitClientConfig +): Promise { + const rpcUrl = Array.isArray(config.rpcUrl) ? config.rpcUrl[0]! : config.rpcUrl; ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `amount` | Gross amount in stroops | +| `config` | Client configuration | + +### Returns + +Fee breakdown with gross, fee, net, and feeBps / + +--- + +## checkRPCHealth + +**Kind:** `function` + +Check the health of the configured RPC endpoint. + +### Signature + +```typescript +export async function checkRPCHealth(server: SorobanRpc.Server): Promise { + const startTime = Date.now(); + + try { + const ledger = await server.getLatestLedger(); ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `server` | Soroban RPC server instance | + +### Returns + +Health status with latency and block height / + +--- + +## CircuitBreakerMonitor + +**Kind:** `class` + +### Signature + +```typescript +export class CircuitBreakerMonitor extends EventEmitter { + private _breakers = new Map(); + + constructor() { + super(); ... +``` + +--- + +## CircuitBreakerStatus + +**Kind:** `interface` + +### Signature + +```typescript +export interface CircuitBreakerStatus { + endpoint: string; + state: CircuitState; + failureCount: number; + lastFailure: number | null; ... +``` + +--- + +## clearFeatureCache + +**Kind:** `function` + +### Signature + +```typescript +export function clearFeatureCache(): void { + _cached = null; +} +``` + +--- + +## CloneOverrides + +**Kind:** `interface` + +### Signature + +```typescript +export interface CloneOverrides { + newDeadline?: number; + newAmounts?: bigint[]; + newRecipients?: string[]; + newOverflowBehavior?: OverflowBehavior; ... +``` + +--- + +## CompiledFilter + +**Kind:** `interface` + +### Signature + +```typescript +export interface CompiledFilter { + predicate: (invoice: Invoice) => boolean; + criteria: FilterCriteria; +} +``` + +--- + +## compileFilter + +**Kind:** `function` + +### Signature + +```typescript +export function compileFilter(criteria: FilterCriteria): CompiledFilter { + return { predicate: buildPredicate(criteria), criteria }; +} +``` + +--- + +## ComplianceReport + +**Kind:** `interface` + +### Signature + +```typescript +export interface ComplianceReport { + passed: boolean; + violations: string[]; +} +``` + +--- + +## CompressedPayload + +**Kind:** `interface` + +### Signature + +```typescript +export interface CompressedPayload { + compressed: true; + algorithm: CompressionAlgorithm; + body: Uint8Array; + originalBytes: number; ... +``` + +--- + +## CompressionAlgorithm + +**Kind:** `type` + +### Signature + +```typescript +export type CompressionAlgorithm = "gzip" | "deflate"; +``` + +--- + +## CompressionConfig + +**Kind:** `interface` + +### Signature + +```typescript +export interface CompressionConfig { + enabled: boolean; + algorithm: CompressionAlgorithm; +} +``` + +--- + +## CompressionPayload + +**Kind:** `type` + +### Signature + +```typescript +export type CompressionPayload = string | Uint8Array; +``` + +--- + +## compressPayload + +**Kind:** `function` + +### Signature + +```typescript +export async function compressPayload( + payload: CompressionPayload, + algorithm: CompressionAlgorithm = "gzip" +): Promise { + const bytes = toBytes(payload); ... +``` + +--- + +## connectWallet + +**Kind:** `function` + +### Signature + +```typescript +export async function connectWallet(): Promise { + const { isConnected: connected } = await isConnected(); + if (!connected) { + throw new Error( + "Freighter wallet is not installed. Please install it from https://freighter.app" ... +``` + +--- + +## ContractFeatures + +**Kind:** `interface` + +Feature detection result indicating which contract features are available. +Each field is true if the deployed contract supports the corresponding method. +/ + +### Signature + +```typescript +export interface ContractFeatures { + batchPay: boolean; + cloneInvoice: boolean; + invoiceGroups: boolean; + templates: boolean; ... +``` + +--- + +## createCompressionRequestInterceptor + +**Kind:** `function` + +### Signature + +```typescript +export function createCompressionRequestInterceptor(config: CompressionConfig): RequestInterceptor { + return async (req) => { + if (!config.enabled) { + return req; + } ... +``` + +--- + +## createCompressionResponseInterceptor + +**Kind:** `function` + +### Signature + +```typescript +export function createCompressionResponseInterceptor(_config: CompressionConfig): ResponseInterceptor { + return async (res) => { + if (!isCompressedPayload(res.result)) { + return res; + } ... +``` + +--- + +## CreateInvoiceParams + +**Kind:** `interface` + +### Signature + +```typescript +export interface CreateInvoiceParams { + /** Stellar address of the creator (must sign). */ + creator: string; + /** Recipients and their owed amounts. */ + recipients: Recipient[]; ... +``` + +--- + +## createRequestSigningInterceptor + +**Kind:** `function` + +### Signature + +```typescript +export function createRequestSigningInterceptor(keypair: Keypair): RequestInterceptor { + return async (req: RPCRequest): Promise => { + const timestamp = Date.now(); + const message = `stellar-split:${timestamp}`; + // Keypair.sign accepts Uint8Array / Buffer ... +``` + +--- + +## DeadlineEngine + +**Kind:** `class` + +### Signature + +```typescript +export class DeadlineEngine { + private interval: TimeoutLike | null = null; + + private readonly intervalMs: number; + private destroyed = false; ... +``` + +--- + +## deadlineFromDays + +**Kind:** `function` + +Return a Unix timestamp (seconds) for a date that is `days` from now. +/ + +### Signature + +```typescript +export function deadlineFromDays(days: number): number { + return Math.floor(Date.now() / 1000) + days * 86_400; +} +``` + +--- + +## DeadlinePassedError + +**Kind:** `class` + +### Signature + +```typescript +export class DeadlinePassedError extends StellarSplitError { + readonly invoiceId: string; + + constructor(invoiceId: string, raw?: string) { + super(`Invoice deadline has passed: ${invoiceId}`, raw); ... +``` + +--- + +## decompressPayload + +**Kind:** `function` + +### Signature + +```typescript +export async function decompressPayload(payload: CompressedPayload): Promise { + return isDecompressionStreamAvailable() + ? await decompressInBrowser(payload.body, payload.algorithm) + : await decompressInNode(payload.body, payload.algorithm); +} +``` + +--- + +## Deduplicator + +**Kind:** `class` + +### Signature + +```typescript +export class Deduplicator { + private _inflight = new Map>(); + private _hits = 0; + private _misses = 0; + ... +``` + +--- + +## defaultCircuitBreakerMonitor + +**Kind:** `const` + +### Signature + +```typescript +defaultCircuitBreakerMonitor = new CircuitBreakerMonitor() +``` + +--- + +## detectContractFeatures + +**Kind:** `function` + +Detect which optional features the deployed Soroban contract supports. +The result is cached for 5 minutes. Call `clearFeatureCache()` to reset. + +### Signature + +```typescript +export async function detectContractFeatures( + config: StellarSplitClientConfig, + source?: string, +): Promise { + // Return cached result if still valid ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `config` | StellarSplit client configuration (must include rpcUrl, contractId, networkPassphrase). | +| `source` | A valid Stellar public key (G...) to use as the simulation source. Defaults to a well-known testnet address if omitted. | + +### Returns + +A `ContractFeatures` object with booleans for each optional feature. / + +--- + +## DIContainer + +**Kind:** `class` + +### Signature + +```typescript +export class DIContainer { + private rpcClient?: IRPCClient; + private cacheStore?: ICacheStore; + private walletAdapter?: IWalletAdapter; + ... +``` + +--- + +## diffInvoice + +**Kind:** `function` + +### Signature + +```typescript +export function diffInvoice(oldInvoice: Invoice, newInvoice: Invoice): InvoiceDiff { + const changed: InvoiceDiff["changed"] = []; + + for (const key of INVOICE_KEYS) { + const oldVal = oldInvoice[key]; ... +``` + +--- + +## diffSimulations + +**Kind:** `function` + +Diff two `SimulateTransactionResponse` objects. +If either response is an error or a restore response, returns +`{ comparable: false }` instead of throwing. +/ + +### Signature + +```typescript +export function diffSimulations( + before: SorobanRpc.Api.SimulateTransactionResponse, + after: SorobanRpc.Api.SimulateTransactionResponse, +): SimulationDiff { + if (SorobanRpc.Api.isSimulationError(before)) { ... +``` + +--- + +## EndpointState + +**Kind:** `interface` + +### Signature + +```typescript +export interface EndpointState { + url: string; + healthy: boolean; + averageLatencyMs: number | null; + consecutiveFailures: number; ... +``` + +--- + +## EnrichedInvoice + +**Kind:** `interface` + +### Signature + +```typescript +export interface EnrichedInvoice extends Invoice { + metadata: Record | null; +} +``` + +--- + +## enrichInvoice + +**Kind:** `function` + +### Signature + +```typescript +export async function enrichInvoice( + invoiceId: string, + getInvoice?: InvoiceFetcher +): Promise { + const fetcher = getInvoice ?? invoiceFetcher; ... +``` + +--- + +## ExpiryCallback + +**Kind:** `type` + +### Signature + +```typescript +export type ExpiryCallback = (event: ExpiryEvent) => void; +``` + +--- + +## ExpiryEvent + +**Kind:** `interface` + +### Signature + +```typescript +export interface ExpiryEvent { + /** Invoice ID. */ + invoiceId: string; + /** Unix timestamp deadline (seconds). */ + deadline: number; ... +``` + +--- + +## ExportPipeline + +**Kind:** `class` + +### Signature + +```typescript +export class ExportPipeline { + private filters: Array> = []; + private transforms: Array> = []; + private formatters: Array> = []; + private sinks: PipelineSink[] = []; ... +``` + +--- + +## FallbackChain + +**Kind:** `class` + +### Signature + +```typescript +export class FallbackChain { + private readonly urls: string[]; + private readonly logger: FallbackFailureLogger; + + constructor(urls: string[], options?: { logger?: FallbackFailureLogger }) { ... +``` + +--- + +## FallbackExhaustedError + +**Kind:** `class` + +### Signature + +```typescript +export class FallbackExhaustedError extends Error { + public readonly attempts: FallbackAttemptLog[]; + + constructor(attempts: FallbackAttemptLog[]) { + super(`Fallback chain exhausted after ${attempts.length} attempts.`); ... +``` + +--- + +## FeeBreakdown + +**Kind:** `interface` + +### Signature + +```typescript +export interface FeeBreakdown { + /** Gross amount before fee deduction. */ + gross: bigint; + /** Protocol fee amount. */ + fee: bigint; ... +``` + +--- + +## FilterCriteria + +**Kind:** `interface` + +### Signature + +```typescript +export interface FilterCriteria { + and?: FilterCriteria[]; + or?: FilterCriteria[]; + status?: InvoiceStatus; + creator?: string; ... +``` + +--- + +## FilterIndex + +**Kind:** `class` + +### Signature + +```typescript +export class FilterIndex { + private statusIndex = new Map>(); + private creatorIndex = new Map>(); + private tokenIndex = new Map>(); + private invoicesRef: Invoice[] | null = null; ... +``` + +--- + +## formatAmount + +**Kind:** `function` + +Format a stroop amount as a human-readable USDC string. + +### Signature + +```typescript +export function formatAmount(stroops: bigint): string { + const whole = stroops / STROOPS_PER_UNIT; + const frac = stroops % STROOPS_PER_UNIT; + return `${whole}.${frac.toString().padStart(7, "0")}`; +} +``` + +### Examples + +```typescript +formatAmount(10_000_000n) // "1.0000000" +/ +``` + +--- + +## generateFlowDiagram + +**Kind:** `function` + +### Signature + +```typescript +export async function generateFlowDiagram( + invoiceId: string, + getInvoice?: InvoiceFlowFetcher +): Promise { + const fetcher = getInvoice ?? invoiceFlowFetcher; ... +``` + +--- + +## generateGraphQLSchema + +**Kind:** `function` + +generateGraphQLSchema — builds a GraphQL SDL string from SDK TypeScript interfaces. +Type mapping: +bigint → String (GraphQL has no native 64-bit int) +string → String +number → Int +boolean → Boolean +/ + +### Signature + +```typescript +export function generateGraphQLSchema(): string { + return ` +type Recipient { + address: String! + amount: String! ... +``` + +--- + +## generateMerkleProof + +**Kind:** `function` + +Generate a Merkle proof for a specific payment within an invoice. + +### Signature + +```typescript +export async function generateMerkleProof( + invoiceId: string, + paymentIndex: number +): Promise { + // In a real implementation, this would: ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `invoiceId` | The invoice ID | +| `paymentIndex` | The index of the payment in the invoice's payments array | + +### Returns + +A Merkle proof object / + +--- + +## getInvoiceAtTime + +**Kind:** `function` + +### Signature + +```typescript +export async function getInvoiceAtTime( + server: SorobanRpc.Server, + contractId: string, + invoiceId: string, + timestamp: number ... +``` + +--- + +## getOptimisticInvoice + +**Kind:** `function` + +Get an optimistically updated invoice reflecting a pending payment. +Returns a new invoice object with the payment applied immediately, +without waiting for on-chain confirmation. Does not mutate the input. + +### Signature + +```typescript +export function getOptimisticInvoice(invoice: Invoice, payment: Payment): Invoice { + const newFunded = invoice.funded + payment.amount; + const newPayments = [...invoice.payments, payment]; + const newStatus = + newFunded >= invoice.recipients.reduce((sum, r) => sum + r.amount, 0n) ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `invoice` | The current invoice state | +| `payment` | The pending payment to apply | + +### Returns + +A new invoice with the payment applied / + +--- + +## getPublicKey + +**Kind:** `function` + +### Signature + +```typescript +export async function getPublicKey(): Promise { + const { isConnected: connected } = await isConnected(); + if (!connected) { + throw new Error("Freighter wallet is not connected."); + } ... +``` + +--- + +## getSDKHealth + +**Kind:** `function` + +### Signature + +```typescript +export async function getSDKHealth(): Promise { + const latencyStart = Date.now(); + let rpcLatency = 0; + + if (serverRef) { ... +``` + +--- + +## groupInvoicesByPattern + +**Kind:** `function` + +### Signature + +```typescript +export function groupInvoicesByPattern(invoices: Invoice[]): InvoiceCluster[] { + if (invoices.length === 0) { + return []; + } + ... +``` + +--- + +## HistoricalInvoice + +**Kind:** `interface` + +### Signature + +```typescript +export interface HistoricalInvoice { + reconstructedAt: number; +} +``` + +--- + +## ICacheStore + +**Kind:** `interface` + +### Signature + +```typescript +export interface ICacheStore { + get(key: string): T | undefined; + set(key: string, value: T): void; + invalidate(key: string): void; + clear(): void; ... +``` + +--- + +## initPoller + +**Kind:** `function` + +Initialize the poller with RPC configuration. +Must be called before using pollUSDCBalance. +/ + +### Signature + +```typescript +export function initPoller(rpcUrl: string, networkPassphrase: string): void { + pollerServer = new SorobanRpc.Server(rpcUrl, { + allowHttp: rpcUrl.startsWith("http://"), + }); +} +``` + +--- + +## InvalidTransitionError + +**Kind:** `class` + +### Signature + +```typescript +export class InvalidTransitionError extends Error { + constructor(from: InvoiceStatus, to: InvoiceStatus) { + super(`Invalid transition from "${from}" to "${to}"`); + this.name = "InvalidTransitionError"; + } ... +``` + +--- + +## Invoice + +**Kind:** `interface` + +### Signature + +```typescript +export interface Invoice { + /** Invoice ID (u64 from the contract). */ + id: string; + /** Address that created the invoice. */ + creator: string; ... +``` + +--- + +## InvoiceCluster + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceCluster { + label: string; + invoices: Invoice[]; + similarity: number; +} +``` + +--- + +## InvoiceDiff + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceDiff { + changed: Array<{ + field: string; + from: unknown; + to: unknown; ... +``` + +--- + +## InvoiceEvent + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceEvent { + type: InvoiceEventType; + invoiceId: string; + amount?: bigint | number | string; + creator?: string; ... +``` + +--- + +## InvoiceEventCallbacks + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceEventCallbacks { + /** Fired when a payment event is detected. */ + onPayment?: (payment: Payment) => void; + /** Fired when the invoice status changes to Released. */ + onReleased?: () => void; ... +``` + +--- + +## InvoiceEventType + +**Kind:** `type` + +### Signature + +```typescript +export type InvoiceEventType = "created" | "payment" | "released" | "refunded" | "expiring"; +``` + +--- + +## InvoiceExt + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceExt { + parentInvoiceId: string | null; + cloneDepth: number; +} +``` + +--- + +## InvoiceFlowFetcher + +**Kind:** `type` + +### Signature + +```typescript +export type InvoiceFlowFetcher = (invoiceId: string) => Promise; +``` + +--- + +## InvoiceFrozenError + +**Kind:** `class` + +### Signature + +```typescript +export class InvoiceFrozenError extends StellarSplitError { + readonly invoiceId: string; + + constructor(invoiceId: string, raw?: string) { + super(`Invoice is frozen: ${invoiceId}`, raw); ... +``` + +--- + +## InvoiceNotFoundError + +**Kind:** `class` + +### Signature + +```typescript +export class InvoiceNotFoundError extends StellarSplitError { + readonly invoiceId: string; + + constructor(invoiceId: string, raw?: string) { + super(`Invoice not found: ${invoiceId}`, raw ?? `Invoice not found: ${invoiceId}`); ... +``` + +--- + +## InvoiceNotPendingError + +**Kind:** `class` + +### Signature + +```typescript +export class InvoiceNotPendingError extends StellarSplitError { + readonly invoiceId: string; + + constructor(invoiceId: string, raw?: string) { + super(`Invoice is not in Pending state: ${invoiceId}`, raw); ... +``` + +--- + +## InvoiceReceipt + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceReceipt { + /** Deterministic receipt identifier. */ + receiptId: string; + /** Invoice ID this receipt belongs to. */ + invoiceId: string; ... +``` + +--- + +## InvoiceStatus + +**Kind:** `type` + +### Signature + +```typescript +export type InvoiceStatus = "Pending" | "Released" | "Refunded" | "Cancelled"; +``` + +--- + +## InvoiceTemplate + +**Kind:** `interface` + +### Signature + +```typescript +export interface InvoiceTemplate { + /** Template name. */ + name: string; + /** Recipients and their owed amounts. */ + recipients: Recipient[]; ... +``` + +--- + +## IRPCClient + +**Kind:** `interface` + +### Signature + +```typescript +export interface IRPCClient extends SorobanRpc.Server { + getFeeStats(): Promise; + close?(): Promise | void; +} +``` + +--- + +## isExpired + +**Kind:** `function` + +Return true if a Unix timestamp deadline has passed. +/ + +### Signature + +```typescript +export function isExpired(deadline: number): boolean { + return Math.floor(Date.now() / 1000) > deadline; +} +``` + +--- + +## isValidAddress + +**Kind:** `function` + +Validate a Stellar public key (G... address). +Uses a simple regex; for full validation use stellar-sdk StrKey. +/ + +### Signature + +```typescript +export function isValidAddress(address: string): boolean { + return /^G[A-Z2-7]{54,55}$/.test(address); +} +``` + +--- + +## IWalletAdapter + +**Kind:** `interface` + +### Signature + +```typescript +export interface IWalletAdapter { + getAddress(): Promise; + signTransaction(xdr: string, network: string): Promise; +} +``` + +--- + +## LoadBalancer + +**Kind:** `class` + +### Signature + +```typescript +export class LoadBalancer { + private readonly endpoints: MutableEndpointState[]; + private readonly maxLatencySamples: number; + private readonly failureThreshold: number; + private readonly reprobeIntervalMs: number; ... +``` + +--- + +## LoadBalancerOptions + +**Kind:** `interface` + +### Signature + +```typescript +export interface LoadBalancerOptions { + maxLatencySamples?: number; + failureThreshold?: number; + reprobeIntervalMs?: number; + now?: () => number; ... +``` + +--- + +## MerkleProof + +**Kind:** `interface` + +Merkle proof structure for invoice payment verification. +/ + +### Signature + +```typescript +export interface MerkleProof { + /** The leaf hash being proven (payment hash) */ + leaf: string; + /** Sibling hashes along the path to the root */ + path: string[]; ... +``` + +--- + +## MultiplexedClient + +**Kind:** `class` + +MultiplexedClient distributes requests across multiple RPC endpoints +using weighted round-robin load balancing based on endpoint health scores. +/ + +### Signature + +```typescript +export class MultiplexedClient { + private endpoints: WeightedEndpoint[]; + private currentWeights: number[]; + private healthScores: number[]; + ... +``` + +--- + +## MultiTenantClient + +**Kind:** `class` + +### Signature + +```typescript +export class MultiTenantClient { + private readonly clients = new Map(); + private readonly clientFactory: (tenantId: string) => StellarSplitClientConfig; + + constructor(clientFactory: (tenantId: string) => StellarSplitClientConfig) { ... +``` + +--- + +## negotiateVersion + +**Kind:** `function` + +Reads the contract's on-chain `get_version()` and compares it against +{@link SDK_CONTRACT_VERSION}. +- `compatible: true` — major versions match. +- `compatible: false` — major versions differ (incompatible ABI). +- Logs a warning when minor versions differ (compatible but potentially stale). +/ + +### Signature + +```typescript +export async function negotiateVersion( + config: StellarSplitClientConfig +): Promise { + const rpcUrl = Array.isArray(config.rpcUrl) ? config.rpcUrl[0]! : config.rpcUrl; + const server = new SorobanRpc.Server(rpcUrl, { ... +``` + +--- + +## NetworkConfig + +**Kind:** `interface` + +### Signature + +```typescript +export interface NetworkConfig { + /** Soroban RPC endpoint URL. */ + rpcUrl: string; + /** Stellar network passphrase. */ + networkPassphrase: string; ... +``` + +--- + +## NotificationCenter + +**Kind:** `class` + +### Signature + +```typescript +export class NotificationCenter extends EventEmitter { + private _watchers = new Map(); + private _fetchInvoice: (invoiceId: string) => Promise; + + constructor(fetchInvoice: (invoiceId: string) => Promise) { ... +``` + +--- + +## OverflowBehavior + +**Kind:** `type` + +### Signature + +```typescript +export type OverflowBehavior = "refund" | "rollback" | "escalate"; +``` + +--- + +## PaginatedResult + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaginatedResult { + items: T[]; + nextCursor: string | null; + total: number; +} +``` + +--- + +## PaginationOptions + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaginationOptions { + /** Cursor (invoice ID) to start after. */ + cursor?: string; + /** Maximum number of items to return. Defaults to 20. */ + limit?: number; ... +``` + +--- + +## parseAmount + +**Kind:** `function` + +Parse a human-readable USDC string into stroops. + +### Signature + +```typescript +export function parseAmount(value: string): bigint { + const [whole = "0", frac = ""] = value.split("."); + const fracPadded = frac.padEnd(7, "0").slice(0, 7); + return BigInt(whole) * STROOPS_PER_UNIT + BigInt(fracPadded); +} +``` + +### Examples + +```typescript +parseAmount("1.5") // 15_000_000n +/ +``` + +--- + +## parseSorobanError + +**Kind:** `function` + +Parse a raw Soroban error string and return the appropriate typed error. + +### Signature + +```typescript +export function parseSorobanError(raw: string, invoiceId: string = ""): StellarSplitError { + for (const { pattern, factory } of ERROR_PATTERNS) { + if (pattern.test(raw)) { + return factory(invoiceId, raw); + } ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `raw` | The raw error message from the RPC. | +| `invoiceId` | The invoice ID involved in the operation, if known. | + +### Returns + +A typed StellarSplitError subclass, or a generic StellarSplitError. / + +--- + +## Payment + +**Kind:** `interface` + +### Signature + +```typescript +export interface Payment { + /** Stellar address of the payer. */ + payer: string; + /** Amount paid in stroops (1 XLM = 10_000_000 stroops). */ + amount: bigint; ... +``` + +--- + +## PaymentAggregator + +**Kind:** `class` + +### Signature + +```typescript +export class PaymentAggregator { + public totalFunded: bigint; + public percentFunded: number; + public readonly payerBreakdown: Map; + public paymentCount: number; ... +``` + +--- + +## PaymentExceedsRemainingError + +**Kind:** `class` + +### Signature + +```typescript +export class PaymentExceedsRemainingError extends StellarSplitError { + readonly invoiceId: string; + + constructor(invoiceId: string, raw?: string) { + super(`Payment exceeds remaining balance for invoice: ${invoiceId}`, raw); ... +``` + +--- + +## PaymentLedger + +**Kind:** `type` + +### Signature + +```typescript +export type PaymentLedger = Payment & { ledger: number }; +``` + +--- + +## PaymentProof + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentProof { + /** Transaction hash. */ + txHash: string; + /** Payer's Stellar address. */ + payer: string; ... +``` + +--- + +## PaymentSnapshot + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentSnapshot { + snapshotId: string; + capturedAt: number; + invoiceId: string; + invoiceTotal: string; ... +``` + +--- + +## PaymentSnapshotPayer + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentSnapshotPayer { + address: string; + amount: string; +} +``` + +--- + +## PaymentSnapshotPayment + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentSnapshotPayment { + payer: string; + amount: string; + ledger: number; + timestamp?: number; ... +``` + +--- + +## PaymentSummary + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentSummary { + totalFunded: bigint; + percentFunded: number; + payerBreakdown: Map; + paymentCount: number; ... +``` + +--- + +## PaymentValidation + +**Kind:** `interface` + +### Signature + +```typescript +export interface PaymentValidation { + valid: boolean; + errors: string[]; +} +``` + +--- + +## PayParams + +**Kind:** `interface` + +### Signature + +```typescript +export interface PayParams { + /** Stellar address of the payer (must sign). */ + payer: string; + /** Invoice ID to pay toward. */ + invoiceId: string; ... +``` + +--- + +## PipelineSink + +**Kind:** `type` + +A sink consumes the final formatted output string. +/ + +### Signature + +```typescript +export type PipelineSink = (output: string) => void | Promise; +``` + +--- + +## PipelineStage + +**Kind:** `type` + +A pipeline stage receives an invoice and may return a transformed value +(sync or async). +/ + +### Signature + +```typescript +export type PipelineStage = (input: T) => T | Promise; +``` + +--- + +## pollUSDCBalance + +**Kind:** `function` + +Poll a wallet's USDC balance and invoke callback when it changes. + +### Signature + +```typescript +export function pollUSDCBalance( + address: string, + callback: (balance: bigint) => void, + intervalMs: number = 10000 +): () => void { ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `address` | Stellar address to monitor | +| `callback` | Function invoked with new balance when it changes | +| `intervalMs` | Poll interval in milliseconds (default: 10000) | + +### Returns + +Cleanup function to stop polling / + +--- + +## ProfileReport + +**Kind:** `interface` + +### Signature + +```typescript +export interface ProfileReport { + sessions: ProfileSession[]; +} +``` + +--- + +## ProfilerSession + +**Kind:** `class` + +### Signature + +```typescript +export class ProfilerSession { + private sessions: ProfileSession[] = []; + private active = false; + private currentEntries: ProfileEntry[] = []; + private currentStartedAt = 0; ... +``` + +--- + +## Recipient + +**Kind:** `interface` + +### Signature + +```typescript +export interface Recipient { + /** Stellar address of the recipient. */ + address: string; + /** Amount owed in stroops. */ + amount: bigint; ... +``` + +--- + +## registerInvoiceFetcher + +**Kind:** `function` + +### Signature + +```typescript +export function registerInvoiceFetcher(fetcher: InvoiceFetcher): void { + invoiceFetcher = fetcher; +} +``` + +--- + +## registerInvoiceFlowFetcher + +**Kind:** `function` + +### Signature + +```typescript +export function registerInvoiceFlowFetcher(fetcher: InvoiceFlowFetcher): void { + invoiceFlowFetcher = fetcher; +} +``` + +--- + +## registerWebhook + +**Kind:** `function` + +### Signature + +```typescript +export function registerWebhook( + invoiceId: string, + url: string, + events: WebhookEvent[], +): void { ... +``` + +--- + +## renderTemplate + +**Kind:** `function` + +### Signature + +```typescript +export function renderTemplate(event: InvoiceEvent, template?: string): string { + const source = template ?? builtInNotificationTemplates[event.type]; + const values: Record<"invoiceId" | "amount" | "creator", string> = { + invoiceId: event.invoiceId, + amount: stringifyTemplateValue(event.amount), ... +``` + +--- + +## replayEvents + +**Kind:** `function` + +Replay historical contract events in a ledger range. + +### Signature + +```typescript +export async function replayEvents( + server: SorobanRpc.Server, + contractId: string, + fromLedger: number, + toLedger: number ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `server` | Soroban RPC server | +| `contractId` | The contract ID to filter events | +| `fromLedger` | Starting ledger sequence | +| `toLedger` | Ending ledger sequence | + +### Returns + +Array of contract events in chronological order / + +--- + +## RequestBatcher + +**Kind:** `class` + +RequestBatcher collects read requests within a configurable time window +and submits them as a single batch RPC call. +/ + +### Signature + +```typescript +export class RequestBatcher { + private pendingRequests: Array<{ + invoiceId: string; + resolve: (invoice: Invoice) => void; + reject: (error: Error) => void; ... +``` + +--- + +## RequestInterceptor + +**Kind:** `type` + +### Signature + +```typescript +export type RequestInterceptor = (req: RPCRequest) => RPCRequest | Promise; +``` + +--- + +## resetSDKHealth + +**Kind:** `function` + +### Signature + +```typescript +export function resetSDKHealth(): void { + totalCalls = 0; + errorCalls = 0; + startTime = Date.now(); +} +``` + +--- + +## resolveToken + +**Kind:** `function` + +Resolve token metadata from a SAC contract address. +Fetches symbol, name, and decimals from the contract and caches results. + +### Signature + +```typescript +export async function resolveToken( + address: string, + config: StellarSplitClientConfig +): Promise { + // Check cache first ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `address` | Token contract address | +| `config` | Client configuration | + +### Returns + +Token metadata / + +--- + +## ResourceDelta + +**Kind:** `interface` + +### Signature + +```typescript +export interface ResourceDelta { + /** Difference in CPU instructions (after − before). */ + cpuInstructions: bigint; + /** Difference in read-bytes (after − before). */ + readBytes: bigint; ... +``` + +--- + +## ResponseInterceptor + +**Kind:** `type` + +### Signature + +```typescript +export type ResponseInterceptor = (res: RPCResponse) => RPCResponse | Promise; +``` + +--- + +## RPCRequest + +**Kind:** `interface` + +### Signature + +```typescript +export interface RPCRequest { + method: string; + params: unknown[]; +} +``` + +--- + +## RPCResponse + +**Kind:** `interface` + +### Signature + +```typescript +export interface RPCResponse { + method: string; + result: unknown; + durationMs: number; +} +``` + +--- + +## ScheduledPayment + +**Kind:** `interface` + +### Signature + +```typescript +export interface ScheduledPayment { + id: string; + invoiceId: string; + amount: bigint; + executeAt: number; ... +``` + +--- + +## ScheduledPaymentManager + +**Kind:** `class` + +### Signature + +```typescript +export class ScheduledPaymentManager { + private _payments: ScheduledPayment[] = load(); + private _timers = new Map>(); + private _pay: PayFn; + ... +``` + +--- + +## SDK_CONTRACT_VERSION + +**Kind:** `const` + +### Signature + +```typescript +SDK_CONTRACT_VERSION = "1.0.0" +``` + +--- + +## SDKHealth + +**Kind:** `interface` + +### Signature + +```typescript +export interface SDKHealth { + rpcLatency: number; + cacheHitRate: number; + errorRate: number; + uptimeMs: number; ... +``` + +--- + +## signTransaction + +**Kind:** `function` + +Sign a Stellar transaction XDR string using Freighter. + +### Signature + +```typescript +export async function signTransaction( + xdr: string, + network: string +): Promise { + const result = await freighterSignTransaction(xdr, { networkPassphrase: network }); ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `xdr` | Base64-encoded transaction XDR. | +| `network` | Network passphrase (e.g. "Test SDF Network ; September 2015"). | + +### Returns + +Signed transaction XDR. / + +--- + +## SimpleCache + +**Kind:** `class` + +### Signature + +```typescript +export class SimpleCache { + private readonly store = new Map>(); + private readonly ttlMs: number; + + constructor(ttlMs: number) { ... +``` + +--- + +## SimulateCreateInvoiceResult + +**Kind:** `interface` + +### Signature + +```typescript +export interface SimulateCreateInvoiceResult { + /** The invoice ID that would be created. */ + invoiceId: string; + /** Estimated fee in stroops. */ + fee: string; ... +``` + +--- + +## SimulatePayResult + +**Kind:** `interface` + +### Signature + +```typescript +export interface SimulatePayResult { + /** Estimated fee in stroops. */ + fee: string; +} +``` + +--- + +## SimulationDiff + +**Kind:** `type` + +### Signature + +```typescript +export type SimulationDiff = SimulationDiffSuccess | SimulationDiffNotComparable; +``` + +--- + +## SimulationDiffNotComparable + +**Kind:** `interface` + +### Signature + +```typescript +export interface SimulationDiffNotComparable { + comparable: false; + reason: string; +} +``` + +--- + +## SimulationDiffSuccess + +**Kind:** `interface` + +### Signature + +```typescript +export interface SimulationDiffSuccess { + comparable: true; + /** Difference in minResourceFee expressed in stroops (after − before). */ + feeDelta: bigint; + /** Number of diagnostic events that appear only in `after`. */ ... +``` + +--- + +## StellarSplitClient + +**Kind:** `class` + +### Signature + +```typescript +export class StellarSplitClient { + private _mainServer!: SorobanRpc.Server; + private _standby: WarmStandby | null = null; + private _queue = new PriorityQueue(); + private contract: Contract; ... +``` + +--- + +## StellarSplitClientConfig + +**Kind:** `interface` + +### Signature + +```typescript +export interface StellarSplitClientConfig { + /** Soroban RPC endpoint URL. Pass an array to enable warm-standby failover. */ + rpcUrl: string | string[]; + /** Stellar network passphrase. */ + networkPassphrase: string; ... +``` + +--- + +## StellarSplitError + +**Kind:** `class` + +### Signature + +```typescript +export class StellarSplitError extends Error { + /** The raw error string from the Soroban RPC, if available. */ + readonly raw: string; + + constructor(message: string, raw: string = message) { ... +``` + +--- + +## StellarSplitTxBuilder + +**Kind:** `class` + +### Signature + +```typescript +export class StellarSplitTxBuilder { + private readonly server: SorobanRpc.Server; + private readonly contract: Contract; + private readonly config: StellarSplitClientConfig; + private readonly sourceAddress: string; ... +``` + +--- + +## telemetry + +**Kind:** `const` + +### Signature + +```typescript +telemetry = new Telemetry() +``` + +--- + +## TelemetryCollector + +**Kind:** `class` + +### Signature + +```typescript +export class TelemetryCollector { + private startTime = Date.now(); + private methods = new Map(); + private readonly windowSize = 100; + ... +``` + +--- + +## TelemetryReport + +**Kind:** `interface` + +### Signature + +```typescript +export interface TelemetryReport { + period: number; + methods: Record; +} +``` + +--- + +## TokenInfo + +**Kind:** `interface` + +### Signature + +```typescript +export interface TokenInfo { + /** Token contract address. */ + address: string; + /** Token symbol (e.g., "USDC"). */ + symbol: string; ... +``` + +--- + +## TopPayer + +**Kind:** `interface` + +### Signature + +```typescript +export interface TopPayer { + address: string; + amount: bigint; +} +``` + +--- + +## triggerWebhook + +**Kind:** `function` + +### Signature + +```typescript +export async function triggerWebhook( + invoiceId: string, + event: WebhookEvent, + data: unknown, +): Promise { ... +``` + +--- + +## truncateAddress + +**Kind:** `function` + +Truncate a Stellar address for display: "GABC...XYZ". +/ + +### Signature + +```typescript +export function truncateAddress(address: string, chars = 4): string { + if (address.length <= chars * 2 + 3) return address; + return `${address.slice(0, chars)}...${address.slice(-chars)}`; +} +``` + +--- + +## TxQueue + +**Kind:** `class` + +### Signature + +```typescript +export class TxQueue { + private server: SorobanRpc.Server; + private networkPassphrase: string; + private sourceAddress: string; + private queue: Promise = Promise.resolve({ txHash: "" }); ... +``` + +--- + +## TxResult + +**Kind:** `interface` + +### Signature + +```typescript +export interface TxResult { + txHash: string; +} +``` + +--- + +## validateTransition + +**Kind:** `function` + +### Signature + +```typescript +export function validateTransition(from: InvoiceStatus, to: InvoiceStatus): boolean { + const allowed = TRANSITIONS[from]; + if (!allowed || !allowed.includes(to)) { + throw new InvalidTransitionError(from, to); + } ... +``` + +--- + +## validateWebhookSignature + +**Kind:** `function` + +### Signature + +```typescript +export async function validateWebhookSignature( + payload: unknown, + signature: string, + secret: string +): Promise { ... +``` + +--- + +## verifyMerkleProof + +**Kind:** `function` + +Verify a Merkle proof against a given root hash. + +### Signature + +```typescript +export function verifyMerkleProof(proof: MerkleProof): boolean { + // In a real implementation, this would: + // 1. Recompute the root hash from the leaf and path + // 2. Compare the computed root with the provided root + ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `proof` | The Merkle proof to verify | + +### Returns + +true if the proof is valid, false otherwise / + +--- + +## VersionInfo + +**Kind:** `interface` + +### Signature + +```typescript +export interface VersionInfo { + contractVersion: string; + sdkVersion: string; + compatible: boolean; +} +``` + +--- + +## WalletAdapter + +**Kind:** `interface` + +### Signature + +```typescript +export interface WalletAdapter { + /** Return the wallet's public key (G... address). */ + getAddress(): Promise; + /** + * Sign a transaction XDR string. ... +``` + +--- + +## WalletConnectAdapter + +**Kind:** `class` + +WalletConnect adapter — routes signing through a WalletConnect session +instead of the Freighter browser extension. +/ + +### Signature + +```typescript +export class WalletConnectAdapter implements WalletAdapter { + private readonly opts: WalletConnectAdapterOptions; + + constructor(opts: WalletConnectAdapterOptions) { + this.opts = opts; ... +``` + +--- + +## watchContractUpgrade + +**Kind:** `function` + +Watch for contract WASM upgrades and invoke callback when detected. +Polls the contract's WASM hash every 60 seconds. When a change is detected, +invokes the callback with the upgrade event. + +### Signature + +```typescript +export function watchContractUpgrade( + server: SorobanRpc.Server, + contractId: string, + callback: (event: UpgradeEvent) => void +): () => void { ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `server` | Soroban RPC server instance | +| `contractId` | The contract ID to watch | +| `callback` | Function to invoke when upgrade is detected | + +### Returns + +Cleanup function that stops polling / + +--- + +## watchExpiry + +**Kind:** `function` + +Watch an invoice for expiry and fire a callback when approaching deadline. +Polls the invoice deadline and fires the callback when the deadline is within +the warning window or has passed. + +### Signature + +```typescript +export function watchExpiry( + invoiceId: string, + client: StellarSplitClient, + callback: ExpiryCallback, + warningSeconds: number = 3600 ... +``` + +### Parameters + +| Name | Description | +|------|-------------| +| `invoiceId` | Invoice ID to watch | +| `client` | StellarSplitClient instance | +| `callback` | Function to call when expiry event occurs | +| `warningSeconds` | Seconds before deadline to trigger callback (default: 3600) | + +### Returns + +Cleanup function to stop polling / + +--- + +## WebhookConfig + +**Kind:** `type` + +### Signature + +```typescript +export type WebhookConfig = { + url: string; + events: WebhookEvent[]; +}; +``` + +--- + +## WebhookEvent + +**Kind:** `type` + +### Signature + +```typescript +export type WebhookEvent = "payment" | "released" | "refunded"; +``` + +--- + +## WeightedEndpoint + +**Kind:** `interface` + +Weighted endpoint configuration for load balancing. +/ + +### Signature + +```typescript +export interface WeightedEndpoint { + /** RPC endpoint URL */ + url: string; + /** Weight for this endpoint (higher = more requests) */ + weight: number; ... +``` + +--- + diff --git a/package-lock.json b/package-lock.json index 6fadad8..1baae93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "devDependencies": { "@types/node": "^25.9.1", "graphql": "^16.14.0", + "ts-node": "^10.9.2", "tsup": "^8.5.1", "typescript": "^5.9.3", "vitest": "^1.6.1" @@ -29,6 +30,30 @@ "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", "license": "MIT" }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -1094,6 +1119,34 @@ "node": ">=18.0.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "dev": true, @@ -1594,6 +1647,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/assertion-error": { "version": "1.1.0", "dev": true, @@ -1878,6 +1938,13 @@ "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", "license": "MIT" }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -1966,6 +2033,16 @@ "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", "license": "MIT" }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -2548,6 +2625,13 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "license": "MIT", @@ -3417,6 +3501,50 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3666,6 +3794,13 @@ "version": "1.19.11", "license": "MIT" }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "5.4.21", "dev": true, @@ -4316,6 +4451,16 @@ } } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "1.2.2", "dev": true, diff --git a/package.json b/package.json index 4028a14..0a28b9e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test:e2e": "vitest run test/e2e", "test:watch": "vitest", "lint": "tsc --noEmit", - "changelog": "vite-node scripts/changelog.ts" + "changelog": "vite-node scripts/changelog.ts", + "docs": "ts-node scripts/generate-docs.ts" }, "dependencies": { "@ledgerhq/hw-app-str": "^6.29.0", @@ -42,6 +43,7 @@ "devDependencies": { "@types/node": "^25.9.1", "graphql": "^16.14.0", + "ts-node": "^10.9.2", "tsup": "^8.5.1", "typescript": "^5.9.3", "vitest": "^1.6.1" diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts new file mode 100644 index 0000000..e815697 --- /dev/null +++ b/scripts/generate-docs.ts @@ -0,0 +1,279 @@ +import ts from "typescript"; +import { readFileSync, writeFileSync } from "node:fs"; +import { resolve, dirname } from "node:path"; + +interface JSDocTag { + name: string; + text: string; +} + +interface Export { + name: string; + kind: "function" | "class" | "type" | "interface" | "enum" | "const"; + signature: string; + description: string; + params: JSDocTag[]; + returns: JSDocTag | null; + examples: string[]; +} + +function extractJSDocText(text: string): { description: string; params: JSDocTag[]; returns: JSDocTag | null; examples: string[] } { + const lines = text.split("\n"); + let description = ""; + const params: JSDocTag[] = []; + let returns: JSDocTag | null = null; + const examples: string[] = []; + + let i = 0; + while (i < lines.length) { + const line = lines[i].replace(/^\s*\*\s?/, "").trim(); + if (line.startsWith("@")) break; + if (line && !line.startsWith("/*") && !line.endsWith("*/")) { + if (description) description += "\n"; + description += line; + } + i++; + } + + while (i < lines.length) { + const line = lines[i].replace(/^\s*\*\s?/, "").trim(); + if (line.startsWith("@param")) { + const match = line.match(/@param\s+(\w+)\s*-?\s*([\s\S]*?)$/); + if (match) { + let content = match[2]; + i++; + while (i < lines.length && !lines[i].match(/^\s*\*\s*@/)) { + const part = lines[i].replace(/^\s*\*\s?/, "").trim(); + if (part) content += " " + part; + i++; + } + params.push({ name: match[1], text: content.trim() }); + continue; + } + } else if (line.startsWith("@returns") || line.startsWith("@return")) { + const match = line.match(/@returns?\s+([\s\S]*?)$/); + if (match) { + let content = match[1]; + i++; + while (i < lines.length && !lines[i].match(/^\s*\*\s*@/)) { + const part = lines[i].replace(/^\s*\*\s?/, "").trim(); + if (part) content += " " + part; + i++; + } + returns = { name: "returns", text: content.trim() }; + continue; + } + } else if (line.startsWith("@example")) { + const match = line.match(/@example\s+([\s\S]*?)$/); + if (match) { + let content = match[1]; + i++; + while (i < lines.length && !lines[i].match(/^\s*\*\s*@/)) { + const part = lines[i].replace(/^\s*\*\s?/, "").trim(); + if (part) content += "\n" + part; + i++; + } + examples.push(content.trim()); + continue; + } + } + i++; + } + + return { description: description.trim(), params, returns, examples }; +} + +function extractJSDocFromNode(node: ts.Node | undefined, sourceFile: ts.SourceFile): { description: string; params: JSDocTag[]; returns: JSDocTag | null; examples: string[] } { + if (!node) return { description: "", params: [], returns: null, examples: [] }; + + // Get JSDoc using getLeadingCommentRanges from source file text + const sourceText = sourceFile.getFullText(); + const nodeStart = node.getFullStart(); + const leadingComments = ts.getLeadingCommentRanges(sourceText, nodeStart); + + if (!leadingComments || leadingComments.length === 0) { + return { description: "", params: [], returns: null, examples: [] }; + } + + const lastComment = leadingComments[leadingComments.length - 1]; + const commentText = sourceText.substring(lastComment.pos, lastComment.end); + + if (!commentText.includes("/**")) { + return { description: "", params: [], returns: null, examples: [] }; + } + + return extractJSDocText(commentText); +} + +function getSignature(node: ts.Node, sourceFile: ts.SourceFile): string { + const start = node.getStart(sourceFile); + const end = node.getEnd(); + let source = sourceFile.text.substring(start, end); + const lines = source.split("\n"); + if (lines.length > 5) { + source = lines.slice(0, 5).join("\n") + " ..."; + } + return source.trim(); +} + +interface Declaration { + node: ts.Node; + kind: "function" | "class" | "type" | "interface" | "enum" | "const"; +} + +function findDeclaration(sourceFile: ts.SourceFile, name: string): Declaration | null { + let result: Declaration | null = null; + + function visit(node: ts.Node): void { + if (!result) { + if (ts.isFunctionDeclaration(node) && node.name?.getText() === name) { + result = { node, kind: "function" }; + } else if (ts.isClassDeclaration(node) && node.name?.getText() === name) { + result = { node, kind: "class" }; + } else if (ts.isTypeAliasDeclaration(node) && node.name.getText() === name) { + result = { node, kind: "type" }; + } else if (ts.isInterfaceDeclaration(node) && node.name.getText() === name) { + result = { node, kind: "interface" }; + } else if (ts.isEnumDeclaration(node) && node.name.getText() === name) { + result = { node, kind: "enum" }; + } else if (ts.isVariableDeclaration(node) && node.name.getText() === name) { + result = { node, kind: "const" }; + } else { + ts.forEachChild(node, visit); + } + } + } + + visit(sourceFile); + return result; +} + +function loadSourceFile(filePath: string): ts.SourceFile { + const content = readFileSync(filePath, "utf8"); + return ts.createSourceFile(filePath, content, ts.ScriptTarget.ES2020, true); +} + +function parseExports(indexContent: string, srcDir: string): Map { + const exports = new Map(); + const sourceFileCache = new Map(); + + function getSourceFile(relativePath: string): ts.SourceFile | null { + const normalized = relativePath.replace(/\.js$/, ".ts"); + const fullPath = resolve(srcDir, normalized); + + if (sourceFileCache.has(fullPath)) { + return sourceFileCache.get(fullPath)!; + } + + try { + const sf = loadSourceFile(fullPath); + sourceFileCache.set(fullPath, sf); + return sf; + } catch { + return null; + } + } + + // Match: export { name1, name2 as alias } from "./module.js" + const exportRegex = /export\s+(?:type\s+)?\{([^}]+)\}\s+from\s+["']([^"']+)["']/g; + let match; + + while ((match = exportRegex.exec(indexContent)) !== null) { + const names = match[1].split(",").map((n) => n.trim()); + const fromModule = match[2]; + + const moduleSource = getSourceFile(fromModule); + if (!moduleSource) continue; + + for (const nameSpec of names) { + const [imported, exported] = nameSpec.includes(" as ") ? nameSpec.split(" as ").map((s) => s.trim()) : [nameSpec, nameSpec]; + + const found = findDeclaration(moduleSource, imported); + if (found) { + const { description, params, returns, examples } = extractJSDocFromNode(found.node, moduleSource); + exports.set(exported, { + name: exported, + kind: found.kind, + signature: getSignature(found.node, moduleSource), + description, + params, + returns, + examples, + }); + } + } + } + + return exports; +} + +function renderMarkdown(exports: Export[]): string { + const sorted = Array.from(exports).sort((a, b) => a.name.localeCompare(b.name)); + let md = "# API Reference\n\n"; + md += `Auto-generated API documentation for @stellar-split/sdk. Total exports: ${sorted.length}\n\n`; + md += "## Table of Contents\n\n"; + + for (const exp of sorted) { + md += `- [${exp.name}](#${exp.name.toLowerCase()})\n`; + } + md += "\n---\n\n"; + + for (const exp of sorted) { + md += `## ${exp.name}\n\n`; + md += `**Kind:** \`${exp.kind}\`\n\n`; + + if (exp.description) { + md += `${exp.description}\n\n`; + } + + md += "### Signature\n\n"; + md += "```typescript\n"; + md += exp.signature + "\n"; + md += "```\n\n"; + + if (exp.params.length > 0) { + md += "### Parameters\n\n"; + md += "| Name | Description |\n"; + md += "|------|-------------|\n"; + for (const param of exp.params) { + const desc = param.text.replace(/\|/g, "\\|").replace(/\n/g, " "); + md += `| \`${param.name}\` | ${desc} |\n`; + } + md += "\n"; + } + + if (exp.returns) { + md += "### Returns\n\n"; + md += `${exp.returns.text}\n\n`; + } + + if (exp.examples.length > 0) { + md += "### Examples\n\n"; + for (const example of exp.examples) { + md += "```typescript\n"; + md += example + "\n"; + md += "```\n\n"; + } + } + + md += "---\n\n"; + } + + return md; +} + +function generate(): void { + const indexPath = resolve(process.cwd(), "src/index.ts"); + const srcDir = resolve(process.cwd(), "src"); + + const indexContent = readFileSync(indexPath, "utf8"); + const exports = parseExports(indexContent, srcDir); + const markdown = renderMarkdown(Array.from(exports.values())); + + const outputPath = resolve(process.cwd(), "docs/API.md"); + writeFileSync(outputPath, markdown, "utf8"); + console.log(`✓ Generated API documentation: ${outputPath}`); + console.log(`✓ Documented ${exports.size} exports`); +} + +generate();