@routedock/routedock 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -12
- package/dist/{MppSessionHandler-1EL8QBAn.d.cts → MppSessionHandler-3QfCHI5x.d.ts} +40 -2
- package/dist/{MppSessionHandler-Bq4S-JTE.d.ts → MppSessionHandler-xu4Kxu6N.d.cts} +40 -2
- package/dist/{ProviderRegistry-w4_hkI_1.d.ts → ProviderRegistry-D9KqyuH-.d.cts} +16 -36
- package/dist/{ProviderRegistry-CyZ5eryO.d.cts → ProviderRegistry-DVi2uzgk.d.ts} +16 -36
- package/dist/{RouteDockClient-CjM2VyOd.d.ts → RouteDockClient-6rmgkbYb.d.cts} +124 -144
- package/dist/{RouteDockClient-CXvWXix5.d.cts → RouteDockClient-CnAkxwrW.d.ts} +124 -144
- package/dist/{SessionReconciler-Bw-PoBVl.d.ts → SessionReconciler-Cce9MXO1.d.ts} +10 -3
- package/dist/{SessionReconciler-Cz2nuOlJ.d.cts → SessionReconciler-Dpe9qXdD.d.cts} +10 -3
- package/dist/{SessionStore-BaYiegP0.d.ts → SessionStore-DkLBxFQu.d.cts} +28 -3
- package/dist/{SessionStore-B4-Hi4H8.d.cts → SessionStore-_ZkoBzFY.d.ts} +28 -3
- package/dist/SpendStore-BUclV02i.d.ts +73 -0
- package/dist/SpendStore-s_dfcemj.d.cts +73 -0
- package/dist/client.cjs +767 -482
- package/dist/client.d.cts +80 -10
- package/dist/client.d.ts +80 -10
- package/dist/client.js +733 -463
- package/dist/index.cjs +1295 -585
- package/dist/index.d.cts +13 -10
- package/dist/index.d.ts +13 -10
- package/dist/index.js +1247 -554
- package/dist/logger-DVa7VT4c.d.cts +41 -0
- package/dist/logger-DVa7VT4c.d.ts +41 -0
- package/dist/network.cjs +57 -0
- package/dist/network.d.cts +72 -0
- package/dist/network.d.ts +72 -0
- package/dist/network.js +49 -0
- package/dist/node.cjs +166 -0
- package/dist/node.d.cts +77 -0
- package/dist/node.d.ts +77 -0
- package/dist/node.js +159 -0
- package/dist/provider/fastify.cjs +847 -62
- package/dist/provider/fastify.d.cts +10 -3
- package/dist/provider/fastify.d.ts +10 -3
- package/dist/provider/fastify.js +848 -63
- package/dist/provider/hono.cjs +936 -60
- package/dist/provider/hono.d.cts +23 -8
- package/dist/provider/hono.d.ts +23 -8
- package/dist/provider/hono.js +937 -61
- package/dist/provider.cjs +894 -70
- package/dist/provider.d.cts +9 -8
- package/dist/provider.d.ts +9 -8
- package/dist/provider.js +895 -72
- package/dist/react.cjs +736 -431
- package/dist/react.d.cts +7 -5
- package/dist/react.d.ts +7 -5
- package/dist/react.js +725 -420
- package/dist/{routedockMiddleware-57gUaEMf.d.ts → routedockMiddleware-Cq9HOJEa.d.ts} +9 -2
- package/dist/{routedockMiddleware-DlyLrxp7.d.cts → routedockMiddleware-CxsWkSKa.d.cts} +9 -2
- package/dist/schema.cjs +7 -0
- package/dist/schema.js +7 -0
- package/dist/{sign-AKH67sft.d.cts → sign-Bq08SeFq.d.ts} +2 -2
- package/dist/{sign-BEq-OsSq.d.ts → sign-DqGE0Plv.d.cts} +2 -2
- package/dist/testing.d.cts +4 -3
- package/dist/testing.d.ts +4 -3
- package/dist/{logger-D8_Q2ytp.d.cts → types-DXaS5Wip.d.cts} +85 -47
- package/dist/{logger-D8_Q2ytp.d.ts → types-DXaS5Wip.d.ts} +85 -47
- package/package.json +50 -4
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { Keypair } from '@stellar/stellar-sdk';
|
|
2
|
-
import {
|
|
2
|
+
import { P as PaymentMode, c as PaymentRequest, R as RouteDockManifest, d as PaymentResult, U as UrlPreflightResult, g as PreflightResult, b as EstimateCostResult, n as SessionOptions, m as SessionHandle } from './types-DXaS5Wip.js';
|
|
3
|
+
import { b as RouteDockLogger } from './logger-DVa7VT4c.js';
|
|
3
4
|
import { ClientStellarSigner } from '@x402/stellar';
|
|
4
|
-
import {
|
|
5
|
-
import { usdcToStroops } from '@routedock/nulth-sdk';
|
|
5
|
+
import { PaymentRequirements } from '@x402/core/types';
|
|
6
|
+
import { NulthSignerConfig, NulthStellarSigner, usdcToStroops } from '@routedock/nulth-sdk';
|
|
7
|
+
import { S as SpendStore } from './SpendStore-BUclV02i.js';
|
|
6
8
|
|
|
7
9
|
interface RetryPolicy {
|
|
8
10
|
/** Total attempts including the first call. Default: 4 (3 retries). */
|
|
9
11
|
maxAttempts?: number;
|
|
10
12
|
/** Base delay for exponential backoff in ms. Default: 250. */
|
|
11
13
|
baseDelayMs?: number;
|
|
12
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Maximum delay of any single retry sleep, in ms. Default: 30000. Caps both
|
|
16
|
+
* the jittered backoff and a server `Retry-After` hint, so a provider that
|
|
17
|
+
* asks for a longer wait is waited on for at most this long. The error's
|
|
18
|
+
* own `retryAfterMs` still carries the raw server value.
|
|
19
|
+
*/
|
|
13
20
|
maxDelayMs?: number;
|
|
14
21
|
/** Called before each retry with the attempt number, error, and upcoming delay. */
|
|
15
22
|
/**
|
|
@@ -32,27 +39,25 @@ declare const DEFAULT_RETRY_POLICY: Required<Omit<RetryPolicy, 'onRetry' | 'logg
|
|
|
32
39
|
declare function backoffDelayMs(attempt: number, baseDelayMs: number, maxDelayMs?: number): number;
|
|
33
40
|
/**
|
|
34
41
|
* Execute `fn` with exponential backoff on retryable RouteDockErrors.
|
|
35
|
-
* Honors `retryAfterMs` from facilitator 429/503 responses when present
|
|
42
|
+
* Honors `retryAfterMs` from facilitator 429/503 responses when present, but
|
|
43
|
+
* never sleeps longer than `maxDelayMs`: a larger server hint is clamped.
|
|
36
44
|
*/
|
|
37
45
|
declare function withRetry<T>(fn: () => Promise<T>, policy?: RetryPolicy): Promise<T>;
|
|
38
46
|
|
|
39
47
|
/**
|
|
40
48
|
* Enforce the per-endpoint `deprecated` and `sunset_at` metadata for the URL
|
|
41
|
-
* being paid.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* `
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* - All keys in `latency_hints` must be a subset of declared `regions`.
|
|
52
|
-
* - If `assets` is defined, it must be a non-empty array whose first entry
|
|
53
|
-
* (`assets[0]`) matches the root-level `asset` and `asset_contract`.
|
|
49
|
+
* being paid.
|
|
50
|
+
*
|
|
51
|
+
* When the caller's HTTP method is known (`method`), only descriptors for that
|
|
52
|
+
* method can speak for the request, so a sunset GET `/infer` no longer blocks
|
|
53
|
+
* an active POST `/infer` (and vice versa). Without a method — `estimateCost()`
|
|
54
|
+
* before a request is chosen, or `openSession()` — a shared path is refused
|
|
55
|
+
* only when every matching descriptor has sunset. Paths the manifest does not
|
|
56
|
+
* list, and (method-known) method/path pairs it does not declare, keep today's
|
|
57
|
+
* behavior. Query strings and hashes are ignored because only `URL.pathname`
|
|
58
|
+
* is compared.
|
|
54
59
|
*/
|
|
55
|
-
declare function
|
|
60
|
+
declare function assertEndpointActive(manifest: RouteDockManifest, url: string, logger?: RouteDockLogger, method?: string): void;
|
|
56
61
|
/**
|
|
57
62
|
* Override the manifest cache's max size (default 512). Affects the shared,
|
|
58
63
|
* process-wide cache used by all RouteDockClient instances.
|
|
@@ -94,6 +99,24 @@ interface ModeSelectOptions {
|
|
|
94
99
|
forceMode?: PaymentMode;
|
|
95
100
|
/** Structured logger for mode selection events. Defaults to no-op. */
|
|
96
101
|
logger?: RouteDockLogger;
|
|
102
|
+
/**
|
|
103
|
+
* HTTP request replayed to the provider. Use it for POST endpoints:
|
|
104
|
+
*
|
|
105
|
+
* ```ts
|
|
106
|
+
* await client.pay(url, {
|
|
107
|
+
* request: {
|
|
108
|
+
* method: 'POST',
|
|
109
|
+
* headers: { 'content-type': 'application/json' },
|
|
110
|
+
* body: JSON.stringify(input),
|
|
111
|
+
* },
|
|
112
|
+
* })
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* The method/headers/body are sent on the unpaid probe, the paid request and
|
|
116
|
+
* every retry; SDK payment headers always override caller headers. The method
|
|
117
|
+
* also scopes the per-endpoint sunset check.
|
|
118
|
+
*/
|
|
119
|
+
request?: PaymentRequest;
|
|
97
120
|
}
|
|
98
121
|
/**
|
|
99
122
|
* Fetch, validate, and cache a RouteDock manifest from `baseUrl`.
|
|
@@ -104,6 +127,12 @@ interface ModeSelectOptions {
|
|
|
104
127
|
* Callers that obtained a payee from a registry/pinned allowlist MUST pass it;
|
|
105
128
|
* without it the self-signed signature alone proves nothing about who served
|
|
106
129
|
* the manifest.
|
|
130
|
+
*
|
|
131
|
+
* Concurrent calls that miss the cache for the same `baseUrl` share one
|
|
132
|
+
* in-flight HTTP request instead of fanning out (the cache is process-wide,
|
|
133
|
+
* so this also holds across `RouteDockClient` instances). The first caller's
|
|
134
|
+
* `retryPolicy` and `manifestTimeoutMs` govern the shared request; the
|
|
135
|
+
* per-caller checks above still run for every caller after it settles.
|
|
107
136
|
*/
|
|
108
137
|
declare function fetchManifest(baseUrl: string, retryPolicy?: RetryPolicy, manifestTimeoutMs?: number, expectedPayee?: string): Promise<RouteDockManifest>;
|
|
109
138
|
/**
|
|
@@ -123,81 +152,45 @@ declare function selectMode(manifest: RouteDockManifest, options?: ModeSelectOpt
|
|
|
123
152
|
*/
|
|
124
153
|
declare function rankProvidersByLatency(manifests: RouteDockManifest[], targetRegion: string): RouteDockManifest[];
|
|
125
154
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
interface PaymentAuthContext {
|
|
136
|
-
authEntry: string;
|
|
137
|
-
payee: string;
|
|
138
|
-
amountStroops: bigint;
|
|
139
|
-
assetContract: string;
|
|
140
|
-
ledgerSequence: number;
|
|
155
|
+
type Network = 'testnet' | 'mainnet';
|
|
156
|
+
/** What an {@link X402PreSignHook} sees: the payment about to be signed. */
|
|
157
|
+
interface X402PreSignContext {
|
|
158
|
+
/** The resource being paid for. */
|
|
159
|
+
readonly url: string;
|
|
160
|
+
/** The signed manifest the challenge was already bound to. */
|
|
161
|
+
readonly manifest: RouteDockManifest;
|
|
162
|
+
/** The challenge entries that passed manifest binding — the signer picks from these. */
|
|
163
|
+
readonly accepts: readonly PaymentRequirements[];
|
|
141
164
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
declare class NulthPolicyError extends Error {
|
|
168
|
-
readonly code: 'payee_not_allowed' | 'daily_cap_exceeded' | 'session_expired' | 'auth_entry_mismatch';
|
|
169
|
-
constructor(code: NulthPolicyError['code'], message: string);
|
|
170
|
-
}
|
|
171
|
-
interface NulthStellarSigner {
|
|
172
|
-
address: string;
|
|
173
|
-
signAuthEntry: SignAuthEntry;
|
|
174
|
-
}
|
|
175
|
-
interface NulthSignerConfig extends NulthClientConfig {
|
|
176
|
-
paymentContext?: Omit<PaymentAuthContext, 'authEntry'>;
|
|
165
|
+
/** Verdict returned by an {@link X402PreSignHook}. */
|
|
166
|
+
type X402PreSignDecision = {
|
|
167
|
+
approve: true;
|
|
168
|
+
} | {
|
|
169
|
+
approve: false;
|
|
170
|
+
reason?: string;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Optional guard run once per `pay()`, after the 402 challenge has been bound to
|
|
174
|
+
* the manifest and immediately before anything is signed. Use it to plug in an
|
|
175
|
+
* external or custom audit. Rejecting — or throwing — aborts before signing
|
|
176
|
+
* (fail closed) with `RouteDockPolicyRejectError`.
|
|
177
|
+
*/
|
|
178
|
+
type X402PreSignHook = (ctx: X402PreSignContext) => X402PreSignDecision | Promise<X402PreSignDecision>;
|
|
179
|
+
declare class X402Client {
|
|
180
|
+
private readonly network;
|
|
181
|
+
private readonly retryPolicy?;
|
|
182
|
+
private readonly preSignHook?;
|
|
183
|
+
private readonly httpClient;
|
|
184
|
+
private readonly signer;
|
|
185
|
+
private readonly logger;
|
|
186
|
+
constructor(secretKeyOrSigner: string | ClientStellarSigner, network: Network, retryPolicy?: RetryPolicy | undefined, preSignHook?: X402PreSignHook | undefined, logger?: RouteDockLogger);
|
|
187
|
+
/** Replace signer (e.g. swap to Nulth ZK account payer before pay) */
|
|
188
|
+
withSigner(signer: ClientStellarSigner): X402Client;
|
|
189
|
+
pay(url: string, manifest: RouteDockManifest, request?: PaymentRequest): Promise<PaymentResult>;
|
|
177
190
|
}
|
|
191
|
+
|
|
192
|
+
/** Preserve RouteDock's logger default and configuration error contract. */
|
|
178
193
|
declare function createNulthSigner(config: NulthSignerConfig): NulthStellarSigner;
|
|
179
|
-
declare function setNulthPaymentContext(config: NulthSignerConfig, context: Omit<PaymentAuthContext, 'authEntry'>): void;
|
|
180
|
-
declare function paymentContextFromManifest(manifest: {
|
|
181
|
-
payee: string;
|
|
182
|
-
asset_contract: string;
|
|
183
|
-
pricing: {
|
|
184
|
-
x402?: {
|
|
185
|
-
amount: string;
|
|
186
|
-
payee?: string;
|
|
187
|
-
};
|
|
188
|
-
'mpp-charge'?: {
|
|
189
|
-
amount: string;
|
|
190
|
-
payee?: string;
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
}, mode: 'x402' | 'mpp-charge', ledgerSequence: number): Omit<PaymentAuthContext, 'authEntry'>;
|
|
194
|
-
declare function createPolicyState(input: {
|
|
195
|
-
dailyCapUsdc: string;
|
|
196
|
-
allowedPayees: readonly string[];
|
|
197
|
-
witnessSecret: string;
|
|
198
|
-
expiryLedger?: number;
|
|
199
|
-
ledgerSequence?: number;
|
|
200
|
-
}): NulthPolicyState;
|
|
201
194
|
interface NulthVaultConfig {
|
|
202
195
|
mode: 'nulth';
|
|
203
196
|
nulthAccount: string;
|
|
@@ -215,56 +208,21 @@ declare function prepareNulthSigner(vault: NulthVaultConfig, manifest: RouteDock
|
|
|
215
208
|
config: NulthSignerConfig;
|
|
216
209
|
}>;
|
|
217
210
|
|
|
218
|
-
declare function decodeAuthSignature(encoded: string): NulthAuthSignature;
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Daily spend accumulator, keyed by an ISO date (YYYY-MM-DD).
|
|
222
|
-
*
|
|
223
|
-
* Totals are decimal strings of integer microUSDC (1 USDC = 10^7 units) so
|
|
224
|
-
* the record stays JSON-serializable with no floating point precision loss.
|
|
225
|
-
*/
|
|
226
|
-
interface DailySpend {
|
|
227
|
-
date: string;
|
|
228
|
-
/** Global accumulated spend for the day, in microUSDC (e.g. "5000000") */
|
|
229
|
-
totalMicros: string;
|
|
230
|
-
/** Per-endpoint accumulated spend for the day, keyed by origin URL, in microUSDC */
|
|
231
|
-
endpoints: Record<string, string>;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Durable backing store for the client's local daily spend cap.
|
|
235
|
-
*
|
|
236
|
-
* The spend cap is a financial safety control: if its accumulator lives only in
|
|
237
|
-
* heap memory, any crash, OOM kill, or container restart silently resets it to
|
|
238
|
-
* zero and the cap can be bypassed. Inject a persistent implementation
|
|
239
|
-
* (Redis, SQL, Supabase, a file, …) so the counter survives restarts.
|
|
240
|
-
*/
|
|
241
|
-
interface SpendStore {
|
|
242
|
-
/** Return the persisted accumulator, or null if none has been written yet. */
|
|
243
|
-
read(): Promise<DailySpend | null>;
|
|
244
|
-
/** Persist the accumulator. */
|
|
245
|
-
write(state: DailySpend): Promise<void>;
|
|
246
|
-
}
|
|
247
|
-
interface InMemorySpendStoreOptions {
|
|
248
|
-
/** Log a startup warning about non-durability. Defaults to true. */
|
|
249
|
-
warn?: boolean;
|
|
250
|
-
/** Log sink for the non-durability warning. Defaults to a console-backed logger. */
|
|
251
|
-
logger?: RouteDockLogger;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Default, non-durable {@link SpendStore}. Holds the accumulator on the heap, so
|
|
255
|
-
* it resets on every process restart. Logs a startup warning unless silenced.
|
|
256
|
-
* Use this only for development; inject a persistent store in production.
|
|
257
|
-
*/
|
|
258
|
-
declare class InMemorySpendStore implements SpendStore {
|
|
259
|
-
private state;
|
|
260
|
-
constructor(options?: InMemorySpendStoreOptions);
|
|
261
|
-
read(): Promise<DailySpend | null>;
|
|
262
|
-
write(state: DailySpend): Promise<void>;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
211
|
interface SpendCap {
|
|
266
|
-
/**
|
|
212
|
+
/**
|
|
213
|
+
* Maximum total spend per day (decimal string, e.g. "1.00"), denominated in
|
|
214
|
+
* `asset`.
|
|
215
|
+
*
|
|
216
|
+
* The accumulator is a single USDC number, so the cap only ever counts USDC.
|
|
217
|
+
* When a cap is configured, a payment whose selected asset (see
|
|
218
|
+
* `selectAsset`) is anything other than `asset` is rejected before any
|
|
219
|
+
* trustline check, network call or on-chain deposit with
|
|
220
|
+
* `RouteDockPolicyRejectError('local_cap_asset_mismatch')`. Amounts in other
|
|
221
|
+
* assets are never added to the USDC total, since 5 XLM is not 5 USDC.
|
|
222
|
+
* Manifests that only price in USDC behave exactly as before.
|
|
223
|
+
*/
|
|
267
224
|
daily: string;
|
|
225
|
+
/** The only asset this cap can account for. */
|
|
268
226
|
asset: 'USDC';
|
|
269
227
|
/**
|
|
270
228
|
* Optional per-endpoint daily spend caps, keyed by endpoint origin URL
|
|
@@ -300,6 +258,11 @@ interface RouteDockClientConfig {
|
|
|
300
258
|
commitmentSecret?: string | undefined;
|
|
301
259
|
/** Retry policy for transient failures (network, facilitator 5xx). */
|
|
302
260
|
retryPolicy?: RetryPolicy;
|
|
261
|
+
/**
|
|
262
|
+
* Optional guard run before an x402 payment is signed. Rejecting or throwing
|
|
263
|
+
* aborts the payment with `RouteDockPolicyRejectError`. x402 mode only.
|
|
264
|
+
*/
|
|
265
|
+
preSignHook?: X402PreSignHook;
|
|
303
266
|
/**
|
|
304
267
|
* Durable backing store for the daily spend cap accumulator. Defaults to a
|
|
305
268
|
* non-durable in-memory store that resets on restart (with a startup warning).
|
|
@@ -376,12 +339,24 @@ declare class RouteDockClient {
|
|
|
376
339
|
*/
|
|
377
340
|
private _withSpendMutex;
|
|
378
341
|
/**
|
|
379
|
-
* Check
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
342
|
+
* Check a provider before committing to a payment. Neither form submits a
|
|
343
|
+
* transaction or moves money.
|
|
344
|
+
*
|
|
345
|
+
* - `preflight(url)` fetches and validates the provider's manifest and
|
|
346
|
+
* returns the supported modes, the estimated cost per mode, and
|
|
347
|
+
* deprecation/sunset flags. It does not check the payer's trustline.
|
|
348
|
+
* Throws the same manifest errors as `pay()` / `estimateCost()` when the
|
|
349
|
+
* manifest is unreachable, invalid, unsupported by this SDK version, or
|
|
350
|
+
* sunset (including a sunset endpoint matching the URL path).
|
|
351
|
+
* - `preflight(manifest, mode?, endpoint?)` checks that the payer has a
|
|
352
|
+
* trustline for the payment asset and surfaces manifest metadata
|
|
353
|
+
* (regions, latency hints, capabilities). For approval gates and manual
|
|
354
|
+
* trustline remediation.
|
|
383
355
|
*/
|
|
356
|
+
preflight(url: string): Promise<UrlPreflightResult>;
|
|
384
357
|
preflight(manifest: RouteDockManifest, mode?: PaymentMode, endpoint?: string): Promise<PreflightResult>;
|
|
358
|
+
/** Manifest-only preflight for a URL — no trustline check, no payment. */
|
|
359
|
+
private _preflightUrl;
|
|
385
360
|
/**
|
|
386
361
|
* Verify via Horizon that the payer account has a trustline for the
|
|
387
362
|
* payment asset declared in the manifest. Throws RouteDockTrustlineError
|
|
@@ -417,6 +392,11 @@ declare class RouteDockClient {
|
|
|
417
392
|
* disable via `options.maxDurationMs`.
|
|
418
393
|
*/
|
|
419
394
|
openSession(url: string, options?: SessionOptions): Promise<SessionHandle>;
|
|
395
|
+
/**
|
|
396
|
+
* The local cap accumulates one USDC total. Refuse to count any other asset
|
|
397
|
+
* against it (or let it through uncounted) when a cap is configured.
|
|
398
|
+
*/
|
|
399
|
+
private _assertCapAsset;
|
|
420
400
|
/**
|
|
421
401
|
* Record spend for an MPP session voucher. Called before each voucher
|
|
422
402
|
* fetch in stream(). Enforces the same local daily and per-endpoint caps
|
|
@@ -468,4 +448,4 @@ declare class RouteDockClient {
|
|
|
468
448
|
private _rollbackSpend;
|
|
469
449
|
}
|
|
470
450
|
|
|
471
|
-
export {
|
|
451
|
+
export { DEFAULT_RETRY_POLICY as D, type ModeSelectOptions as M, type NulthVaultConfig as N, type RetryPolicy as R, type SpendCap as S, type VaultConfig as V, X402Client as X, RouteDockClient as a, type RouteDockClientConfig as b, type X402PreSignContext as c, type X402PreSignDecision as d, type X402PreSignHook as e, assertEndpointActive as f, assertNulthVaultManifest as g, backoffDelayMs as h, configureManifestCache as i, createNulthSigner as j, fetchLedgerSequence as k, fetchManifest as l, invalidateManifest as m, prepareNulthSigner as p, rankProvidersByLatency as r, selectMode as s, usdcToMicros as u, withRetry as w };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import { S as SeenTxStore, a as SettlementRecord } from './MppSessionHandler-
|
|
3
|
-
import {
|
|
2
|
+
import { S as SeenTxStore, a as SettlementRecord } from './MppSessionHandler-3QfCHI5x.js';
|
|
3
|
+
import { R as RouteDockManifest } from './types-DXaS5Wip.js';
|
|
4
4
|
import { Keypair } from '@stellar/stellar-sdk';
|
|
5
|
+
import { b as RouteDockLogger } from './logger-DVa7VT4c.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Settlement idempotency backed by Postgres via Supabase.
|
|
@@ -95,7 +96,7 @@ type Network = 'testnet' | 'mainnet';
|
|
|
95
96
|
type ChannelCloseFn = (args: {
|
|
96
97
|
channel: string;
|
|
97
98
|
amount: bigint;
|
|
98
|
-
signature:
|
|
99
|
+
signature: Uint8Array;
|
|
99
100
|
feePayer: {
|
|
100
101
|
envelopeSigner: Keypair;
|
|
101
102
|
};
|
|
@@ -108,12 +109,18 @@ interface SessionReconcilerOptions {
|
|
|
108
109
|
onRecovered?: (channelId: string, txHash: string, totalPaid: string) => Promise<void>;
|
|
109
110
|
/** Injectable for testing. Defaults to @stellar/mpp/channel/server close() */
|
|
110
111
|
channelClose?: ChannelCloseFn;
|
|
112
|
+
/** Failed close attempts before a row is moved to the terminal `failed` status. Default 5. */
|
|
113
|
+
maxReconcileAttempts?: number;
|
|
111
114
|
}
|
|
112
115
|
interface ReconciliationStats {
|
|
113
116
|
orphanedCount: number;
|
|
114
117
|
recoveredCount: number;
|
|
115
118
|
skippedCount: number;
|
|
116
119
|
failedCount: number;
|
|
120
|
+
/** Rows whose channel was already closed on-chain; marked `closed` without a new close. */
|
|
121
|
+
alreadyClosedCount: number;
|
|
122
|
+
/** Rows moved to the terminal `failed` status this run (attempt limit reached). */
|
|
123
|
+
terminalCount: number;
|
|
117
124
|
errors: Array<{
|
|
118
125
|
channelId: string;
|
|
119
126
|
reason: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import { S as SeenTxStore, a as SettlementRecord } from './MppSessionHandler-
|
|
3
|
-
import {
|
|
2
|
+
import { S as SeenTxStore, a as SettlementRecord } from './MppSessionHandler-xu4Kxu6N.cjs';
|
|
3
|
+
import { R as RouteDockManifest } from './types-DXaS5Wip.cjs';
|
|
4
4
|
import { Keypair } from '@stellar/stellar-sdk';
|
|
5
|
+
import { b as RouteDockLogger } from './logger-DVa7VT4c.cjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Settlement idempotency backed by Postgres via Supabase.
|
|
@@ -95,7 +96,7 @@ type Network = 'testnet' | 'mainnet';
|
|
|
95
96
|
type ChannelCloseFn = (args: {
|
|
96
97
|
channel: string;
|
|
97
98
|
amount: bigint;
|
|
98
|
-
signature:
|
|
99
|
+
signature: Uint8Array;
|
|
99
100
|
feePayer: {
|
|
100
101
|
envelopeSigner: Keypair;
|
|
101
102
|
};
|
|
@@ -108,12 +109,18 @@ interface SessionReconcilerOptions {
|
|
|
108
109
|
onRecovered?: (channelId: string, txHash: string, totalPaid: string) => Promise<void>;
|
|
109
110
|
/** Injectable for testing. Defaults to @stellar/mpp/channel/server close() */
|
|
110
111
|
channelClose?: ChannelCloseFn;
|
|
112
|
+
/** Failed close attempts before a row is moved to the terminal `failed` status. Default 5. */
|
|
113
|
+
maxReconcileAttempts?: number;
|
|
111
114
|
}
|
|
112
115
|
interface ReconciliationStats {
|
|
113
116
|
orphanedCount: number;
|
|
114
117
|
recoveredCount: number;
|
|
115
118
|
skippedCount: number;
|
|
116
119
|
failedCount: number;
|
|
120
|
+
/** Rows whose channel was already closed on-chain; marked `closed` without a new close. */
|
|
121
|
+
alreadyClosedCount: number;
|
|
122
|
+
/** Rows moved to the terminal `failed` status this run (attempt limit reached). */
|
|
123
|
+
terminalCount: number;
|
|
117
124
|
errors: Array<{
|
|
118
125
|
channelId: string;
|
|
119
126
|
reason: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as RouteDockManifest, P as PaymentMode, A as AssetConfig, p as SessionState } from './types-DXaS5Wip.cjs';
|
|
2
2
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
3
3
|
|
|
4
4
|
/** Stable error codes for programmatic handling. */
|
|
5
|
-
type RouteDockErrorCode = 'MANIFEST' | 'MANIFEST_TIMEOUT' | 'MANIFEST_SUNSET' | 'NO_SUPPORTED_MODE' | 'FACILITATOR' | 'NETWORK' | 'SIGNATURE' | 'VOUCHER_MONOTONICITY' | 'POLICY_REJECT' | 'CHANNEL_STATE' | 'DISPUTE' | 'REFUND_WINDOW' | 'CLIENT_VERSION_TOO_OLD' | 'TRUSTLINE';
|
|
5
|
+
type RouteDockErrorCode = 'MANIFEST' | 'MANIFEST_TIMEOUT' | 'MANIFEST_SUNSET' | 'NO_SUPPORTED_MODE' | 'FACILITATOR' | 'NETWORK' | 'SIGNATURE' | 'VOUCHER_MONOTONICITY' | 'POLICY_REJECT' | 'PAYMENT_REJECTED' | 'CHANNEL_STATE' | 'DISPUTE' | 'REFUND_WINDOW' | 'CLIENT_VERSION_TOO_OLD' | 'TRUSTLINE';
|
|
6
6
|
interface RouteDockErrorOptions {
|
|
7
7
|
cause?: unknown;
|
|
8
8
|
/** Milliseconds to wait before retry (e.g. from Retry-After header). */
|
|
@@ -68,6 +68,24 @@ declare class RouteDockPolicyRejectError extends RouteDockError {
|
|
|
68
68
|
readonly reason: string;
|
|
69
69
|
constructor(reason: string, options?: RouteDockErrorOptions);
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The provider refused a signed, paid request (HTTP 4xx such as 401/402).
|
|
73
|
+
* Not a manifest problem and not a local policy rejection: `status` is the HTTP
|
|
74
|
+
* status and `reason` is the provider's own explanation when it sent one
|
|
75
|
+
* (e.g. the facilitator's `invalidReason`). Not retryable — the same signed
|
|
76
|
+
* payment would be refused again.
|
|
77
|
+
*/
|
|
78
|
+
declare class RouteDockPaymentRejectedError extends RouteDockError {
|
|
79
|
+
readonly status: number;
|
|
80
|
+
/** The provider's `reason` field, if its response body was JSON and had one. */
|
|
81
|
+
readonly reason?: string;
|
|
82
|
+
/** The provider's `error` field (short summary), if present. */
|
|
83
|
+
readonly providerError?: string;
|
|
84
|
+
constructor(message: string, status: number, details?: {
|
|
85
|
+
reason?: string;
|
|
86
|
+
providerError?: string;
|
|
87
|
+
}, options?: RouteDockErrorOptions);
|
|
88
|
+
}
|
|
71
89
|
/** Channel lifecycle failure (open, simulate, close, missing settlement fields). */
|
|
72
90
|
declare class RouteDockChannelStateError extends RouteDockError {
|
|
73
91
|
constructor(message: string, options?: RouteDockErrorOptions);
|
|
@@ -88,6 +106,13 @@ declare const RouteDockSessionError: typeof RouteDockVoucherMonotonicityError;
|
|
|
88
106
|
declare function parseRetryAfterMs(response: Response): number | undefined;
|
|
89
107
|
/** Map an HTTP status to a typed, retryable-aware RouteDock error. */
|
|
90
108
|
declare function httpStatusToError(message: string, status: number, response?: Response): RouteDockError;
|
|
109
|
+
/**
|
|
110
|
+
* Build a {@link RouteDockPaymentRejectedError} from a rejected paid response.
|
|
111
|
+
* Reads the JSON body best-effort (a missing, non-JSON or oversized body just
|
|
112
|
+
* means no `reason`; the provider is untrusted, so the read is bounded and the
|
|
113
|
+
* fields are length-capped), and never throws.
|
|
114
|
+
*/
|
|
115
|
+
declare function paymentRejectedError(label: string, status: number, response: Response): Promise<RouteDockPaymentRejectedError>;
|
|
91
116
|
/** Wrap a low-level fetch failure; preserves existing RouteDockErrors. */
|
|
92
117
|
declare function wrapFetchError(err: unknown, context: string): RouteDockError;
|
|
93
118
|
/**
|
|
@@ -140,4 +165,4 @@ declare class SupabaseSessionStore implements SessionStore {
|
|
|
140
165
|
close(channelId: string): Promise<void>;
|
|
141
166
|
}
|
|
142
167
|
|
|
143
|
-
export {
|
|
168
|
+
export { resolveAssetContract as A, selectAsset as B, wrapFetchError as C, wrapMppError as D, RouteDockChannelStateError as R, type SessionStore as S, RouteDockClientVersionError as a, RouteDockDisputeError as b, RouteDockError as c, type RouteDockErrorCode as d, type RouteDockErrorOptions as e, RouteDockFacilitatorError as f, RouteDockManifestError as g, RouteDockManifestSunsetError as h, RouteDockManifestTimeoutError as i, RouteDockNetworkError as j, RouteDockNoSupportedModeError as k, RouteDockPaymentRejectedError as l, RouteDockPolicyRejectError as m, RouteDockPolicyRejectedError as n, RouteDockRefundWindowError as o, RouteDockSessionError as p, RouteDockSignatureError as q, RouteDockTrustlineError as r, RouteDockVoucherMonotonicityError as s, SupabaseSessionStore as t, getEligibleAssets as u, httpStatusToError as v, isAssetEligible as w, normalizeManifestAssets as x, parseRetryAfterMs as y, paymentRejectedError as z };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as RouteDockManifest, P as PaymentMode, A as AssetConfig, p as SessionState } from './types-DXaS5Wip.js';
|
|
2
2
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
3
3
|
|
|
4
4
|
/** Stable error codes for programmatic handling. */
|
|
5
|
-
type RouteDockErrorCode = 'MANIFEST' | 'MANIFEST_TIMEOUT' | 'MANIFEST_SUNSET' | 'NO_SUPPORTED_MODE' | 'FACILITATOR' | 'NETWORK' | 'SIGNATURE' | 'VOUCHER_MONOTONICITY' | 'POLICY_REJECT' | 'CHANNEL_STATE' | 'DISPUTE' | 'REFUND_WINDOW' | 'CLIENT_VERSION_TOO_OLD' | 'TRUSTLINE';
|
|
5
|
+
type RouteDockErrorCode = 'MANIFEST' | 'MANIFEST_TIMEOUT' | 'MANIFEST_SUNSET' | 'NO_SUPPORTED_MODE' | 'FACILITATOR' | 'NETWORK' | 'SIGNATURE' | 'VOUCHER_MONOTONICITY' | 'POLICY_REJECT' | 'PAYMENT_REJECTED' | 'CHANNEL_STATE' | 'DISPUTE' | 'REFUND_WINDOW' | 'CLIENT_VERSION_TOO_OLD' | 'TRUSTLINE';
|
|
6
6
|
interface RouteDockErrorOptions {
|
|
7
7
|
cause?: unknown;
|
|
8
8
|
/** Milliseconds to wait before retry (e.g. from Retry-After header). */
|
|
@@ -68,6 +68,24 @@ declare class RouteDockPolicyRejectError extends RouteDockError {
|
|
|
68
68
|
readonly reason: string;
|
|
69
69
|
constructor(reason: string, options?: RouteDockErrorOptions);
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The provider refused a signed, paid request (HTTP 4xx such as 401/402).
|
|
73
|
+
* Not a manifest problem and not a local policy rejection: `status` is the HTTP
|
|
74
|
+
* status and `reason` is the provider's own explanation when it sent one
|
|
75
|
+
* (e.g. the facilitator's `invalidReason`). Not retryable — the same signed
|
|
76
|
+
* payment would be refused again.
|
|
77
|
+
*/
|
|
78
|
+
declare class RouteDockPaymentRejectedError extends RouteDockError {
|
|
79
|
+
readonly status: number;
|
|
80
|
+
/** The provider's `reason` field, if its response body was JSON and had one. */
|
|
81
|
+
readonly reason?: string;
|
|
82
|
+
/** The provider's `error` field (short summary), if present. */
|
|
83
|
+
readonly providerError?: string;
|
|
84
|
+
constructor(message: string, status: number, details?: {
|
|
85
|
+
reason?: string;
|
|
86
|
+
providerError?: string;
|
|
87
|
+
}, options?: RouteDockErrorOptions);
|
|
88
|
+
}
|
|
71
89
|
/** Channel lifecycle failure (open, simulate, close, missing settlement fields). */
|
|
72
90
|
declare class RouteDockChannelStateError extends RouteDockError {
|
|
73
91
|
constructor(message: string, options?: RouteDockErrorOptions);
|
|
@@ -88,6 +106,13 @@ declare const RouteDockSessionError: typeof RouteDockVoucherMonotonicityError;
|
|
|
88
106
|
declare function parseRetryAfterMs(response: Response): number | undefined;
|
|
89
107
|
/** Map an HTTP status to a typed, retryable-aware RouteDock error. */
|
|
90
108
|
declare function httpStatusToError(message: string, status: number, response?: Response): RouteDockError;
|
|
109
|
+
/**
|
|
110
|
+
* Build a {@link RouteDockPaymentRejectedError} from a rejected paid response.
|
|
111
|
+
* Reads the JSON body best-effort (a missing, non-JSON or oversized body just
|
|
112
|
+
* means no `reason`; the provider is untrusted, so the read is bounded and the
|
|
113
|
+
* fields are length-capped), and never throws.
|
|
114
|
+
*/
|
|
115
|
+
declare function paymentRejectedError(label: string, status: number, response: Response): Promise<RouteDockPaymentRejectedError>;
|
|
91
116
|
/** Wrap a low-level fetch failure; preserves existing RouteDockErrors. */
|
|
92
117
|
declare function wrapFetchError(err: unknown, context: string): RouteDockError;
|
|
93
118
|
/**
|
|
@@ -140,4 +165,4 @@ declare class SupabaseSessionStore implements SessionStore {
|
|
|
140
165
|
close(channelId: string): Promise<void>;
|
|
141
166
|
}
|
|
142
167
|
|
|
143
|
-
export {
|
|
168
|
+
export { resolveAssetContract as A, selectAsset as B, wrapFetchError as C, wrapMppError as D, RouteDockChannelStateError as R, type SessionStore as S, RouteDockClientVersionError as a, RouteDockDisputeError as b, RouteDockError as c, type RouteDockErrorCode as d, type RouteDockErrorOptions as e, RouteDockFacilitatorError as f, RouteDockManifestError as g, RouteDockManifestSunsetError as h, RouteDockManifestTimeoutError as i, RouteDockNetworkError as j, RouteDockNoSupportedModeError as k, RouteDockPaymentRejectedError as l, RouteDockPolicyRejectError as m, RouteDockPolicyRejectedError as n, RouteDockRefundWindowError as o, RouteDockSessionError as p, RouteDockSignatureError as q, RouteDockTrustlineError as r, RouteDockVoucherMonotonicityError as s, SupabaseSessionStore as t, getEligibleAssets as u, httpStatusToError as v, isAssetEligible as w, normalizeManifestAssets as x, parseRetryAfterMs as y, paymentRejectedError as z };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { b as RouteDockLogger } from './logger-DVa7VT4c.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Daily spend accumulator, keyed by an ISO date (YYYY-MM-DD).
|
|
5
|
+
*
|
|
6
|
+
* Totals are decimal strings of integer microUSDC (1 USDC = 10^7 units) so
|
|
7
|
+
* the record stays JSON-serializable with no floating point precision loss.
|
|
8
|
+
*/
|
|
9
|
+
interface DailySpend {
|
|
10
|
+
date: string;
|
|
11
|
+
/** Global accumulated spend for the day, in microUSDC (e.g. "5000000") */
|
|
12
|
+
totalMicros: string;
|
|
13
|
+
/** Per-endpoint accumulated spend for the day, keyed by origin URL, in microUSDC */
|
|
14
|
+
endpoints: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Durable backing store for the client's local daily spend cap.
|
|
18
|
+
*
|
|
19
|
+
* The spend cap is a financial safety control: if its accumulator lives only in
|
|
20
|
+
* heap memory, any crash, OOM kill, or container restart silently resets it to
|
|
21
|
+
* zero and the cap can be bypassed. Inject a persistent implementation
|
|
22
|
+
* (Redis, SQL, Supabase, a file, …) so the counter survives restarts.
|
|
23
|
+
*
|
|
24
|
+
* ### Sharing a store between processes
|
|
25
|
+
* The client serializes its own read → check → write with an in-memory mutex,
|
|
26
|
+
* but that only protects one `RouteDockClient` instance. If more than one
|
|
27
|
+
* process (or client) can use the same backing store — the MCP server and the
|
|
28
|
+
* E2E agent share `~/.routedock/spend.json` by default — a plain `read()` /
|
|
29
|
+
* `write()` pair is a lost-update race: two writers both read total X, both
|
|
30
|
+
* pass the cap check, and both write X + amount, so a payment vanishes from the
|
|
31
|
+
* accumulator and the cap can be exceeded. A store that is shared across
|
|
32
|
+
* processes MUST implement {@link SpendStore.update}, which the client uses
|
|
33
|
+
* instead of `read()` + `write()` whenever it is present.
|
|
34
|
+
*/
|
|
35
|
+
interface SpendStore {
|
|
36
|
+
/** Return the persisted accumulator, or null if none has been written yet. */
|
|
37
|
+
read(): Promise<DailySpend | null>;
|
|
38
|
+
/** Persist the accumulator. */
|
|
39
|
+
write(state: DailySpend): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Atomically read-modify-write the accumulator. Optional, but required for
|
|
42
|
+
* stores shared across processes (see above).
|
|
43
|
+
*
|
|
44
|
+
* Implementations must guarantee that no other writer can change the state
|
|
45
|
+
* between the read that produces `prev` and the write of `fn`'s result:
|
|
46
|
+
* `fn` receives the current state (or `null`), and returns the new state to
|
|
47
|
+
* persist, or `undefined` to leave the state unchanged (no write). If `fn`
|
|
48
|
+
* throws, nothing is written and the error propagates. `fn` is synchronous
|
|
49
|
+
* in spirit — keep it fast and free of side effects, because an
|
|
50
|
+
* implementation backed by optimistic concurrency (compare-and-swap) may
|
|
51
|
+
* call it more than once. Resolves to the state after the update.
|
|
52
|
+
*/
|
|
53
|
+
update?(fn: (prev: DailySpend | null) => DailySpend | undefined): Promise<DailySpend | null>;
|
|
54
|
+
}
|
|
55
|
+
interface InMemorySpendStoreOptions {
|
|
56
|
+
/** Log a startup warning about non-durability. Defaults to true. */
|
|
57
|
+
warn?: boolean;
|
|
58
|
+
/** Log sink for the non-durability warning. Defaults to a console-backed logger. */
|
|
59
|
+
logger?: RouteDockLogger;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Default, non-durable {@link SpendStore}. Holds the accumulator on the heap, so
|
|
63
|
+
* it resets on every process restart. Logs a startup warning unless silenced.
|
|
64
|
+
* Use this only for development; inject a persistent store in production.
|
|
65
|
+
*/
|
|
66
|
+
declare class InMemorySpendStore implements SpendStore {
|
|
67
|
+
private state;
|
|
68
|
+
constructor(options?: InMemorySpendStoreOptions);
|
|
69
|
+
read(): Promise<DailySpend | null>;
|
|
70
|
+
write(state: DailySpend): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { type DailySpend as D, InMemorySpendStore as I, type SpendStore as S, type InMemorySpendStoreOptions as a };
|