@routedock/routedock 0.1.1 → 0.2.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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -19
  3. package/dist/MppSessionHandler-B4TyD7Xg.d.cts +121 -0
  4. package/dist/MppSessionHandler-CLB5SzgN.d.ts +121 -0
  5. package/dist/ProviderRegistry-BJ9oJLVz.d.cts +142 -0
  6. package/dist/ProviderRegistry-C50nyhaF.d.ts +142 -0
  7. package/dist/RouteDockClient-B1roCa8h.d.cts +455 -0
  8. package/dist/RouteDockClient-BbkqjhMG.d.ts +455 -0
  9. package/dist/SessionReconciler-Dscw43F3.d.ts +125 -0
  10. package/dist/SessionReconciler-bi0z2qtN.d.cts +125 -0
  11. package/dist/SessionStore-BpK8vv0L.d.cts +111 -0
  12. package/dist/SessionStore-DvkZv8-E.d.ts +111 -0
  13. package/dist/client.cjs +2419 -202
  14. package/dist/client.d.cts +50 -81
  15. package/dist/client.d.ts +50 -81
  16. package/dist/client.js +2373 -203
  17. package/dist/index.cjs +3080 -298
  18. package/dist/index.d.cts +12 -3
  19. package/dist/index.d.ts +12 -3
  20. package/dist/index.js +3026 -298
  21. package/dist/provider/fastify.cjs +882 -0
  22. package/dist/provider/fastify.d.cts +35 -0
  23. package/dist/provider/fastify.d.ts +35 -0
  24. package/dist/provider/fastify.js +880 -0
  25. package/dist/provider/hono.cjs +948 -0
  26. package/dist/provider/hono.d.cts +99 -0
  27. package/dist/provider/hono.d.ts +99 -0
  28. package/dist/provider/hono.js +938 -0
  29. package/dist/provider.cjs +827 -97
  30. package/dist/provider.d.cts +32 -25
  31. package/dist/provider.d.ts +32 -25
  32. package/dist/provider.js +810 -99
  33. package/dist/react.cjs +2641 -0
  34. package/dist/react.d.cts +107 -0
  35. package/dist/react.d.ts +107 -0
  36. package/dist/react.js +2634 -0
  37. package/dist/routedockMiddleware-BlDsHuPl.d.ts +59 -0
  38. package/dist/routedockMiddleware-D97A5ptQ.d.cts +59 -0
  39. package/dist/schema.cjs +364 -0
  40. package/dist/schema.d.cts +3 -0
  41. package/dist/schema.d.ts +3 -0
  42. package/dist/schema.js +359 -0
  43. package/dist/sign-CUf3rhTx.d.cts +53 -0
  44. package/dist/sign-Dpj2Mp7k.d.ts +53 -0
  45. package/dist/testing.cjs +57 -0
  46. package/dist/testing.d.cts +95 -0
  47. package/dist/testing.d.ts +95 -0
  48. package/dist/testing.js +54 -0
  49. package/dist/types-DmhmfLzP.d.cts +410 -0
  50. package/dist/types-DmhmfLzP.d.ts +410 -0
  51. package/package.json +143 -23
  52. package/PUBLISH_CHECKLIST.md +0 -70
  53. package/dist/SessionStore-CCVO0PHM.d.cts +0 -152
  54. package/dist/SessionStore-CCVO0PHM.d.ts +0 -152
  55. package/dist/routedockMiddleware-B7WTB3LT.d.cts +0 -53
  56. package/dist/routedockMiddleware-DRgdrycG.d.ts +0 -53
  57. package/src/client/ModeRouter.ts +0 -104
  58. package/src/client/MppChargeClient.ts +0 -44
  59. package/src/client/MppSessionClient.ts +0 -144
  60. package/src/client/RouteDockClient.ts +0 -124
  61. package/src/client/index.ts +0 -8
  62. package/src/client/x402Client.ts +0 -84
  63. package/src/index.ts +0 -9
  64. package/src/provider/MppChargeHandler.ts +0 -90
  65. package/src/provider/MppSessionHandler.ts +0 -157
  66. package/src/provider/index.ts +0 -7
  67. package/src/provider/routedockMiddleware.ts +0 -154
  68. package/src/provider/x402Handler.ts +0 -164
  69. package/src/schemas/routedock.schema.json +0 -159
  70. package/src/store/SessionStore.ts +0 -90
  71. package/src/tests/smoke.test.ts +0 -228
  72. package/src/types.ts +0 -165
  73. package/tsconfig.json +0 -14
  74. package/tsup.config.ts +0 -15
@@ -0,0 +1,455 @@
1
+ import { Keypair } from '@stellar/stellar-sdk';
2
+ import { P as PaymentMode, R as RouteDockManifest, d as PreflightResult, c as PaymentResult, b as EstimateCostResult, k as SessionOptions, j as SessionHandle } from './types-DmhmfLzP.js';
3
+ import { ClientStellarSigner } from '@x402/stellar';
4
+ import { SignAuthEntry } from '@stellar/stellar-sdk/contract';
5
+ import { usdcToStroops } from '@routedock/nulth-sdk';
6
+
7
+ interface RetryPolicy {
8
+ /** Total attempts including the first call. Default: 4 (3 retries). */
9
+ maxAttempts?: number;
10
+ /** Base delay for exponential backoff in ms. Default: 250. */
11
+ baseDelayMs?: number;
12
+ /** Maximum jittered backoff delay in ms. Default: 30000. */
13
+ maxDelayMs?: number;
14
+ /** Called before each retry with the attempt number, error, and upcoming delay. */
15
+ /**
16
+ * Observability hook fired before each retry. Awaited, so a returned promise
17
+ * is honoured; throwing or rejecting is caught and logged rather than
18
+ * aborting the retry loop.
19
+ */
20
+ onRetry?: (attempt: number, error: Error, nextDelayMs: number) => void | Promise<void>;
21
+ }
22
+ declare const DEFAULT_RETRY_POLICY: Required<Omit<RetryPolicy, 'onRetry'>>;
23
+ /**
24
+ * Full jitter exponential backoff delay.
25
+ * Delay range: 0..min(maxDelayMs, baseDelayMs * 2 ** attempt).
26
+ */
27
+ declare function backoffDelayMs(attempt: number, baseDelayMs: number, maxDelayMs?: number): number;
28
+ /**
29
+ * Execute `fn` with exponential backoff on retryable RouteDockErrors.
30
+ * Honors `retryAfterMs` from facilitator 429/503 responses when present.
31
+ */
32
+ declare function withRetry<T>(fn: () => Promise<T>, policy?: RetryPolicy): Promise<T>;
33
+
34
+ /**
35
+ * Enforce the per-endpoint `deprecated` and `sunset_at` metadata for the URL
36
+ * being paid. `pay()` never learns the HTTP method, so a path shared by more
37
+ * than one descriptor (e.g. GET and POST on `/infer`) is refused only when
38
+ * every matching descriptor has sunset. Paths the manifest does not list keep
39
+ * today's behavior. Query strings and hashes are ignored because only
40
+ * `URL.pathname` is compared.
41
+ */
42
+ declare function assertEndpointActive(manifest: RouteDockManifest, url: string, logger?: RouteDockLogger): void;
43
+ /**
44
+ * Validate semantic constraints for manifest fields beyond JSON schema syntax.
45
+ * Enforces that all keys in `latency_hints` must be a subset of declared `regions`.
46
+ */
47
+ declare function assertManifestValid(manifest: RouteDockManifest, baseUrl?: string): void;
48
+ /**
49
+ * Override the manifest cache's max size (default 512). Affects the shared,
50
+ * process-wide cache used by all RouteDockClient instances.
51
+ */
52
+ declare function configureManifestCache(maxSize: number): void;
53
+ /**
54
+ * Evict a provider's cached manifest immediately, forcing the next
55
+ * `fetchManifest(baseUrl)` to re-fetch and re-validate it. Use when a
56
+ * provider signals that their manifest changed (e.g. after a config update
57
+ * or a failed payment due to a mode change) so the next request does not
58
+ * keep routing on a stale cached copy for the remainder of the TTL.
59
+ *
60
+ * The cache is keyed by base URL as passed to `fetchManifest` — callers that
61
+ * pass a full URL (e.g. `RouteDockClient.invalidateManifest`) must normalize
62
+ * to the origin before calling this.
63
+ */
64
+ declare function invalidateManifest(baseUrl: string): void;
65
+ type RouteDockLogger = (message: string) => void;
66
+ interface ModeSelectOptions {
67
+ /** Force mpp-session if the provider supports it */
68
+ sustained?: boolean;
69
+ session?: boolean;
70
+ /**
71
+ * Preferred streaming transport for sustained sessions. When 'websocket',
72
+ * the mpp-session-ws variant is preferred when the provider advertises it;
73
+ * when 'sse' (or omitted), the classic mpp-session mode is preferred.
74
+ * Either transport falls back to the other session variant when the
75
+ * preferred one is not advertised.
76
+ */
77
+ transport?: 'sse' | 'websocket';
78
+ /** Prefer the lowest-cost supported per-request mode when set to 'cost'. */
79
+ optimize?: 'cost';
80
+ /** Optional maximum acceptable per-request amount for cost-based selection. */
81
+ budget_per_request?: string;
82
+ /**
83
+ * Override mode selection and use this specific mode.
84
+ * Throws RouteDockNoSupportedModeError if the provider does not support it.
85
+ */
86
+ forceMode?: PaymentMode;
87
+ /** Structured logger for mode selection events. Defaults to no-op. */
88
+ logger?: RouteDockLogger;
89
+ }
90
+ /**
91
+ * Fetch, validate, and cache a RouteDock manifest from `baseUrl`.
92
+ *
93
+ * `expectedPayee`, when provided, is the out-of-band trust anchor for the
94
+ * manifest (e.g. the account this provider registered on-chain): the fetched
95
+ * manifest's `payee` must equal it or `RouteDockSignatureError` is thrown.
96
+ * Callers that obtained a payee from a registry/pinned allowlist MUST pass it;
97
+ * without it the self-signed signature alone proves nothing about who served
98
+ * the manifest.
99
+ */
100
+ declare function fetchManifest(baseUrl: string, retryPolicy?: RetryPolicy, manifestTimeoutMs?: number, expectedPayee?: string): Promise<RouteDockManifest>;
101
+ /**
102
+ * Deterministic mode selection per Section 6.3 of ROUTEDOCK_MASTER.md.
103
+ *
104
+ * Active modes are always considered before modes listed in `deprecated_modes`.
105
+ * Deprecated modes remain usable as a compatibility fallback and produce a
106
+ * logger warning when selected. An explicit forceMode remains authoritative,
107
+ * but also warns when the forced mode is deprecated.
108
+ */
109
+ declare function selectMode(manifest: RouteDockManifest, options?: ModeSelectOptions): PaymentMode;
110
+ /**
111
+ * Rank a list of provider manifests by estimated p50 round-trip latency for a given target region.
112
+ * Manifests declaring latency_hints for the target region are sorted ascending by latency (lowest first).
113
+ * Manifests that list the region in `regions` without an explicit latency hint come next.
114
+ * Manifests not advertising the target region come last.
115
+ */
116
+ declare function rankProvidersByLatency(manifests: RouteDockManifest[], targetRegion: string): RouteDockManifest[];
117
+
118
+ interface NulthPolicyState {
119
+ dailyCapStroops: bigint;
120
+ allowlistCommitment: string;
121
+ allowedPayees: readonly string[];
122
+ dailySpendStroops: bigint;
123
+ dayBucket: number;
124
+ witnessSecret: string;
125
+ expiryLedger?: number;
126
+ }
127
+ interface PaymentAuthContext {
128
+ authEntry: string;
129
+ payee: string;
130
+ amountStroops: bigint;
131
+ assetContract: string;
132
+ ledgerSequence: number;
133
+ }
134
+ interface NulthProof {
135
+ version: 1;
136
+ proof: string;
137
+ publicInputs: {
138
+ authDigest: string;
139
+ allowlistCommitment: string;
140
+ capCommitment: string;
141
+ payeeHash: string;
142
+ amountStroops: string;
143
+ dayBucket: string;
144
+ };
145
+ }
146
+ interface NulthAuthSignature {
147
+ nulth: 'zk-v1';
148
+ proof: NulthProof;
149
+ }
150
+ interface NulthClientConfig {
151
+ nulthAccount: string;
152
+ network: 'testnet' | 'mainnet';
153
+ prover?: 'mock';
154
+ policy: NulthPolicyState;
155
+ verifierContract?: string;
156
+ }
157
+ declare class NulthPolicyError extends Error {
158
+ readonly code: 'payee_not_allowed' | 'daily_cap_exceeded' | 'session_expired' | 'auth_entry_mismatch';
159
+ constructor(code: NulthPolicyError['code'], message: string);
160
+ }
161
+ interface NulthStellarSigner {
162
+ address: string;
163
+ signAuthEntry: SignAuthEntry;
164
+ }
165
+ interface NulthSignerConfig extends NulthClientConfig {
166
+ paymentContext?: Omit<PaymentAuthContext, 'authEntry'>;
167
+ }
168
+ declare function createNulthSigner(config: NulthSignerConfig): NulthStellarSigner;
169
+ declare function setNulthPaymentContext(config: NulthSignerConfig, context: Omit<PaymentAuthContext, 'authEntry'>): void;
170
+ declare function paymentContextFromManifest(manifest: {
171
+ payee: string;
172
+ asset_contract: string;
173
+ pricing: {
174
+ x402?: {
175
+ amount: string;
176
+ };
177
+ 'mpp-charge'?: {
178
+ amount: string;
179
+ };
180
+ };
181
+ }, mode: 'x402' | 'mpp-charge', ledgerSequence: number): Omit<PaymentAuthContext, 'authEntry'>;
182
+ declare function createPolicyState(input: {
183
+ dailyCapUsdc: string;
184
+ allowedPayees: readonly string[];
185
+ witnessSecret: string;
186
+ expiryLedger?: number;
187
+ ledgerSequence?: number;
188
+ }): NulthPolicyState;
189
+ interface NulthVaultConfig {
190
+ mode: 'nulth';
191
+ nulthAccount: string;
192
+ prover?: 'mock';
193
+ witnessSecret: string;
194
+ allowedPayees: readonly string[];
195
+ dailyCapUsdc: string;
196
+ expiryLedger?: number;
197
+ verifierContract?: string;
198
+ }
199
+ declare function assertNulthVaultManifest(manifest: RouteDockManifest, nulthAccount: string): void;
200
+ declare function fetchLedgerSequence(network: 'testnet' | 'mainnet'): Promise<number>;
201
+ declare function prepareNulthSigner(vault: NulthVaultConfig, manifest: RouteDockManifest, mode: PaymentMode, network: 'testnet' | 'mainnet', ledgerSequenceOverride?: number): Promise<{
202
+ signer: ClientStellarSigner;
203
+ config: NulthSignerConfig;
204
+ }>;
205
+
206
+ declare function decodeAuthSignature(encoded: string): NulthAuthSignature;
207
+
208
+ /**
209
+ * Daily spend accumulator, keyed by an ISO date (YYYY-MM-DD).
210
+ *
211
+ * Totals are decimal strings of integer microUSDC (1 USDC = 10^7 units) so
212
+ * the record stays JSON-serializable with no floating point precision loss.
213
+ */
214
+ interface DailySpend {
215
+ date: string;
216
+ /** Global accumulated spend for the day, in microUSDC (e.g. "5000000") */
217
+ totalMicros: string;
218
+ /** Per-endpoint accumulated spend for the day, keyed by origin URL, in microUSDC */
219
+ endpoints: Record<string, string>;
220
+ }
221
+ /**
222
+ * Durable backing store for the client's local daily spend cap.
223
+ *
224
+ * The spend cap is a financial safety control: if its accumulator lives only in
225
+ * heap memory, any crash, OOM kill, or container restart silently resets it to
226
+ * zero and the cap can be bypassed. Inject a persistent implementation
227
+ * (Redis, SQL, Supabase, a file, …) so the counter survives restarts.
228
+ */
229
+ interface SpendStore {
230
+ /** Return the persisted accumulator, or null if none has been written yet. */
231
+ read(): Promise<DailySpend | null>;
232
+ /** Persist the accumulator. */
233
+ write(state: DailySpend): Promise<void>;
234
+ }
235
+ interface InMemorySpendStoreOptions {
236
+ /** Log a startup warning about non-durability. Defaults to true. */
237
+ warn?: boolean;
238
+ }
239
+ /**
240
+ * Default, non-durable {@link SpendStore}. Holds the accumulator on the heap, so
241
+ * it resets on every process restart. Logs a startup warning unless silenced.
242
+ * Use this only for development; inject a persistent store in production.
243
+ */
244
+ declare class InMemorySpendStore implements SpendStore {
245
+ private state;
246
+ constructor(options?: InMemorySpendStoreOptions);
247
+ read(): Promise<DailySpend | null>;
248
+ write(state: DailySpend): Promise<void>;
249
+ }
250
+
251
+ interface SpendCap {
252
+ /** Maximum total USDC spend per day (decimal string, e.g. "1.00") */
253
+ daily: string;
254
+ asset: 'USDC';
255
+ /**
256
+ * Optional per-endpoint daily spend caps, keyed by endpoint origin URL
257
+ * (e.g. "https://api.openai.com"). Checked before the global `daily` cap.
258
+ * An endpoint not listed here is only subject to the global cap.
259
+ * Both limits are enforced independently — hitting an endpoint cap does
260
+ * not prevent spend on other endpoints, but all spend still counts toward
261
+ * the global cap.
262
+ *
263
+ * Keys are normalized to `new URL(key).origin` when the client is
264
+ * constructed (lowercased host, no trailing slash, no default port), so
265
+ * "https://API.example.com/", "https://api.example.com" and
266
+ * "https://api.example.com:443" are all equivalent. A key that isn't a
267
+ * valid URL, that includes a path/query/hash, or that normalizes to the
268
+ * same origin as another key throws at construction time.
269
+ */
270
+ endpointCaps?: Record<string, string>;
271
+ }
272
+ type VaultConfig = NulthVaultConfig;
273
+ interface RouteDockClientConfig {
274
+ /** Stellar keypair or raw secret key (S...) — fee payer / fallback signer */
275
+ wallet: Keypair | string;
276
+ network: 'testnet' | 'mainnet';
277
+ /** Optional local daily spend cap — checked before every payment (local-key vault only) */
278
+ spendCap?: SpendCap;
279
+ /**
280
+ * Ed25519 secret key (S...) for signing channel commitments. Required for mpp-session.
281
+ *
282
+ * WARNING: Do not log or serialize the config object — it contains this secret in plaintext.
283
+ * The RouteDockClient stores it outside the instance to prevent leakage via JSON.stringify
284
+ * or console.log, but the raw config object is not protected.
285
+ */
286
+ commitmentSecret?: string | undefined;
287
+ /** Retry policy for transient failures (network, facilitator 5xx). */
288
+ retryPolicy?: RetryPolicy;
289
+ /**
290
+ * Durable backing store for the daily spend cap accumulator. Defaults to a
291
+ * non-durable in-memory store that resets on restart (with a startup warning).
292
+ * Inject a persistent implementation for production safety.
293
+ */
294
+ spendStore?: SpendStore;
295
+ /** Structured logger for SDK events. Defaults to no-op (silent). */
296
+ logger?: RouteDockLogger;
297
+ /**
298
+ * Timeout in milliseconds for manifest fetches. A provider that accepts the TCP
299
+ * connection but never sends a response body will be aborted after this delay.
300
+ * Default: 5000 ms.
301
+ */
302
+ manifestTimeoutMs?: number;
303
+ /**
304
+ * Out-of-band trust anchor for manifests: the Stellar payee (G...) the client
305
+ * expects the provider to be (e.g. the account registered in the on-chain
306
+ * provider registry, or a pinned/allowlisted key). When set, every fetched
307
+ * manifest's `payee` must equal this value or payment is refused with
308
+ * `RouteDockSignatureError`. Without it, a self-signed manifest proves nothing
309
+ * about who served it, so set this whenever you obtained a provider address
310
+ * from a registry.
311
+ */
312
+ expectedPayee?: string;
313
+ /**
314
+ * Vault custody mode. When `nulth`, payments use a Nulth account as payer
315
+ * with off-chain ZK proofs attached as auth signatures.
316
+ */
317
+ vault?: VaultConfig;
318
+ }
319
+ /**
320
+ * @deprecated Use {@link usdcToStroops} — the same validated bigint converter,
321
+ * shared across the SDK and nulth-sdk. Kept as a thin alias so existing spend
322
+ * cap code keeps working during migration.
323
+ */
324
+ declare const usdcToMicros: typeof usdcToStroops;
325
+ declare class RouteDockClient {
326
+ /** Per-(account,asset) trustline existence cache keyed by `${network}:${pubkey}:${asset}` */
327
+ private static _trustlineCache;
328
+ private readonly keypair;
329
+ private readonly network;
330
+ private readonly spendCap;
331
+ private readonly retryPolicy;
332
+ private readonly logger;
333
+ private readonly manifestTimeoutMs;
334
+ private readonly expectedPayee;
335
+ private readonly vault;
336
+ /**
337
+ * Durable backing store for the local daily spend accumulator (keyed by
338
+ * YYYY-MM-DD). Totals are persisted as decimal strings of microUSDC
339
+ * (1 USDC = 10^7) so stores stay JSON-safe with no precision loss.
340
+ */
341
+ private readonly spendStore;
342
+ private x402;
343
+ private readonly charge;
344
+ private readonly session;
345
+ /**
346
+ * Promise-chain mutex for serializing read-modify-write on the spend
347
+ * accumulator. Each pay() call awaits the previous one, preventing
348
+ * concurrent read-modify-write races that could overrun the cap.
349
+ */
350
+ private _spendMutex;
351
+ /**
352
+ * Pending spend reservations keyed by reserveId. Used by _rollbackSpend
353
+ * to revert the accumulator when a payment fails after reservation.
354
+ */
355
+ private _pendingReserves;
356
+ constructor(config: RouteDockClientConfig);
357
+ /** Fetch manifest and select mode — shared by pay() and estimateCost(). */
358
+ private _resolveManifest;
359
+ /**
360
+ * Serialize async work through the per-instance spend mutex so concurrent
361
+ * pay() calls cannot interleave read-modify-write on the accumulator.
362
+ */
363
+ private _withSpendMutex;
364
+ /**
365
+ * Check that the payer has a trustline for the payment asset and surface
366
+ * manifest metadata (regions, latency hints, capabilities). Safe to
367
+ * call before committing to a payment — for approval gates and manual
368
+ * trustline remediation.
369
+ */
370
+ preflight(manifest: RouteDockManifest): Promise<PreflightResult>;
371
+ /**
372
+ * Verify via Horizon that the payer account has a trustline for the
373
+ * payment asset declared in the manifest. Throws RouteDockTrustlineError
374
+ * with an exact Stellar CLI change-trust remediation command when the
375
+ * trustline is missing. Results are cached per (account, asset) so the
376
+ * per-payment overhead is one RPC call amortized to zero after the first.
377
+ */
378
+ private _checkTrustline;
379
+ /**
380
+ * Pay for one request at `url`. Fetches manifest, selects payment mode,
381
+ * runs trustline preflight, reserves local spend cap BEFORE executing the
382
+ * payment, then commits the spend record on success. Rolls back the
383
+ * reservation if the on-chain payment fails. Concurrent pay() calls are
384
+ * serialized through a per-instance mutex to prevent spend-cap overrun.
385
+ */
386
+ pay(url: string, options?: ModeSelectOptions): Promise<PaymentResult>;
387
+ /** Nulth ZK vault path — proof built off-chain, attached as auth signature */
388
+ private _payWithNulthVault;
389
+ /**
390
+ * Resolve manifest and compute the expected charge WITHOUT submitting any
391
+ * transaction. Safe to call before committing — for approval gates and
392
+ * budget-aware routing.
393
+ */
394
+ estimateCost(url: string, options?: ModeSelectOptions): Promise<EstimateCostResult>;
395
+ /**
396
+ * Open a sustained MPP session at `url`. Verifies mpp-session is supported
397
+ * before opening a channel. Returns a SessionHandle for streaming + closing.
398
+ *
399
+ * By default the session auto-closes after a wall-clock timeout (1h) so an
400
+ * orphaned channel cannot keep collateral locked on-chain — override or
401
+ * disable via `options.maxDurationMs`.
402
+ */
403
+ openSession(url: string, options?: SessionOptions): Promise<SessionHandle>;
404
+ /**
405
+ * Record spend for an MPP session voucher. Called before each voucher
406
+ * fetch in stream(). Enforces the same local daily and per-endpoint caps
407
+ * as discrete pay() calls. Returns a commit function to clean up the
408
+ * reservation after the voucher fetch succeeds.
409
+ */
410
+ private _recordSessionSpend;
411
+ /**
412
+ * Remove the commitment secret from memory and make it eligible for GC.
413
+ * Call when this client instance is no longer needed — particularly in
414
+ * long-lived processes or Worker threads.
415
+ */
416
+ dispose(): void;
417
+ /**
418
+ * Evict the cached manifest for `url` immediately, so the next pay() /
419
+ * estimateCost() / openSession() call re-fetches it instead of routing on
420
+ * the stale cached copy. Use when a provider signals their manifest
421
+ * changed (e.g. after a config update) and the change must take effect
422
+ * before the cache TTL expires.
423
+ */
424
+ invalidateManifest(url: string): void;
425
+ /**
426
+ * Reserve spend against local daily caps BEFORE dispatching payment.
427
+ * Returns a reserveId that must be passed to _commitSpend() on success
428
+ * or _rollbackSpend() on failure.
429
+ *
430
+ * The entire read-check-write is serialized through the per-instance
431
+ * mutex (_withSpendMutex) so concurrent pay() calls cannot interleave
432
+ * and overrun the cap.
433
+ *
434
+ * Enforcement order:
435
+ * 1. Per-endpoint cap (if `endpointCaps[endpointKey]` is set) — checked first.
436
+ * 2. Global daily cap — checked second.
437
+ *
438
+ * Both limits are independent: a payment is only reserved after **both** pass.
439
+ * All spend (regardless of endpoint) counts toward the global accumulator.
440
+ */
441
+ private _checkAndReserveSpend;
442
+ /**
443
+ * Commit a previously reserved spend after payment succeeds. For discrete
444
+ * pay() calls this is a no-op (the spend was already recorded by reserve).
445
+ * Reserved for future use if the committed amount differs from the reserved.
446
+ */
447
+ private _commitSpend;
448
+ /**
449
+ * Rollback a previously reserved spend when payment fails. Reverts the
450
+ * tentative accumulator write done by _checkAndReserveSpend.
451
+ */
452
+ private _rollbackSpend;
453
+ }
454
+
455
+ export { rankProvidersByLatency as A, selectMode as B, setNulthPaymentContext as C, DEFAULT_RETRY_POLICY as D, usdcToMicros as E, withRetry as F, InMemorySpendStore as I, type ModeSelectOptions as M, type NulthAuthSignature as N, type PaymentAuthContext as P, type RetryPolicy as R, type SpendCap as S, type VaultConfig as V, type DailySpend as a, type InMemorySpendStoreOptions as b, type NulthClientConfig as c, NulthPolicyError as d, type NulthPolicyState as e, type NulthProof as f, type NulthSignerConfig as g, type NulthStellarSigner as h, type NulthVaultConfig as i, RouteDockClient as j, type RouteDockClientConfig as k, type RouteDockLogger as l, type SpendStore as m, assertEndpointActive as n, assertManifestValid as o, assertNulthVaultManifest as p, backoffDelayMs as q, configureManifestCache as r, createNulthSigner as s, createPolicyState as t, decodeAuthSignature as u, fetchLedgerSequence as v, fetchManifest as w, invalidateManifest as x, paymentContextFromManifest as y, prepareNulthSigner as z };
@@ -0,0 +1,125 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { S as SeenTxStore, a as SettlementRecord } from './MppSessionHandler-CLB5SzgN.js';
3
+ import { R as RouteDockManifest } from './types-DmhmfLzP.js';
4
+ import { Keypair } from '@stellar/stellar-sdk';
5
+
6
+ /**
7
+ * Settlement idempotency backed by Postgres via Supabase.
8
+ *
9
+ * The in-memory default {@link InMemorySeenTxStore} is per-handler and
10
+ * per-process. On serverless/edge runtimes (Cloudflare Workers, Deno Deploy,
11
+ * Lambda@Edge) each request may land in a fresh isolate with an empty cache.
12
+ * An agent retrying after a post-settlement timeout would miss the cache and
13
+ * settle a second time, charging twice on-chain.
14
+ *
15
+ * Postgres gives the read-your-writes consistency this needs; Workers KV
16
+ * would not, since a retry can outrun propagation and reintroduce the double
17
+ * settle.
18
+ *
19
+ * Requires the `settlements` table from migration `003_settlement_idempotency`:
20
+ *
21
+ * ```sql
22
+ * CREATE TABLE settlements (
23
+ * key TEXT PRIMARY KEY,
24
+ * tx_hash TEXT,
25
+ * headers JSONB,
26
+ * created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
27
+ * );
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { SupabaseSeenTxStore } from '@routedock/routedock/store'
33
+ * import { createClient } from '@supabase/supabase-js'
34
+ *
35
+ * const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
36
+ * const seenTxStore = new SupabaseSeenTxStore(supabase)
37
+ *
38
+ * routedockHono({ ..., seenTxStore })
39
+ * ```
40
+ */
41
+ declare class SupabaseSeenTxStore implements SeenTxStore {
42
+ private readonly supabase;
43
+ constructor(supabase: SupabaseClient);
44
+ get(key: string): Promise<SettlementRecord | undefined>;
45
+ set(key: string, record: SettlementRecord): Promise<void>;
46
+ }
47
+
48
+ interface RegisterProviderOptions {
49
+ supabase: SupabaseClient;
50
+ manifest: RouteDockManifest & {
51
+ signature?: string;
52
+ signature_version?: string;
53
+ };
54
+ baseUrl: string;
55
+ /**
56
+ * Whether to flag the provider record as verified in Supabase.
57
+ * Defaults to true (since signature is verified before writing).
58
+ */
59
+ verified?: boolean;
60
+ }
61
+ /**
62
+ * Register a provider in the Supabase `providers` table.
63
+ *
64
+ * Verifies the manifest's Ed25519 signature before upserting into the
65
+ * database keyed on `base_url`.
66
+ *
67
+ * Throws `RouteDockSignatureError` if the manifest is unsigned or invalid,
68
+ * and `Error` if the database upsert fails.
69
+ */
70
+ declare function registerProvider(optionsOrSupabase: RegisterProviderOptions | SupabaseClient, manifestArg?: RouteDockManifest & {
71
+ signature?: string;
72
+ }, baseUrlArg?: string, verifiedArg?: boolean): Promise<void>;
73
+
74
+ /**
75
+ * Session Reconciler — Recovery mechanism for orphaned MPP sessions
76
+ *
77
+ * Scans Supabase for sessions marked as "closing" (client initiated close but
78
+ * provider crashed before broadcasting settlement). Recovers abandoned funds by
79
+ * broadcasting the channel close transaction with the latest signed commitment.
80
+ *
81
+ * Idempotent: Safe to run multiple times on the same session (settlement_tx_hash
82
+ * acts as deduplication key).
83
+ */
84
+
85
+ type Network = 'testnet' | 'mainnet';
86
+ type ChannelCloseFn = (args: {
87
+ channel: string;
88
+ amount: bigint;
89
+ signature: Buffer;
90
+ feePayer: {
91
+ envelopeSigner: Keypair;
92
+ };
93
+ network: 'stellar:testnet' | 'stellar:pubnet';
94
+ }) => Promise<string>;
95
+ interface SessionReconcilerOptions {
96
+ supabase: SupabaseClient;
97
+ network: Network;
98
+ payeeSecretKey: string;
99
+ onRecovered?: (channelId: string, txHash: string, totalPaid: string) => Promise<void>;
100
+ /** Injectable for testing. Defaults to @stellar/mpp/channel/server close() */
101
+ channelClose?: ChannelCloseFn;
102
+ }
103
+ interface ReconciliationStats {
104
+ orphanedCount: number;
105
+ recoveredCount: number;
106
+ skippedCount: number;
107
+ failedCount: number;
108
+ errors: Array<{
109
+ channelId: string;
110
+ reason: string;
111
+ }>;
112
+ }
113
+ declare function decimalToStroops(amountStr: string): bigint;
114
+ /**
115
+ * Scan for abandoned sessions and attempt recovery.
116
+ * Idempotent: sessions with settlement_tx_hash are skipped (already recovered).
117
+ */
118
+ declare function reconcileAbandonedSessions(opts: SessionReconcilerOptions): Promise<ReconciliationStats>;
119
+ /**
120
+ * Run reconciliation on provider startup.
121
+ * Logs results and recovers orphaned sessions before accepting new connections.
122
+ */
123
+ declare function runStartupReconciliation(opts: SessionReconcilerOptions, logger?: (msg: string) => void): Promise<void>;
124
+
125
+ export { type ReconciliationStats as R, type SessionReconcilerOptions as S, type RegisterProviderOptions as a, SupabaseSeenTxStore as b, registerProvider as c, decimalToStroops as d, runStartupReconciliation as e, reconcileAbandonedSessions as r };